粘包拆包现象
TCP是流协议,传输数据时无边界保护,它会根据缓冲区的实际情况对单个数据包进行拆分或者将多个数据包组合起来发送。下图中就出现了粘包和拆包现象:
Netty中的解决方案
Netty通过解码器来解决粘包拆包的
FixedLengthFrameDecoder(消息定长)
比如解决上面截图中的问题,可以设置如下:
// 添加解码器,设置消息大小为25个字节(25等于"大家好,我叫heoller"的字节长度) // 如果发送的消息字节长度小于设置的字节数,可以通过补空格使其达到设置长度 ch.pipeline().addLast(new FixedLengthFrameDecoder(25));
// FixedLengthFrameDecoder原理 // ByteBuf的可读长度只有达到设置值时才会去读取数据,且只读取固定长度的数据。 protected Object decode( @SuppressWarnings("UnusedParameters") ChannelHandlerContext ctx, ByteBuf in) throws Exception { if (in.readableBytes() < frameLength) { return null; } else { return in.readRetainedSlice(frameLength); } }
LineBasedFrameDecoder(回车换行)
解码数据时会根据\r\n
或\n
截取
DelimiterBasedFrameDecoder(指定分隔符)
解码数据时会根据指定的分隔符截取
自定义解码器
实现发送数据时设置指定长度,解码时也会先读取数据长度再读取数据。
定义编码器
public class CustomEncoder extends MessageToByteEncoder<String> { @Override protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception { out.writeInt(msg.getBytes().length); out.writeBytes(msg.getBytes()); } }
定义解码器
public class MessWithLenDecoder extends ByteToMessageDecoder { private int messLen = 0; @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { final int integerBytes = 4; int readableLen = in.readableBytes(); // 可读长度小于4时,不进行读取 if (0 == messLen && readableLen < integerBytes) { return; } if (0 == messLen) { messLen = in.readInt(); } readableLen = in.readableBytes(); // 说明消息没有发送完 if (readableLen < messLen) { return; } out.add(in.readBytes(messLen)); messLen = 0; } }