什么是粘包?出现粘包的原因?如何解决?

本文解释了粘包现象的定义,探讨其由接收方未及时接收和应用层协议设计缺陷引起的原因。提供了通过发送数据长度预兆和相应服务器/客户端代码示例来避免粘包的解决方案。
摘要由CSDN通过智能技术生成

什么是粘包?

简单来说,粘包就是连续向对端发送两个或者两个以上的数据包,对端在一次收取中收到的数据包数量可能大于1个,当大于1个时,可能时几个包加上某个包的部分。当然,也可能收到的数据只是一个包的部分,这种情况一般也叫做半包。

多个数据包被连续存储于连续的缓存中,在对数据包进行读取时由于无法确定发送方的发送边界,而采用某一估测值大小来进行数据读出,若双方的size不一致时就会使发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。

出现粘包的原因?

出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。

先说接收方原因:接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区中取数据,这样就一次取到了多包数据。

再说由发送导致的粘包:粘包并不是 TCP 协议造成的,它的出现是因为应用层协议设计者对 TCP 协议的错误理解。我们将从 TCP 协议以及应用层协议出发,分析我们经常提到的 TCP 协议中的粘包是如何发生的。

TCP 协议是面向字节流的协议,它可能会组合或者拆分应用层协议的数据;

应用层协议没有定义消息的边界导致数据的接收方无法拼接数据;

TCP 协议是面向连接的、可靠的、基于字节流的传输层通信协议,应用层交给 TCP 协议的数据并不会以消息为单位向目的主机传输,这些数据在某些情况下会被组合成一个数据段发送给目标的主机。

解决方案?

发送数据的时候,先发送大小,然后再发送内容。

server端代码:

	def send_file(self, data, flag_bytes=False):
	    if flag_bytes:
	        data_len = len(data)
	        self.new_socket.send(struct.pack('I', data_len))  # 先发长度(大小)
	        self.new_socket.send(data)  # 再发内容
	    else:
	        # 字符串
	        data_bytes = data.encode('utf8')
	        data_len = len(data_bytes)
	        self.new_socket.send(struct.pack('I', data_len))  
	        self.new_socket.send(data_bytes) 
	
	def recv_file(self, new_socket):
	    # struct.unpack(fmt, string)
	    #
	    #   顾名思义,解包。比如pack打包,然后就可以用unpack解包了。返回一个由解包数据(string)
	    #    得到的一个元组(tuple), 即使仅有一个数据也会被解包成元组。
	    file_len = new_socket.recv(4)  # 拿到长度
	    file_content_len = struct.unpack('I', file_len)[0]  # [0]是因为unpack得到的是一个元组,[0]才是你要的数(file_len)
	    file_content: bytes = new_socket.recv(file_content_len)
	    return file_content.decode('utf8')

client端代码:

	def send_file(self, new_socket, data: str):
	    data_bytes = data.encode('utf8')
	    data_len = len(data_bytes)
	    # struct.pack(fmt,v1,v2,.....)
	    #
	    #   将v1,v2等参数的值进行一层包装,包装的方法由fmt指定。被包装的参数必须严格符合fmt。最后返回一个包装后的字符串。
	    new_socket.send(struct.pack('I', data_len))  # 发长度
	    new_socket.send(data_bytes)  # 发内容
	
	def recv_file(self):
	    file_len = self.client_socket.recv(4)  # socket.recv()指接收对方发来的数据,4表示最多收4字节
	    file_content_len = struct.unpack('I', file_len)[0]
	    file_content: bytes = self.client_socket.recv(file_content_len)
	    return file_content.decode('utf8')
  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值