什么是TCP拆包粘包
假设客户端发送了2条消息M1,M2。可能会出现以下几种情况。
1、服务端正常接收到M1,M2这两条消息。
2、服务端一次接收到了2个数据包,M1和M2粘合在一起,这时候就被称为TCP粘包。
3、服务端分两次读取到了两个数据包,第一次读取到M1包整包和M2包部分内容M2_1,第二次读取到了M2剩余的内容M2_2,这时候被称为TCP拆包。
4、服务端分两次读取到了两个数据包,第一次读取到M1包的部分内容M1_1,第二次读取到了M1剩余的内容M1_2和M2整包,这时候也是发生了TCP拆包。
5、当服务端TCP接收滑窗非常小,或者M1、M2数据包比较大时,服务端需要将M1,M2进行多次拆包才能接收完全,这时候也是发生了TCP拆包。
同时,我们需要明确的是,其实在TCP层面上它是没有拆包粘包概念的。因为TCP只是负责传输上层协议所提供的数据,它本身并不知道这些数据的关系,只是像流一样将这些数据传输过去。因此,这个拆包粘包概念更准确来说是相对于应用层数据划分的。
拆包粘包产生原因
粘包可能的产生原因
1、原本没有粘包进行TCP传输的数据包,在read操作调用不及时去读取缓冲区数据的情况下,很可能会发生粘包;
2、应用程序写入数据大小小于缓冲区大小,网卡会将应用多次写入的数据发送到网络上,这将会发生粘包。
拆包可能的产生原因
1、应用程序写入的数据比缓冲区还大时,数据不能一次性发送,将发生拆包。
2、TCP进行分段时数据大小大于MSS大小的时候将发生拆包。
3、以太网帧的payload数据大于MTU大小时发生拆包。
注:MTU和MSS的概念如下。
MTU:代表TCP/IP协议传输数据报时的最大传输单元。以太网协议规定最大帧长度是1500Byte(不包括报文头部)。
MSS: MSS是TCP专属概念,TCP在三次握手建立连接过程中,会在SYN报文中使用MSS(Maximum Segment Size)选项功能,协商交互双方能够接收的最大段长MSS值。其标识TCP能够承载的最大的应用数据段长度。
MSS=MTU-20字节TCP报头-20字节IP报头。在IPv4网络中,一般情况下,MSS=1460=1500-20-20。 在IPv6网络中,一般情况下,MSS=1440=1500-20-40。
常见解决策略
1、消息定长。例如每个报文的大小固定为200字节,不够则空格补齐;
2、在包尾增加回车换行符进行分割,如FTP协议;
3、将消息分成消息头和消息体,消息头中包含表示消息(体)总长度的字段。如下示例,length字段代表本条消息的长度大小。
+————+—————————+
| Length | Actual Content |
| 0x000C | "HELLO, WORLD" |
+————+—————+———-+
Netty常见解决类
1、LineBasedFrameDecoder:基于行来进行消息粘包拆包处理的。
2、DelimiterBaseFrameDecoder:基于分隔符做结束标志进行粘包拆包处理的,对于使用"\n","\r\n"做分隔符内部会调用LineBasedFrameDecoder来做统一的行分割。
3、FixedLengthFrameDecoder:基于固定长度消息进行粘包拆包处理的。
4、LengthFieldBasedFrameDecoder:基于消息头指定消息长度进行粘包拆包处理的。