tcp的粘包现象与解决方案

时间:2024-01-19 19:20:38

粘包现象:

粘包1:连续的小包,会被优化机制给合并

粘包2:服务端一次性无法完全就收完客户端发送的数据,第二再次接收的时候,会接收到第一次遗留的内容

模拟一个粘包现象

服务端

import socket

server = socket.socket(type=socket.SOCK_STREAM)
ip_port = ('127.0.0.1',8080)
server.bind(ip_port) server.listen()
conn, addr = server.accept() # 粘包1:连续的小包,会被优化机制给合并
recv_mes1 = conn.recv(1024)
# 第二次拿到的就是空数据,因为客户端关闭,服务端会自动执行后续的recv,如果客户端没关闭,服务端就会在recv
# 处阻塞
recv_mes2 = conn.recv(1024) print('第一次接收',recv_mes1.decode('utf-8'))
print('第二次接收',recv_mes2.decode('utf-8')) conn.close()
server.close()

客户端

import socket

client = socket.socket(type=socket.SOCK_STREAM)

ip_port = ('127.0.0.1',8080)
client.connect(ip_port) client.send('哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈'.encode('utf-8'))
client.send('呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵'.encode('utf-8')) client.close()

代码执行结果:

tcp的粘包现象与解决方案

客户端两次发的内容都被服务端第一个recv接收了,这即为粘包!

粘包解决方案一:

先告诉客户端,数据信息的长度,然后等客户端确认之后,再发送真实内容

代码示例:

import socket
# 服务端
# 读者可以自行创建一个txt文档,里面放的内容劲量大于1024字节,可以放一篇文章进去
with open('test.txt',mode='rb') as f:
content = f.read() data_len = len(content)
print('文章长度:',data_len)
data_len_str = str(data_len)
count = 0 server = socket.socket(type=socket.SOCK_STREAM)
ip_port = ('127.0.0.1',8080)
server.bind(ip_port)
server.listen()
conn, addr = server.accept()
print(addr,'已经连接') conn.send(data_len_str.encode('utf-8')) status = conn.recv(1024)
if status == b'ok':
while count <data_len:
send_mes = content[count:count+1024]
len_of_mes = len(send_mes)
conn.send(send_mes)
count +=len_of_mes server.close()
import socket
# 客户端
content = b''
count = 0
client = socket.socket(type=socket.SOCK_STREAM)
ip_port = ('127.0.0.1',8080)
client.connect(ip_port) data_len_str = client.recv(1024).decode('utf-8')
data_len = int(data_len_str) client.send(b'ok') while count < data_len:
recv_mes = client.recv(1024)
content +=recv_mes
count += 1024 print(content.decode('utf-8'))
client.close()

粘包解决方案二:

通过struct模块,将要发送的真实数据的长度进行打包,打包成4个字节,和真实数据一起一次性发送个客户端.客户端取出前4个字节,通过struct解包获得后面真实数据的长度,根据这个长度再进行数据的接受

代码示例:

import socket
import struct
# 服务端 server = socket.socket(type=socket.SOCK_STREAM)
ip_port = ('127.0.0.1',8080)
server.bind(ip_port)
server.listen()
conn, addr = server.accept()
print(addr,'已经连接') with open('test.txt',mode='rb') as f:
for data in f:
# data = f.readline()
data_len = len(data)
data_len_b = struct.pack('i',data_len)
conn.send(data_len_b+data) input('输入任意键结束')
server.close() ---------------------分割线------------------------
# 客户端
import socket
import struct client = socket.socket(type=socket.SOCK_STREAM)
ip_port = ('127.0.0.1',8080)
client.connect(ip_port)
content = b'' while 1:
recv_mes = client.recv(4)
data_len = struct.unpack('i',recv_mes)[0]
recv_mes = client.recv(data_len)
print(recv_mes.decode('utf-8'),end='') client.close()