TCP粘包拆包

TCP粘包拆包教程

TCP 是一个 “流” 协议,所谓的流,就是没有界限的一个长串的二进制数据。TCP 的底层并不了解上层业务的数据的具体意义,它会根据 TCP 缓冲区的实际情况进行包的划分,所以按照人的思维在业务上传递的数据是一个完整的数据,有可能会被 TCP 拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送。将多个包合并成一个包我们就叫做粘包,把一个包分成多个包我们叫做拆包。

TCP粘包拆包描述

其实 TCP 粘包拆包和我们现实生活中的问题很像,比如你将一个大的物件从 A 地点运送到 B 地点。如果车比较小,你每次只能将物件上面的物件拆分出来,拆成一个一个小的物件,通过车多次运输。而在 TCP 传送数据的过程中,也会存在这样的问题。就是你需要传送的数据有可能比你套接字缓冲区的数据大,一次性传送不过去,这个时候只能将传输的数据拆分,分多次传送到套接字缓冲区了。这个就是我们理解的拆包。还有一种场景就是你要把两个比较小的物件从 A 地点运送到 B 地点。其实一趟就可以将两个物件运送过去,不需要分两次,所以就将两个物件一起运输过去。这个就是我们所谓的粘包。

01 TCP粘包拆包.png

如上图,我们将数据从客户端 Client 传送到服务端 Server,假设有 Data1 数据和 Data2 数据。

  1. 服务器端分两次读取到了客户端发送端数据 Data1 和 Data2,数据都是完整的,没有 TCP 拆包和 TCP 粘包。
  2. 服务端只接受了一次数据,但是将 Data2 和 Data1 这两个数据都获取到了,这种场景称为 TCP 粘包。
  3. 服务端分两次接受到了客户端发来的数据,第一次收到了 Data1 的完整数据和 Data2 的部分数据,第二次收到了 Data2 的剩余的数据。将 Data2 分成两批传输,这就叫 TCP 拆包。当然,我们看到 Data1 和 Data2 的部分数据一起,这边的逻辑是 TCP 粘包。
  4. 如果服务端 TCP 接受数据的缓冲区比较小,那么就将 Data1 和 Data2 都划分成小的数据块传送到服务端,这就会将数据进行多次拆分,这个部分就叫做拆包。

TCP粘包拆包原因

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

TCP粘包拆包解决策略

由于底层的 TCP 无法理解上层的业务数据,所以在底层是无法保证数据不被拆分和重组的,这个问题只能够由上层的应用协议栈设计来解决。业界的主流协议的解决方案如下:

  1. 消息定长,报文长度固定。比如给每个报文的长度规定都是 200 字节,如果不够的话,就以空位来补充。
  2. 在报文的尾部添加特殊分割符,例如每条报文的结构都添加回车换行符或者用特殊的字符作为报文分割符,接收端通过特殊分割符来对数据进行区分。
  3. 将消息分为消息头和消息体,消息头包含信息的总长度(或者消息体的总长度)。
  4. 自定义更加复杂的应用层协议。

TCP粘包拆包总结

本章我们了解了什么是 TCP 粘包和拆包,结合实际生活案例,我们可以很好的理解 TCP 粘包拆包发生的缘由。TCP 拆包就是将一个大的数据报文分批次送到被请求端手里。TCP 粘包就是将多次请求端报文合并成一次送到被请求方手里。