TCP通信中的“沾包”现象

Posted “逛丢一只鞋”

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP通信中的“沾包”现象相关的知识,希望对你有一定的参考价值。

TCP与UDP通信的特点

关于对这两者的比较,网上一搜一大片,讲得也比较清楚。

TCP通信就像打电话,双方通信之前需要建立连接、双方就位后方可开始会话;

而UDP通信就像发短信,一方给另一方发送数据前,并不需要对方就位。
在这里插入图片描述
在这里插入图片描述

上面两幅图显示了TCP与UDP通信过程建立的区别。

除了它们通信过程建立的不同之外,两者还有以下区别:

TCP通信特点

1)可靠性;

通信双方均就位,一方发送数据,另一方收到后会做出回应,如果超时未发送成功,会自动重发,数据不会丢失。

2)顺序性;

既然数据是按顺序走在建立的一条隧道中,那么数据遵循“先走先达到”的规则,并且隧道中的数据以 “流” 的形式传输,发送方发送的前后两次数据之间没有边界,需要接收方自己根据事先规定好的“协议”去判断数据边界。

3)高损耗。

“高损耗”包括机器性能损耗高、宽带流量损耗高。因为通信双方时刻需要维持着连接的存在,这必然会损耗通信双方主机性能,要想维持隧道的通畅,通信双方必须不断地发送检测包和应答包,同时,它还支持数据重发等数据纠错功能,这些都将导致网络流量的增加。

UDP通信特点

1)不可靠性;

既然无连接,发送方只管发送数据,而不管对方是否能够正确地接收到数据,更不负责数据超时重发等功能。

2)无序性;

数据以“数据报”的形式发送,可以把“数据报”看成是一个 “包”。如果把TCP传输数据比如成“河里的流水”,那么UDP传输数据就是‘邮局寄信’。发送方先发送的数据可能后到达,后发送的数据可能先到达,这个跟短消息类似。

3)低损耗。

“低损耗”包括机器性能损耗低、宽带流量损耗低。UDP通信不需要维持一个连接的存在,所以它不需要消耗额外的机器性能。同时它也没有像TCP通信那样为了保持隧道的通畅,而必须不停地发送检测包和应答包,更不会进行一些数据检测纠错、重发等行为。

这次我们只讨论TCP通信。

TCP通信中的“沾包”现象

上面提到过,TCP通信中,数据是以“流”的形式传输的。前一次发送的数据和后一次发送的数据之间并没有明显的界限,这就会出现一个问题:当你收到一部分数据时,你无法判断接收到的数据是否是完整的?
在这里插入图片描述

如上图,发送方发送三次数据,而接收方可能一共分四次接收。并且每次接收到的数据量不确定(虽然每次收到的数据不确定,但是将四次接收到的数据拼接起来,与发送时的一致)。

这样一来,当我们每次收到一份数据时,我们无法轻易判断(几乎不能)收到的数据是否完整(是否可以正确地被处理)。

以上现象我们称之为 “沾包”。TCP通信过程中,要想解决“沾包”问题,我们必须人工采取一些措施,比如在发送数据时遵循一些“规则”,在接收到数据时,再按照相同的“规则”去解析数据,最终得到一份完整的数据,并进行正确的处理。没错,这里说的“规则”便是我们通常听到的“协议”

关于协议,讲到的地方也很多。简单的说,协议就是一种“数据结构”,合作双方必须同时按照相同的数据结构发送/接收数据,比如传输层的TCP/UDP协议,又比如应用层的HTTP/FTP等协议。B/S结构系统使用到的协议见下图:
在这里插入图片描述

在TCP通信中,在发送和接收数据的时候,如果我们遵循事先定义的一种“协议”(属于一种应用层协议)。

比如,在发送数据时,按照 “数据头(4Byte)+内容长度(4Byte)+内容正文(NByte)+附加信息(8Byte)” 这种形式去“格式化”需要发送的数据;同理,在接收到数据后,按照这种形式去“反格式化”数据,这样我们便可以判断数据边界,轻松得到一条完整数据。

自定义应用层协议

是的。我们自己完全可以定义一个类似HTTP这样的应用层协议,只要你能力足够强,系统足够大。今天在这里,我只举个简单的例子:

假设一个TCP通信系统中,客户端连接上服务器后,客户端向服务器发送一个字符串,并发送一个字符串转换指令(比如大小写转换、除去特殊字符等指令),服务器接收到数据后,按照对应的指令,将字符串转换后发送回给客户端。那么这里的应用层协议可以这样设计:

序号指令值(byte)说明
10x01将字符串中小写字符转换成大写
20x02将字符串中大写字符转换成小写
30x03去掉字符串中的百分号(%)字符
40x04将字符串中的百分号(%)替换为空格

如上表所示,假设一共有四种字符串转换请求,那么我们可以按下面图设计应用层协议的数据结构:
在这里插入图片描述
如上图所示,开头一个字节代表字符串转换指令类型,后续四个字节存放一个Int32的整型数据,表示字符串的长度(字符串采用Unicode编码),最后N个字节表示字符串内容。数据发送方必须按照此协议格式发送数据,数据接收方必须按照此协议格式接收数据。

发送数据时按照协议格式化数据很简单,但是,接收数据后,按照协议去解析数据该怎样呢?

事实上,这个相对来讲稍微复杂一点。

我们可以将每次接收到的数据(字节流)写入一个缓冲区,然后判断缓冲区中是否存在一条完整的数据,如果存在,则处理这条完整的数据;否则,继续接收数据,将接收到的数据再次写入缓冲区…以此循环。
在这里插入图片描述

以上是关于TCP通信中的“沾包”现象的主要内容,如果未能解决你的问题,请参考以下文章

TCP流有啥特点?沾包问题又是啥?如何解决粘包问题?

机器人控制tcp通信参数调优

TCP沾包问题

socket模块和粘包现象

结合RPC框架通信谈 netty如何解决TCP粘包问题

关于TCP/IP协议