Python之粘包成因与解决

粘包成因:

1.tcp协议的拆包机制,当发送缓冲区的大小大于网卡的MTU(网络上传输的最大数据包)时,大数据报会被拆分程多个小包传送,接收端,接收端一次性未完全接收所有的包,会与下一次传输的时候与下一次传输的数据一起接收过来。

2.面向流的通信特点合Nagle优化算法,当客户端连续发送多个小数据包时,会合并成一个较大的包进行发送,则客户端接收的数据会看到客户端发来的两个不同包的数据合并在了一个包里

server端:

import socket
sk = socket.socket()
addr = ('127.0.0.1',8089)
sk.bind(addr)
sk.listen(5)
conn,adr = sk.accept()
# client端发送两次,通常server端需要接收两次
ret = conn.recv(1024).decode('utf-8')
ret1 = conn.recv(1024).decode('utf-8')
print('ret:' + ret)
print('ret1:' + ret1)
conn.close()
sk.close()
'''
输出结果:
ret:hello,word
ret1:
'''

client端:

import socket
sk = socket.socket()
addr = ('127.0.0.1',8089)
sk.connect(addr)
sk.send('hello,'.encode('utf-8'))
sk.send('word'.encode('utf-8'))
sk.close()

可以看到,客户端发送的两个数据包,发往服务端的时候做了合包机制,粘包的根本原因在于数据接收方不知道数据发送端发送的数据大小,无消息边界

2.粘包的解决--struct模块定制报头协议

struct模块功能:将python数据类型转化成定长byte类型

例如:int型转化完成后定长为4字节byte类型

import struct
ret1 = struct.pack('i',136500) # 转化
ret2 = struct.pack('i',4500) # 转化
print(len(ret1),len(ret2)) # 4 4 转化后结果均为4字节
print(ret1,ret2) # b'4\x15\x02\x00' b'\x94\x11\x00\x00'

将转化后的数据转化回来使用

res1 = struct.unpack('i',ret1) # 返回一个元组
res2 = struct.unpack('i',ret2)
print(res1)
print(res2)

通过struct的特性,可以先将要发送的数据的大小,通过struct转化后经由网络传输,将数据大小先发送给接收端,接收端由此获得将要接收的数据的大小,根据数据大小去接收,则可以避免发生粘包现象

例如:文件传输定义包头协议,在正式发送文件内容之前,首先要将文件名,文件大小,文件存储路径发送给接收端

import struct
import json

head = {"filename":"test.txt","filesize":214560,"storepath":"/tmp"} # 文件传输报头
head_json = json.dumps(head) # 报头转化为json格式
print(head_json)
head_strut = struct.pack('i',len(head_json))
print(head_strut,len(head_strut)) # b'A\x00\x00\x00' 4
# 报头长度封装成4个字节大小,发送给接收端

# 接收端对先接收4个字节-即接收报头长度,然后通过接收到地长度,接收报头,通过报头地文件大小再进行文件传输

猜你喜欢

转载自blog.csdn.net/qq_40199698/article/details/89070439