为什么会出现粘包?
TCP是面向字节流的协议,数据是二进制数据,没有任何边界。这些数据可能被切割或者组装成各种数据包,接受端收到这些数据包没有正确还原原有的信息,因此出现粘包现象。
粘包发生场景?
- 如果前后两次TCP发送的数据都远小于MSS,会通过TCP的Nagle算法优化(避免发送小数据包、防止浪费网络IO)进行发送;
- 接收数据端的应用层没有及时读取TCP Recv Buffer 中的数据,也会发生粘包。
如何解决粘包?
出现粘包的根本问题是不确定消息的边界。
- 发送方将每个包都封装层固定的长度,不足的时候进行填充字符;
- 发送方在每个包末尾使用固定的分隔符,例如:FTP协议;
- 将消息分为头部和消息体,头部中保存整个消息的长度,只有读取到足够长度的消息之后才算是读到了一个完整的消息,例如:HTTP协议的Content-Length。
UDP会出现粘包嘛?
- 不会;
- UDP基础数据报文的形式,无论应用层交给UDP多长的报文,UDP都会照样发送,即一次发送一个报文。至于如果数据包太长,需要分片也是交给IP层处理(MTU),保留了报文边界;
- 同时,接收方在接收数据报文的时候,会根据UDP发送端发送了多少此的报文,接收端就在多少次收完,以此来保证每一次都是完整的数据报。
读到这里,可能会有几个问题,下面依次解答。
为什么UDP报文头部会保存16位UDP长度?
同时TCP头部没有长度这个信息,是通过“IP总长度 - IP Header长度 - TCP Header长度 ”计算得到TCP数据长度
同理,UDP也可以通过“IP总长度 - IP Header长度 - UDP Header长度(固定8字节)”得到UDP数据长度,为什么会有冗余的长度字段加在UDP首部中。
- 用于计算校验和;
- UDP底层使用的可以不是IP协议,可以换成其他网络协议。
为什么TCP分段,不仿照UDP直接丢给IP层处理?
- 发生重传的时候只需要重传分段后小份的数据;
- 数据在TCP分段,尽量避免了IP层不需要分片,通过“MTU = Ip Header长度 + TCP Header长度 + MSS”,小于MTU的时候,IP就不需要分片;
IP层有粘包问题嘛?
粘包问题是使用者无法正确区分消息边界导致的一个问题。Ip分片只管运输,按照MTU将数据切片通过不同路由流转最后组合成一个数据包,不在意消息的边界和内容。所以就不存在粘包一说。