TCP粘包、拆包问题

697 阅读3分钟

什么是粘包、拆包?

假设 Client 向 Server 连续发送了两个数据包,用 packet1 和 packet2 来表示,那么服务端收到的数据可以分为三种情况,现列举如下:

第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象。

第二种情况,接收端只收到一个数据包,由于TCP是不会出现丢包的,所以这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。这种情况由于接收端不知道这两个数据包的界限,所以对于接收端来说很难处理。

第三种情况,这种情况有两种表现形式,如下图。接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。这两种情况如果不加特殊处理,对于接收端同样是不好处理的。

什么情况下会产生粘包、拆包?

  1. 应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包
  2. 进行MSS(最大报文长度)大小的TCP分段,当TCP报文长度-TCP头部长度>MSS的时候将发生拆包
  3. 应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包
  4. 接收方法不及时读取套接字缓冲区数据,这将发生粘包

为什么只有 TCP 有粘包和拆包的问题而UDP没有 ?

UDP 是基于报文发送的,UDP首部采用了 16bit 来指示 UDP 数据报文的长度,因此数据交付到应用层、应用程序就可以根据这些信息将不同的数据报文区分开,从而避免粘包和拆包的问题。

TCP 是基于字节流的,应用层和 TCP 传输层之间的数据交互是大小不等的数据块,但是 TCP 并没有把这些数据块区分界,仅仅是一连串没有结构的字节流;从 TCP 的首部结构就可以看出,在 TCP 的首部没有表示数据长度的字段,基于以上原因, TCP 传输数据时,有粘包或者拆包现象发生的可能。

粘包、拆包解决办法

由于 TCP 本身是面向字节流的,无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决

消息定长:发送端将每个数据包封装为固定长度(可以补 0 填充),这样接收端每次接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。

设置消息边界:服务端从网络流中按消息边界分离出消息内容。在包尾增加回车换行符进行分割,例如 FTP 协议。

将消息分为消息头和消息体:消息头中包含表示消息总长度(或者消息体长度)的字段。

Reference:

www.codesheep.cn