粘包现象及解决方法(Python)
什么是粘包
只有TCP有粘包现象,UDP没有
socket收发消息的原理
应用程序看到的数据是一个整体,或称为一个流(stream),而一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议。
而UDP协议是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据。
而消息可以认为发送方一次性write/send的数据为一个消息,但是,当send一条信息时,无论底层怎样分段分片,TCP协议层会把构成整条信息的数据段排序完成后才呈现在内核缓冲区。即面向流的通信是无消息保护边界的。
由于UDP支持的是一对多的模式,所以接收端的套接字缓冲区采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址、端口等信息),对于接受端来说就容易进行区分处理。即面向消息的通信是有消息保护边界的
所谓粘包问题主要是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要发送的数据都很少,通常TCP会根据优化算法(Nagle算法)把这些数据合成为一个TCP段后一次发送出去。
两种情况下会发生粘包
1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据量很小,会合并到一起,产生粘包)
2.接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一部分,服务端下一次接收的时候还是从缓冲区取上次遗留的数据,产生粘包)
解决粘包
1.发送端发送数据之前,先将数据长度让接收端知晓,接收端根据总长度利用循环完成消息的接收
# 客户端:
from socket import *
ip_port = ('127.0.0.1', 8000)
back_log = 5
buffer_size = 1024
tcp_client = socket(AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port)
while True:
cmd = input(">>:").strip().encode("utf-8")
if not cmd: continue
if cmd == 'quit': break
tcp_client.send(cmd) # 发送指令
length = tcp_client.recv(buffer_size).decode("utf-8") # 接收数据长度信息
tcp_client.send(b"ready") # 发送确认信息
server_result = tcp_client