TCP包问题
拆包和组包
TCP协议能够将要传输的数据拆成若干个数据包,并在保证数据包的传输顺序的前提下发送到远程主机,并组装回原来的样子。
例子如下:
当包1.0.0很大,会发现2.0.0数据包发送被延迟了,如果数据包1.0.0发送失败,会导致整个请求发送失败。
为了避免如上情况发生,我们将1.0.0包拆成更小的包放入发送队列,并在接收时将这些包组装还原。效果如下所示。
如图所示,我们将原来的1.0.0拆分成了1.1.0+1.2.0+1.3.0+1.4.0,使其他的数据包有机会插入到其中得以传输,而不至于等整个请求的数据传输完成。这一改进适用于对传输的时候又一定限制,且需要兼顾公平的场景。另外如果请求某个数据包在传输过程中出现失败,就可以从失败的数据包开始继续传输,这适用于需要断点续传的场景。
断包和粘包
TCP协议会根据包的大小对要传输的数据切分或合并发送(Nagle算法)。因此,同一个TCP数据包中就可能包含我们自定义的多个数据包的内容,可能是小的数据包一次发送,大的数据包切分后分配发送。
如图:缓冲区中是完整的数据包1和只有一半的应用数据包2,TCP连接正在传输的是应用数据包2、3、4、5,一共分装为3个TCP数据包,分3次发送。
应用程序首次从缓冲区读取数据只有包1和一半的包2,不考虑拆分的情况,包2断掉了,被称为断包。
第二次从缓冲区读取时,将读到应用数据包2的后半部分、应用数据包3、4和应用数据包5的一部分,。应用数据包3、4是完整的,但是看上去数据包4粘在数据包3后面,称为粘包。
应用从Socket读取数据时,实际是将数据从操作系统的缓冲区读取到应用程序的缓冲区。因此即使我们使用TCP NODELAY算法禁止TCP应用Nagle算法来合并发送数据包,也不能保证接收方不出现粘包现象。
封包技术可以解决如上问题:给数据加入数据头和数据尾标示,以明确每个数据包的开始和结尾。
数据包结构
- 数据包ID:所属数据包ID。
- 序号:分包的序号。
- 分包大小:此分包的数据长度。
- 数据段:实际的数据内容。