TCP的粘包问题

TCP粘包这似乎是个老生长谈的问题,想不到还是在项目中给我遇到了。

服务器本来每次收取的就是客户端发送的简短的消息,即发送一次收一次,客户端也并没有频繁请求。不过却在有一次的测试中给报错了,看日志是解析数据出错,我想客户端发送的消息还是以前的,怎么到我这里来就解析出错了呢?不可能是掉包呀!再看解析错误的是什么数据,噢!原来是在后来的程序中我们服务器加上了与客户端的保活连接模块,然而我们在解析的代码里面并没有进一步的判断,而这次收到的数据却是既包含了正常请求数据+keepalive数据,于是就报异常了(话说以前我赶时间,这程序写的真渣…)。

和客户端那边的同事再次沟通了下,大致就明白什么原因了,原来他貌似是为了测试把发送keepalive的时间设置得很短(1s),可能在一次正常的发送数据中,几乎是同一时间正常数据和keepalive数据被send到了缓冲区,那么Tcp协议里面机制的原因,默认是将Nagle算法开启,这也是为了作拥塞控制(后面日志可能会笔记到)考虑的。即当缓冲区中的数据到达一定量或者一定时间后才由tcp将其发出,以到达网络中节约资源的目的。这可能就是文章开头所抛异常的原因。

上面是粘包的一种情况,还有另一种情况就是,对于一个socket的包,如发送 AAAAABBBBB两次,由于网络原因第一次又分成两次发送, AAAAAB和BBBB,接收端可能第一次接收到了AAAAAB,那么此时发送端又发送了一个CCCCDDD的数据包过来,那么很有可能,接收端第二次就接收到了BBBBCCCCDDD,即第一次和第二次的包粘在了一起,所以如果程序没有很好的机制处理,是容易出问题的。那么在应用层面在定义数据包的格式的时候就可以考虑包头(数据长度)+数据的方式,接收端每次收到数据先解析包头,然后在解析数据,如果不对,再等待第二次的接收,方可处理。

当然上面说的这些情况只会出现在基于流式数据传输的TCP出现,而以面向报文传输的UDP则是不会出现的,它只有一种情况就是,接收端要么收到数据,要么收不到!