为什么会有这样的问题?
TCP是一个面向字节流的协议,所谓字节流就是没有界限的一串数据。要想从这个混沌的流中获取数据并解析含义必须经过:
分割读取-->解码 这两个基本过程。
其中我们将分割读取后获得的一段一段有含义的数据叫做包。
很显然,从TCP流中读取数据之初,我们并不知道包有多大,界限在哪里,因此我们常常会借助于一个一定大小的缓存区读取进行数据读取。也就是说每次读取缓存获得的数据可能包含一个整包,也可能包含包的一部分数据(这个就是半包)。我们需要将整包分割出来,这就是拆包,需要将不完整的包亲前后拼接起来,这就是粘包。
怎样解决?
定义界限
定长边界
例如FixedLengthFrameDecoder
变长边界,定义分割标志
例如通用的DelimiterBasedFrameDecoder,或者最常见的换行分割LineBasedFrameDecoder
残包缓冲
示例
以换行为分割标志的字符数据LineBasedFrameDecoder+StringDecoder
LineBasedFrameDecoder会遍历ByteBuf(定长缓冲区),如果遇到“\n”或者“\r\n”就认为是一个包界限,进行拆包/粘包操作。如果在定常缓冲区内没有找到这个界限就抛弃之前读到的码流并抛出异常。
StringDecoder的作用是将码流转换成字符串。
LineBasedFrameDecoder+StringDecoder可实现按换行分割的文本解码器,它常常用于TCP的粘包和拆包,但需要注意的是,单个包(每行数据)大小不能超出LineBasedFrameDecoder规定的缓冲区大小。