产生粘包和拆包问题的主要原因是,操作系统在发送TCP数据的时候,底层会有一个缓冲区,例如1024个字节大小,如果一次请求发送的数据量比较小,没达到缓冲区大小,TCP则会将多个请求合并为同一个请求进行发送,这就形成了粘包问题;如果一次请求发送的数据量比较大,超过了缓冲区大小,TCP就会将其拆分为多次发送,这就是拆包,也就是将一个大的包拆分为多个小包进行发送。

常用解决方案

由于底层的TCP无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决。业界的主流协议的解决方案,可以归纳如下:

  1. 消息定长,报文大小固定长度,例如每个报文的长度固定为200字节,如果不够空位补空格;
  2. 包尾添加特殊分隔符,例如每条报文结束都添加回车换行符(\r\n例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分;
  3. 将消息分为消息头和消息体,消息头中包含表示信息的总长度(或者消息体长度)的字段;
  4. 更复杂的自定义应用层协议。

Netty中实现拆包沾包处理

  • FixedLengthFrameDecoder:固定报文长度;
  • LineBasedFrameDecoder与DelimiterBasedFrameDecoder:分隔符处理拆包沾包,LineBasedFrameDecoder:换行符,DelimiterBasedFrameDecoder用户指定符号;
  • LengthFieldBasedFrameDecoder与LengthFieldPrepender:添加报文头来描述报文偏移量以及长度;
  • 实现MessageToByteEncoder和ByteToMessageDecoder自定义处理。