TCP 拆包和粘包原因及解决方案

1. 拆包和粘包的概念

在这里插入图片描述
上图为 TCP 协议传输的大致过程,其数据传输的性质是流式的,并没有分段的概念,所以这个过程可能有 3 种情况:

  1. 正常情况
    发送端发了两条消息,接收端也读到了两个数据包,第一个包包含发送端发出的第一条消息的完整信息,第二个包包含完整的第二条消息。这种情况接收端只需要简单的从缓冲区去读数据就好了,能够正确处理
  2. TCP粘包
    发送端发出了两条消息,接收端只读到一个数据包。发送端发出的两条消息的完整信息都被包含在一个数据包中,接收端不知道第一条消息从哪儿结束而第二条消息是从哪儿开始的,很难正确处理数据
  3. TCP拆包
    发送端发送两条消息,接收端一共收到了两个数据包,其中一个数据包只包含了一条消息的一部分,这条消息的后半部分和另一条消息都在另一个数据包中,简而言之就是一条消息被拆分在两个包里面发送了

2. 拆包和粘包的原因

TCP协议以流的方式传输数据,传输的最小单位为一个报文段(segment)。TCP报文的Header中有个Options标识位,其常见的标识MSS(Maximum Segment Size)指的是报文传输的内容的最大长度,这个值通常为 MTU 减去 IP头(20 Byte)和TCP头(20 Byte)的大小,一般为1460 Byte

MTU(Maximum Transmission Unit)是数据链路层每次传输的数据的最大限制,通常是1500 Byte,超过这个大小的数据要分成多个报文段

为提高性能,TCP 发送端会将需要发送的数据发送到缓冲区,等待缓冲区满了或者触发 flush 操作再将缓冲中的数据发送到接收方。同样,接收方也有缓冲区用于接收数据,因此发生TCP粘包、拆包主要有以下原因:

  1. 发送方发送的数据经过 MSS(最大报文长度)大小的 TCP 分段,当TCP 数据量 - TCP头部长度 > MSS的时候将发生拆包
  2. 发送方写入的数据大于 socket 缓冲区大小,将会发生拆包
  3. 发送方写入数据小于 socket 缓冲区大小,网卡将其多次写入的数据一起发送,将会发生粘包
  4. 接收方不及时读取 socket 缓冲区数据,可能发生粘包

3. 解决方案

底层的 TCP 协议本身不关心上层的业务数据,所以无法避免粘包拆包的发生,只能在应用层数据协议上加以控制,常用的方法如下:

  1. 使用带消息头的协议
    消息头存储消息开始标识及消息长度信息,接收端获取消息头的时候解析出消息长度,然后向后读取该长度的内容,位数不够补 0
  2. 设置定长消息
    接收端每次读取既定长度的内容作为一条完整消息,不足的空位补齐
  3. 设置消息边界
    可在报文末尾增加换行符表明一条完整的消息,这样接收端可以根据这个换行符来判断消息是否完整

Netty 框架中对 TCP 粘包拆包有现成的解决工具,只要在其 pipline 中加入对应的解码器就可实现。需注意一旦添加了对应的解码器,发送数据时一定要符合其规范,否则消息可能无法正常读取

  • LineBasedFrameDecoder 基于换行符解决
  • DelimiterBasedFrameDecoder 基于分隔符解决
  • FixedLengthFrameDecoder 指定长度解决

猜你喜欢

转载自blog.csdn.net/weixin_45505313/article/details/106788946