day30 粘包
一,什么是粘包?
发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。
所谓粘包问题,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成。
两种情况下会发生粘包:
一种:
发送端需要等缓冲区满了才发送出去,造成粘包(发送数据时间间隔很短,数据很小,就会合到一起,产生粘包)
二种:
接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包。)
解决粘包的方法:
为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端再接收时,先从缓存中取出固定长的报头,然后再取真实数据。
补充知识:
struct模块
该模块可以吧一个整形,转成固定长度的bytes类型。
import struct
s=struct.pack('i',123232)
print(s,len(s))
s1=struct.unpack('i',s)
print(s1)
我们可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节(4个自己足够用了)
发送时:
先发报头长度
再编码报头内容然后发送
最后发真实内容
接收时:
先手报头长度,用struct取出来
根据取出的长度收取报头内容,然后解码,反序列化
从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容
服务端:
from socket import *
import subprocess
import struct
import json
server=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,client_addr=server.accept()
print('新的客户端',client_addr)
while True:
try:
cmd=conn.recv(1024) #cmd=b'dir'
if len(cmd) == 0:break
# 运行系统命令
obj=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE
)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
#先制作报头
header_dic={
'filename':'a.txt',
'total_size':len(stdout) + len(stderr),
'hash':'xasf123213123'
}
header_json=json.dumps(header_dic)
header_bytes=header_json.encode('utf-8')
#1、先把报头的长度len(header_bytes)打包成4个bytes,然后发送
conn.send(struct.pack('i',len(header_bytes)))
#2、发送报头
conn.send(header_bytes)
#3、再发送真实的数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
客户端:
from socket import *
import struct
import json
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
cmd=input('>>: ').strip()
if len(cmd) == 0:continue
client.send(cmd.encode('utf-8'))
#1、先收4个字节,该4个字节中包含报头的长度
header_len=struct.unpack('i',client.recv(4))[0]
#2、再接收报头
header_bytes=client.recv(header_len)
#从报头中解析出想要的内容
header_json=header_bytes.decode('utf-8')
header_dic=json.loads(header_json)
print(header_dic)
total_size=header_dic['total_size']
#3、再收真实的数据
recv_size=0
res=b''
while recv_size < total_size :
data=client.recv(1024)
res+=data
recv_size+=len(data)
print(res.decode('gbk'))