解决数据传输粘包的问题
服务端
将数据分为6个阶段发送给客户端
- 将报头内容,制作成字典形式
- 将字典dumps为json字符串
- 把得到的json字符串,转换为二进制bytes
- 通过struct.pack把二进制bytes的json字符串计算出4个字节长度
- 发送报头 4个字节长度
- 发送数据
import socket
import subprocess
import struct
import jsonphone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 在bing 之前添加; 用于端口重用,释放phone.bind(("127.0.0.1",8080))phone.listen(5)print("server start....")# 接收消息,和发送消息
while True:conn,client_addr=phone.accept() # 重新建立连接while True:try:cmd = conn.recv(1024)print("客户端接收:",cmd.decode("utf-8"))if not cmd:break # 针对linux 客户端如果断开后,服务端持续打印空res = subprocess.Popen(cmd.decode("utf-8"),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)stdout = res.stdout.read()stderr = res.stderr.read()# 制作报头# header = struct.pack("i",len(stdout)+len(stderr)) # i 表示把长度打包成 4个 字节长度header_dic = {"total_size":len(stdout)+len(stderr),"md5":None}header_json = json.dumps(header_dic)header_bytes = header_json.encode("utf-8")# 先把报头的长度 打包成 4个 字节长度 发送给客户端conn.send(struct.pack("i",len(header_bytes)))# 发送报头conn.send(header_bytes)conn.send(stderr)conn.send(stdout)except Exception: # 针对windows 如果客户端断开,服务端也断开breakconn.close()
phone.close()
客户端
客户端接收数据
- 接收报头长度为struct.pack“i”大小为4个字节,固定长度
- 解析报头,取出真正数据报头的长度
- 根据数据报头的长度收取bytes字符串长度
- 提取报头字典
- 从字典中取出,数据的大小
- 循环接收数据
import socket
import struct
import jsonphone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.connect(("127.0.0.1",8080))# phone.send("hello".encode("utf-8"))
while True:cmd = input(">>:").strip()if not cmd:continuephone.send(cmd.encode("utf-8"))# 接收报头长度,大小为 “i” 定义的4个字节struct_res = phone.recv(4)# 解析报头,取出报头长度header_size = struct.unpack("i",struct_res)[0]# 在收报头header_bytes = phone.recv(header_size)# 提取报头字典head_json = header_bytes.decode("utf-8")head_dic = json.loads(head_json)# 取出loads后字典中的total_size的值total_size = head_dic["total_size"]# 根据 取出的 字符串长度,循环接收内容recv_size = 0 # 初始接收字节数为空data = b"" # 初始数据值为空while recv_size < total_size:recv_date = phone.recv(1024)recv_size += len(recv_date)data += recv_dateprint("命令结果:", data.decode("gbk"))phone.close()