python粘包问题及解决⽅法
⼀粘包
  TCP协议是⾯向对象的,⾯向流的,提⾼可靠性服务。使⽤了优化算法,Nagle算法。将多次间隔较少且数据量⼩的数据,合并成⼀个⼤的数据块,然后进⾏封包。这样接收端就很难分辨出来。TCP协议数据是可靠的,但是会粘包。
  问题的关键是另⼀端并不知道你要传多少字节的数据,处理办法是必须先传⼀个报头,告诉另⼀端我要发送多少,然后另⼀端就接收指定数量的字节,这是可以做到的。粘包问题就解决了。
  借助struct模块来解决。
⼆ struct模块
import struct
res=struct.pack('i',111)
print(res,len(res))
res=struct.pack('i',11111111)
print(res,len(res))
  输出:
b'o\x00\x00\x00' 4
b'\xc7\x8a\xa9\x00' 4
  struct模块对int类型的数字进⾏字节化,(bytes⽅法是把字符串类型的字节化),⼀定范围内的数字字节化后的长度都是4个字节。
import struct
res=struct.pack('i',111)        #'i’是表⽰整数格式
print(res,len(res))
ret=struct.unpack('i',res)    #unpack的结果是⼀个元组
print(ret)
print(ret[0])                  #取第⼀个值,便是pack的整数
  输出:
b'o\x00\x00\x00' 4
(111,)
111
三彻底解决粘包问题。
  思路:
  A想要给B发送⼀个字典,这个字典包含filename,filesize,md5等等信息,怎么发送呢
  先⽤json把字典序列化为字符串,然后字节化。⽤len⽅法取其长度 length。这个length就是字典字符串化字节化的长度。
  在⽤sturct模块,pack⽅法将上⾯取到的length,字节化的结果,传给B,这个结果固定长度为4。
  B这边,先recv(4),拿到后,⽤struct的unpack⽅法,取到的就是字典字符串化字节化的长度length。
  在recv(length),拿到的就是字典字符串化字节化的结果。这个结果怎么来的就怎么倒回去,decode,json.loads(),拿到字典。拿到filesize。
  while循环,接收长度为filesize的字节。
  到此,粘包问题解决,⼤功告成。
  简⾔之:
    服务器  1 发送报头的长度。借⽤struct模块客户端    1 接收报头的长度。  recv(4),struct模块unpack取到报头的长度length
         2 发送报头。借⽤json模块                                                      2 接收报头。              recv(lenth)。json反序列化得到报头。报头中有‘’size‘’
          3 发送真实数据                                                                      3 接收真实数据报头中含有size,对应真实数据的数据。
  客户端:
import socket
import struct
import json
#1、买⼿机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、打电话
#3、发收消息
while True:
cmd=input('>>: ').strip()
if cmd == 'quit':break
if not cmd:continue
phone.de('utf-8'))
#先收报头长度
v(4)
header_size=struct.unpack('i',obj)[0]
#再收报头
header_v(header_size)
header_json=header_bytes.decode('utf-8')
header_dic=json.loads(header_json)
print(header_dic)
#最后循环收真实的数据
total_size=header_dic['total_size']
filename=header_dic['filename']
total_data=b''
recv_size=0
with open(filename,'wb') as f:
while recv_size < total_size:
recv_v(1024)
total_data+=recv_data
recv_size+=len(recv_data)
print(total_data.decode('gbk'))
#4、挂电话
phone.close()
 服务端
import socket
import subprocess
import struct
import json
#1、买⼿机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、绑定⼿机卡
# phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加phone.bind(('192.168.11.53',8080))
#3、开机
phone.listen(5)
#4、等电话连接
print('')
while True: #连接循环
conn,addr=phone.accept()
print('IP:%s,PORT:%s' %(addr[0],addr[1]))
#5、收发消息
while True: #通信循环
try:
字符串长度怎么数v(1024) #最⼤收1024
if not cmd:break#针对linux
#执⾏命令
obj=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout=ad()
stderr=ad()
#制作报头
header_dic = {'filename': 'a.txt',
'total_size': len(stdout)+len(stderr),
'md5': 'asdfa123xvc123'}
header_json = json.dumps(header_dic)
header_bytes = de('utf-8')
#先发报头的长度。发送的长度是字典格式的报头经过json化的bytes化处理的长度。经过struct.pack,固定长度为4个字节。            conn.send(struct.pack('i',len(header_bytes)))
#再发送报头。发送的报头是json化,bytes化后的报头。
conn.send(header_bytes)
#最后发送真实数据
conn.send(stdout)
conn.send(stderr)
except Exception:
break
#6、挂电话
conn.close()
#7、关机
phone.close()

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。