一、什么是粘包/半包问题

在客户端发送数据时,实际是把数据写入到了TCP发送缓存里面的; 如图:

粘包/半包问题解析

1. 如果发送的包的大小比TCP发送缓存的容量大那么这个数据包就会被分成多个包,通过socket多次发送到服务端,服务端第一次从接受缓存里面获取的数据,实际是整个包的一部分,这时候就产生了半包现象,半包不是说只收到了全包的一半,是说收到了全包的一部分。

2. 如果发送的包的大小比TCP发送缓存容量小,并且TCP缓存可以存放多个包,那么客户端和服务端的一次通信就可能传递了多个包,这时候服务端从接受缓存就可能一下读取了多个包,这时候就出现了粘包现象。

服务端从接受缓存读取数据后一般都是进行解码操作,也就是会把byte流转换了pojo对象,如果出现了粘包或者半包现象,则进行转换时候就会出现异常。出现粘包和半包的原因是TCP层不知道上层业务的包的概念,它只是简单的传递流,所以需要上层应用层协议来识别读取的数据是不是一个完整的包


示例:

假设客户端分别发送了两个数据包Data1和Data2给服务端, 由于服务端一次读取到的字节数是不确定的, 故可能存在以下4种情况:
    (1) 服务端分两次读取到了两个独立的数据包,分别是Data1和Data2,没有粘包和拆包

         粘包/半包问题解析
  (2) 服务端一次接收到了两个数据包,Data1和Data2粘合在一起,被称为TCP粘包

         粘包/半包问题解析
  (3) 服务端分两次读取到了两个数据包,第一次读取到了完整的Data1包和Data2包的前一部分内容Data2_1,第二次读取到了Data2包的剩余内容Data2_2,这被称为TCP拆包

         粘包/半包问题解析
  (4) 服务端分两次读取到了两个数据包,第一次读取到了Data1包的部分内容Data1_1,第二次读取到了Data1包的剩余内容Data1_2和Data2包的整包

         粘包/半包问题解析

如果此时服务端TCP接收缓冲区非常小,而数据包Data1和Data2比较大,很有可能会发生第五种可能,即服务端分多次才能将Data1和Data2包接收完全,期间发生多次拆包

二、TCP粘包/半包发生的原因

1. 客户端发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包

2. 客户端待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包

3. 客户端发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包

4. 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包

三、粘包/半包解决办法

1. 可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开

2. 发送端将每个数据包封装为固定长度(不够的可以通过补0填充), 例如每个报文的大小为固定长度200字节,这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来

3. 发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了


四、Netty中粘包/半包解决示例

 

相关文章: