一.什么是粘包问题
tcp协议通过negle算法会将数据量较小,发送时间间隔较短的多个数据包合并一个发送
二.如何解决粘包问题
解决粘包问题就在于要知道每个包的数据大小,然后在准确的接受就可以了.
这里使用自定义报头的方法,思路:
1.先发固定的报头长度,比如4个bytes(基于struct可以把任意类型转换会固定的bytes)
2.先发送自定义报头(报头中要包含真正数据的一些信息,如文件名,文件大小,md5值等等)
3.再发送真正的数据
关键就在于如何制作包含真正数据信息的报头
1.报头要包含真正数据的信息,那么我们可以用字典的形式记录下来(header_dic).
2.利于json模块把字典序列化成json格式的字符串(header_json)
3.既然是字符串肯定可以encode,那么把json格式的字符串encode成bytes类型(header_bytes).
4.然后用len方法计算长度(header_len=len(header_bytes))
5.利用struct把header_len转换为固定的4个bytes
6.发送固定的4个bytes(接收端首先也接受固定的4个bytes)
7.再发送报头数据header_bytes
8.再发送真正的数据
9.接受端先接受固定的4个bytes,然后再接受报头然后反解出来,最后再根据反解得到的header_dic里的内容(如文件名,文件大小,md5值等等)接受真正的数据.
服务端
import subprocess
from socket import *
import struct
import json
server = socket(AF_INET,SOCK_STREAM)
HOST = '127.0.0.1'
PORT = 8080
ADDR = (HOST,PORT)
BUFSIZE = 1024
server.bind((ADDR))
server.listen(5)
while True:
conn,addr = server.accept()
print('客户端:',addr)
while True:
try:
cmd = conn.recv(BUFSIZE)
if len(cmd) == 0:break
print(cmd.decode('utf-8'))
obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
# 1.先制作报头
header_dic = {'filename':'a.txt',
'total_size':len(stdout)+len(stderr),
'hash':'qwerqw165q45e1qew5631'}
#2.将报头转化为json格式的字符串
header_json = json.dumps(header_dic)
#3.将json格式的字符串转化为bytes
header_bytes = header_json.encode('utf-8')
print(header_bytes)
#4.再将bytes类型的json文件通过模块struct转化为固定长度的bytes
# struct.pack('i',header_bytes)
#5.发送报头的的固定长度
conn.send(struct.pack('i',len(header_bytes)))
#6.发送报头
conn.send(header_bytes)
# 7.再发送真实的数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
客户端
import struct
import json
from socket import *
client = socket(AF_INET,SOCK_STREAM)
HOST = '127.0.0.1'
PORT = 8080
ADDR = (HOST,PORT)
BUFSIZE = 1024# 再大不能超过8096
client.connect(ADDR)
while True:
msg = input('>>>:').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
res = b''
recv_size = 0
#1.先拿到报头的固定长度4,然后把4个bytes通过struct模块的unpack并反转换成报头的大小数字
header_len = struct.unpack('i',client.recv(4))[0]
#2.通过大小信息拿到报头的bytes信息
header_bytes = client.recv(header_len)
#3.将bytes反解成json格式的字符串
header_json = header_bytes.decode('utf-8')
#4.将json格式反解成报头字典信息
header_dic = json.loads(header_json)
#5.取出真实数据的文件大小信息
total_size = header_dic['total_size']
while recv_size<total_size:
#6.接受真正的数据
data = client.recv(BUFSIZE)
res+=data
recv_size+=len(data)
print(res.decode('gbk'))