TCP粘包/拆包现象

       TCP作为面向有连接、字节流的传输层协议,消息无边界,应用层的一次读取操作并不能区分拿到的是半个报文、一个报文还是两个报文的组合。假设这么一个场景,客户端连续向服务端发送两个报文Data1和Data2,则可能会发生以下三种情况:

             1、Data1和Data2分开到达了Server端,没有产生粘包或者拆包的情况。
             2、Data1的尾部和Data2的首部连在一起,也就是说合成了一个报文,同时到达Server端,这就是粘包的情况。
             3、由于某种原因Data2被分离成Data2_1和Data2_2,Data1的尾部和Data2_1的首部连在一起,合成一个大的报文,首先到达server端,Data2_2稍后到达,这既发生了粘包,同时也发生了拆包。
      产生拆包的原因:
             1、tcp传送的端mss大小限制;链路层也有MTU大小限制,如果数据包大于MTU时要在IP层进行分片,导致报文分割。
             2、tcp的流量控制和拥塞控制,也可能导致粘包、半包。
             3、应用层设定的缓冲区不足以容纳报文,读取时只读取了一部分。

      产生粘包的原因:
             1、是为了减少网络中小分组的数目,尽可能避免网络拥塞的出现而采用Nagle算法。采用Nagle算法时发送端会先将第一个小分组发送出去,而将后面到达的小分组都缓存起来而不立即发送,直到收到接收端对前一个小分组的ACK确认、或当前字符属于紧急数据,或者积攒到了一定数量的小分组等多种情况才将其组成一个较大的分组发送出去。
             2、发送端需要等缓冲区满才发送出去,造成粘包。
             3、接收方不及时接收缓冲区的包,造成多个包堆积在一起。

      解决的办法:
             1、禁用Nagle算法,这种方法虽然能一定程度上解决TCP粘包,但是并不能完全解决问题,反而可能降低TCP传输效率。而且接收方也是可能造成粘包的原因,这种方法只是发送方有效。所以,这并不是一种理想的方法。
             2、格式化报文:每个报文有固定的格式如尾部包含结束符,但在选择结束符时一定不能在报文内部。
             3、设定报文头:在每个报文前添加一个报文头,头部含有长度、类型等信息。读取时,根据报文头的长度来读取报文,从而判断每个报文的开始和结束。

      实际上也有结合后两者的通讯协议如HTTP,redis等。

      由于UDP不是面向流的,作为无连接的不可靠的传输协议,适合频繁发送较小的数据包。UDP是具有消息边界的,也就是说UDP的发送的每一个数据包都是独立的,故UDP并不存在粘包的问题。

猜你喜欢

转载自blog.csdn.net/i792439187/article/details/65628934