粘包半包原因
因为 TCP 是面向连接的传输协议,TCP 传输的数据是以流的形式,而流数据是没有明确的开始结尾边界,所以 TCP 也没办法判断哪一段流属于一个消息。
粘包的主观原因:
- 发送方每次写入数据 < 套接字(Socket)缓冲区大小;
- 接收方读取套接字(Socket)缓冲区数据不够及时。
半包的主观原因:
- 发送方每次写入数据 > 套接字(Socket)缓冲区大小;
- 发送的数据大于协议的 MTU (Maximum Transmission Unit,最大传输单元),因此必须拆包。
一些客观原因:
1.服务器处理间隔超过客户端的发送间隔,最常见。
2.客户端几乎同一时间发送了数据过来,本质上也是服务器还没来的及处理,就又来新的数据。
3.绝对时间相同发送数据,多见于分布式。
本质上是ByteBuf 来不及处理,把ByteBuf比喻成嘴,就像一张嘴吃着白菜还没来得及咽下去又来了一根黄瓜,白菜黄瓜一起在嘴里吃就是粘包。嘴吧放不下了,只能咬下半根黄瓜,那就造成剩下的半根黄瓜不完整(半包)。所以粘包半包一方面取决于嘴(Bytebuf)的容量,能否在服务器响应时间内,处理(吃)足够多的(食物)数据。但又要考虑一旦没食物(数据),空有一张大嘴在不断咀嚼会不会过于浪费的问题。
目前我个人总结的处理办法有6种:
1.短链接
发送完毕,立即断开,每次都要新建连接。
适用于不频繁使用的数据发送,但是使用短连接后就不能发挥出NIO的优势。
2.FixedLengthFrameDecoder
填补空白法,但是要确定最大长度,比较耗费空间。
适用于每次传输的数据长度差异较小的情况,但只是减少浪费而已。
3.LineBasedFrameDecoder
根据换行符切分,接收到字符后要每一个字节去查分隔符,效率太低。
适用于传输数据量较短的情况,数据越短寻找数据的时间就短。
4.DelimiterBasedFrameDecoder
根据分隔符切分...,和上一条没太大区别,只是可以自定义分隔符。
5.LengthFieldBaseFrameDecoder
长度域,发送包之前加上一个长度字段、一个类型字段。每次只获取固定长度固定类型的数据,其他的都不会被处理。最常用的处理器。但如果数据量极小的情况下,会浪费资源。
6.客户端线程睡眠
使客户端在发送前睡眠一段时间,时间需要大于服务器的处理时间。
造成效率降低,但是在数据实时性要求不高,而且只在固定时间大量通信的情况下成本最低。