在python网络编程中两台电脑在进行收发数据时,其实不是直接将数据传输给对方。
对于发送者,执行 sendall/send 发送消息时,是将数据先发送至自己网卡的 写缓冲区 ,再由缓冲区将数据
对于接受者,执行 recv 接收消息时,是从自己网卡的读缓冲区获取数据。
所以,如果发送者连续快速的发送了2条信息,接收者在读取时会认为这是1条信息,即:2个数据包粘在了一起。
粘包示例:这里ip是以本机默认ip,方便测试客户端:发送方
import socket #创建连接 client = socket.socket() client.connect(('127.0.0.1', 8001)) #发送两条消息 client.sendall('学如初出之苗,不见其增'.encode('utf-8')) client.sendall('日有所长'.encode('utf-8')) #关闭连接 client.close()
服务端:接收方
import socket #创建连接 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8001)) sock.listen(5) #等待连接 conn, addr = sock.accept() 读取收到的数据并打印 client_data = conn.recv(1024) print(client_data.decode('utf-8')) #输出结果:学如初出之苗,不风其增日有所长 conn.close() sock.close()
发送方分开发送了2条的消息,接收却是1条,显然是不合适的
解决粘包的问题每次发送的消息时,都将消息划分为 头部(固定字节长度) 和 数据 两部分。例如:头部,用4个字节表示后面数据的长度。
发送数据,先发送数据的长度,再发送数据(或拼接起来再发送)。
接收数据,先读4个字节就可以知道自己这个数据包中的数据长度,再根据长度读取到数据。
对于头部需要一个数字并固定为4个字节,这个功能可以借助python的struct包来实现:
import struct # i 表示4个字节的int # 199 表示字节大小为199 v1 = struct.pack('i', 199)# 把199转成固定4字节 print(v1) # b'xc7x00x00x00' v2 = struct.unpack('i', v1) # 4个字节转换为数字 print(v2) # 199
附上转字节大小图
接下来示例
发送方
import socket import struct client = socket.socket() client.connect(('127.0.0.1', 8001)) # 第一条数据 data1 = '惰如磨刀之石'.encode('utf-8') len1 = struct.pack('i', len(data1))# 获取要发送数据的字节长度 client.sendall(len1)# 先发送数据字节长度 client.sendall(data)# 发送数据 # 第二条数据 data2 = '不见其损,日有所耗'.encode('utf-8') len2 = struct.pack('i', len(data2)) client.sendall(len2) client.sendall(data2) client.close()
接收方
import socket import struct #创建连接,等待连接 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8001)) sock.listen(5) conn, addr = sock.accept() # 固定读取4字节 header1 = conn.recv(4) data_length1 = struct.unpack('i', header1)[0] has_recv_len = 0 data1 = b"" while True: length = data_length1 - has_recv_len if length > 1024: lth = 1024 else: lth = length chunk = conn.recv(lth) # 可能一次收不完,自己可以计算长度再次使用recv收取,只到收完为止。 接收最大字节1024*8 = 8196 data1 += chunk has_recv_len += len(chunk) if has_recv_len == data_length1: break print(data1.decode('utf-8'))# 惰如磨刀之石 # 固定读取4字节 header2 = conn.recv(4) data_length2 = struct.unpack('i', header2)[0] # 数据字节长度 data2 = conn.recv(data_length2) # 读取数据 print(data2.decode('utf-8')) # 不见其损,日有所耗 conn.close() sock.close()
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)