Python菜鸟编程第十五课之网络编程
1.引言
假如有两个脚本:foo.py,bar.py,分别运行时都可以正常运行。但是现在想从两个程序间传递一个数据。
同一台电脑时:创建一个文件,将foo.py的内容写入文件,bar.py读取文件内容
不同电脑:联想我们平时使用的微信QQ等。
2.软件架构
2.1C/S架构
C/S即Client和Server:客户端和服务器端架构。
2.2B/S架构
B/S即Browser和Server:浏览器端和服务器端架构
3端口
整个网络通信通过IP地址+端口号来识别不同的网络服务。
端口号是用来表示区别网络找那个不同的应用,操作系统会对端口号进行编号,即端口号。端口号使用16位,范围0-2^16-1,0-65535端口的分配是基于一定规则的,而不是随意分配。例如几个知名端口:80,分配给Http服务的;21分配给Ftp服务的。
动态端口:
一般不固定分配某种服务,动态分配。范围:1024-65535
所谓的动态分配,是指一个程序需要网络通信时,他向主机申请一个端口,主机可从可用的端口号中分配一个供其使用。关闭程序时,提示释放占用的端口。
端口查看:netstat -ano
用IP可以唯一标识网络中的主句,协议+端口号唯一标识主机中的应用进程。
4.Socket
创建一个socket
import socke
socket.socket(AddressFamilym,Type)
AddressFamilym:
AF_INET,internet间进程通信,实际工作中最常用的。
AF_UNIX,同一台机器间进程通信。
Type 套接字类型
SOCK_DGRAM,数据报套接字,主要用于UDP协议
SOCK_ATREAM,流式套接字,主要用于TCP协议
demo:
sever.py脚本代码:
# server
import socket
# 创建一个socket对象
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
# host = socket.gethostname()
# 设置端口号
port = 9955
# 绑定端口
serversocket.bind(('localhost', port))
# 设置最大连接数,超过后排队
serversocket.listen(5)
while True:
# 建立客户端连接
clientsocket, addr = serversocket.accept()
print('连接地址:', str(addr))
msg = '网络编程测试' + '\r\n'
clientsocket.send(msg.encode('utf-8'))
msg_back=clientsocket.recv(1024)
print(msg_back.decode('utf-8'))
clientsocket.close()
client.py脚本代码:
import socket
# 创建一个socket对象
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
host = socket.gethostname()
# 设置端口号
port = 9955
# 连接服务,指定主机和端口
ss.connect(('localhost', port))
msg = ss.recv(1024) # 指定要接收的最大数据量,这里是1024k
if msg:
msg_back='我收到了'
ss.send(msg_back.encode('utf-8'))
ss.close()
print(msg.decode('utf-8'))
先运行server.py,再运行client.py
运行结果:
#server.py处运行结果
连接地址: ('127.0.0.1', 49427)
我收到了
#.py处运行结果
网络编程测试
5.UDP网络程序
UDP:用户数据报协议是一个无连接的简单的面向数据报的运输层协议。一般多用于多点通信和实时的数据业务,比如:视频、qq、语音广播等。
优点:传输速度快。UDP在传输时无需在客户端和服务器端之间建立连接,也无超时重新发送机制。
缺点:不能保证可靠性。UDP是一种模型无连接的协议,每个数据都是一个独立的信息,包含完整的源地址或者目的地址,在网络上以任何可能的路径传往目的地。因此能够到达的目的地以及到达的时间和内容的正确性都无法保证。
特点:
- 面向无连接的通讯协议。
- 包含目的端口号和源端口号信息,通讯不需要连接,能够实现广播发送。
- 传输的数据大小有限制,每个被传输的数据报必须限定在64k以内。
- UDP是一个不可靠的协议。发送出去的数据报不一定以相同的次序到达接收方。
流程:
- 创建一个客户端套接字
- 发送/接收数据
- 关闭套接字
socket和file的区别:
file针对指定模块进行’打开’,‘读写’,‘关闭’
socket针对服务器端和客户端socket进行’打开’,‘读写’,‘关闭’
demo:
循环发送/接收数据
udp_sendmsg.py脚本代码:
import socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
addr = ('192.168.1.134',1234)
while True:
sendDate = input('请输入要发送的数据:')
udp_socket.sendto(sendDate.encode('utf-8'), addr)
udp_socket.close()
udp_recmsg.py脚本代码:
import socket
udp_socket1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket1.bind(('192.168.1.134', 1234))
while True:
mmsg = udp_socket1.recvfrom(1024)
print(str(mmsg[0]),type(str(mmsg[0])))
udp_socket1.close()
运行结果:
#udp_sendmsg.py运行结果
请输入要发送的数据:qewqwe
请输入要发送的数据:weqe
#udp_recmsg.py运行结果
b'qewqwe' <class 'str'>
b'weqe' <class 'str'>
使用网络调试助手时,端口号会一直变动
5.1UDP端口号绑定:
一般情况下,在一台电脑上运行的网络程序有很多,为了不与其他的网络程序占用同一个端口号,往往在编程中,udp的端口号一般不绑定。但是如果需要做成一个服务器端的程序的话,是需要绑定的,想想看这又是为什么呢?如果报警电话每天都在变,想必世界就会乱了,所以一般服务性的程序,往往需要一个固定的端口号,这就是所谓的端口绑定
demo:
import socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
local_addr = ('', 12345) # ip地址和端口号,ip可以不写默认本地。
udp_socket.bind(local_addr)
udp_data = udp_socket.recvfrom(1024)
print(udp_data[0].decode('gbk'))
udp_socket.close()
总结:
- 一个udp网络程序,可以不绑定端口,此时系统会自动分配一个端口。重新运行此程序,端口号可能会发生变化。
- 一个udp网络程序,可以绑定信息(IP,Port)。如果绑定成功,那么操作系统用这个端口号来进行区别收到的网络数据是否是此进程的。
6.TCP简介
TCP协议:传输控制协议,是一种面向连接、可靠的、基本字节流的传输层通信协议。
TCP通信需要经过创建连接,传输数据,终止连接三个步骤。
TCP特点
面向连接
通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。
双方间的数据传输都可以通过这个连接进行。
完成数据交换后,双方断开此连接,以释放系统资源。
这种连接是一对一的
因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议。
可靠传输
1)TCP采用发送应答机制
TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功
2)超时重传
发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。
TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
3)错误校验
TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
- 流量控制和阻塞管理
流量控制用来避免主机发送得过快而使接收方来不及完全收下。
TCP和UDP的不同点
TCP | UDP |
---|---|
面向连接 | 面向无连接的通讯协议 |
有序的数据传输 | UDP是一个不可靠的协议。发送出去的数据报不一定以相同的次序到达接收方。 |
无差错的数据传输(重发丢失的数据包,舍弃重复的数据包) | |
阻塞/流量控制 | |
TCP通信模型,类型打电话。在通信开始前一定要建立相关连接,才能发送数据 | UDP通信模型,类似写信,不需要建立相关连接,只需要发送数据即可 |
7.Socket 对象(内建)方法
函数 | 描述 |
---|---|
服务器端套接字 | |
s.bind() | 绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。 |
s.listen() | 开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 |
s.accept() | 被动接受TCP客户端连接,(阻塞式)等待连接的到来 |
客户端套接字 | |
s.connect() | 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 |
s.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 |
公共用途的套接字函数 | |
s.recv() | 接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。 |
s.send() | 发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。 |
s.sendall() | 完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 |
s.recvform() | 接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 |
s.sendto() | 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。 |
s.close() | 关闭套接字 |
s.getpeername() | 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 |
s.getsockname() | 返回套接字自己的地址。通常是一个元组(ipaddr,port) |
s.setsockopt(level,optname,value) | 设置给定套接字选项的值。 |
s.getsockopt(level,optname[.buflen]) | 返回套接字选项的值。 |
s.settimeout(timeout) | 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect()) |
s.gettimeout() | 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。 |
s.fileno() | 返回套接字的文件描述符。 |
s.setblocking(flag) | 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。 |
s.makefile() | 创建一个与该套接字相关连的文件 |