TCP的黏包问题及解决办法
什么是粘包?
TCP本身面向字节流的协议,它本身就没有“包”的概念,所以说粘包也不能说是它的缺陷,这是必然会出现的情况。
传输过程中出现的情况:
情况1:
数据包1和数据包2正常发送过去,没有发送粘包现象。
情况2:
数据包1和数据包2组成一个数据包被发送了过去,出现了粘包现象。
情况3:
数据包2被分成了两块,一块和数据包1粘在一起发送了过去,另一块和数据包3粘在一起发送了过去。
举个例子
比如说我们要发送“abc” ,"edf”
按照第一种情况:
正常发送了过去
接收方知道了对方想发送两个字符,一个是"abc",一个是"def"。
按照第二种情况:
发送过去后则就变成
“abcdef” ,接收端也不知道对方想发送的是什么?
是“abcdef”呐,还是“a” "bcdef"呐,还是“ab” “cdef” 等等(情况有很多)。
按照第三种情况
假如说之后要发送数据包是"hjk"。
则发送过去就变成了
“abcde” “fhjk”。
和上面一样,接收端也不知道发送端发送的具体是什么。
为什么会出现粘包的问题?
对于发送端:
1.因为数据包还包含报头信息,所以为了提高传输效率 ,发送方往往会收集到足够的数据才会把数据一次发送过去。
2.因为协议的原因,有MSS的限制,数据包过长就会分开传输。
对于接收端:
若接收端没有及时从缓冲区取走数据,等到下个数据包到达时,也就会存储到缓冲区,这样和之前的数据包混在一起,也就发生了粘包现象。
本质原因就是接收端并不知道每个包具体的大小,或者是每个包的边界。
UDP为什么没有粘包?
因为UDP不是流式传输,而是面向消息传输的,它每次都只传输一条消息,所以不会出现粘包的情况。
解决办法
1.发生定长数据包。
如果消息的长度都是一样的话,那么接收方只要计算消息的长度,当达到该定长时,就把它划做一个消息。
2.包尾部加上标记。
如在包尾加上\r\n,但是这种有一个问题,假如说消息的内容中有\r\n,则此处将会被当作消息的尾部,所以我们要避免这种情况。
3.在包头中加上包的长度
当对方收到后,按照包的长度进行读取则就可以了。