测试环境
粘包和分包
- 粘包:发送方发送两个字符串”hello”+”world”,接收方却一次性接收到了”helloworld”
- 分包:发送方发送字符串”helloworld”,接收方却接收到了两个字符串”hello”和”world”
解决方案
- 自定义传输协议:消息头 + 消息体
- 其中消息头定长,且包含消息体的长度
具体操作
- 关键:一个FIFO队列作为数据缓冲区,用于接收数据和判断
- 流程:
- 把从socket接收到的数据,push进队列
- 判断数据的长度是否大于消息头(自定义长度)的长度,如果成立则继续下一步,否则跳出循环继续接收数据
- 如果当前数据长度大于消息头,则读取消息头里消息体的长度,判断当前数据长度是否大于消息头+消息体的长度
- 如果当前数据大于消息头+消息体的长度,则处理数据,然后pop出队列
代码实现
import socket
import struct
HOST = ''
PORT = 1234
dataBuffer = bytes()
headerSize = 12
sn = 0
def dataHandle(headPack, body):
global sn
sn += 1
print(f"第{sn}个数据包")
print(body.decode())
print("\n")
if __name__ == '__main__':
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
with conn:
print('Connected by', addr)
while True:
data = conn.recv(1024)
if data:
dataBuffer += data
while True:
if len(dataBuffer) < headerSize:
print("数据包(%s Byte)小于消息头部长度,跳出小循环" % len(dataBuffer))
break
headPack = struct.unpack('!3I', dataBuffer[:headerSize])
bodySize = headPack[1]
if len(dataBuffer) < headerSize+bodySize :
print("数据包(%s Byte)不完整(总共%s Byte),跳出小循环" % (len(dataBuffer), headerSize+bodySize))
break
body = dataBuffer[headerSize:headerSize+bodySize]
dataHandle(headPack, body)
dataBuffer = dataBuffer[headerSize+bodySize:]
import socket
import time
import struct
import json
host = "localhost"
port = 1234
ADDR = (host, port)
if __name__ == '__main__':
client = socket.socket()
client.connect(ADDR)
ver = 1
body = json.dumps(dict(hello="world"))
print(body)
cmd = 101
header = [ver, body.__len__(), cmd]
headPack = struct.pack("!3I", *header)
sendData1 = headPack+body.encode()
ver = 2
body = json.dumps(dict(hello="world2"))
print(body)
cmd = 102
header = [ver, body.__len__(), cmd]
headPack = struct.pack("!3I", *header)
sendData2_1 = headPack+body[:2].encode()
sendData2_2 = body[2:].encode()
ver = 3
body1 = json.dumps(dict(hello="world3"))
print(body1)
cmd = 103
header = [ver, body1.__len__(), cmd]
headPack1 = struct.pack("!3I", *header)
ver = 4
body2 = json.dumps(dict(hello="world4"))
print(body2)
cmd = 104
header = [ver, body2.__len__(), cmd]
headPack2 = struct.pack("!3I", *header)
sendData3 = headPack1+body1.encode()+headPack2+body2.encode()
client.send(sendData1)
time.sleep(3)
client.send(sendData2_1)
time.sleep(0.2)
client.send(sendData2_2)
time.sleep(3)
client.send(sendData3)
time.sleep(3)
client.close()
效果
Connected by ('127.0.0.1', 29771)
第1个数据包
ver:1, bodySize:18, cmd:101
{"hello": "world"}
数据包(0 Byte)小于消息头部长度,跳出小循环
数据包(14 Byte)不完整(总共31 Byte),跳出小循环
第2个数据包
ver:2, bodySize:19, cmd:102
{"hello": "world2"}
数据包(0 Byte)小于消息头部长度,跳出小循环
第3个数据包
ver:3, bodySize:19, cmd:103
{"hello": "world3"}
第4个数据包
ver:4, bodySize:19, cmd:104
{"hello": "world4"}