Netty(三、拆包粘包)
拆包粘包
TCP在向目标发送数据时,会通过缓冲区发送,当数据达不到缓冲区的大小时会发生粘包。当数据超过缓冲区大小时,则会分成多个部分,就是拆包。
例如上图:我们实际发送和希望接收到这样的数据,但是实际却有可能接收成这样。
程序中,我们这样向服务器发送三条数据,而服务端只接收到一条:
Netty解决方案
DelimiterBasedFrameDecoder
通过自定义分隔符实现拆包粘包
服务端:
//5.配发事件处理器流水线 .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { String delimiter = "_$"; //定义拆包粘包的解码器 socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer(delimiter.getBytes()))); //解码器 socketChannel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8)); //编码器 socketChannel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8)); //自定义的编码器,输出信息后面加上分隔符 socketChannel.pipeline().addLast(new DelimiterBasedFrameEncoder(delimiter)); //自定义事件处理器 socketChannel.pipeline().addLast(new NettyServerHandler()); } });
自定义:DelimiterBasedFrameEncoder
package com.wk.test.nettyTest; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; @ChannelHandler.Sharable public class DelimiterBasedFrameEncoder extends MessageToByteEncoder<String> { private String delimiter; public DelimiterBasedFrameEncoder(String delimiter){ this.delimiter = delimiter; } @Override protected void encode(ChannelHandlerContext channelHandlerContext, String message, ByteBuf byteBuf) throws Exception { channelHandlerContext.writeAndFlush(Unpooled.wrappedBuffer((message + delimiter).getBytes())); } }
客户端:
.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { String delimiter = "_$"; socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer(delimiter.getBytes()))); //解码器 socketChannel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8)); //编码器 socketChannel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8)); socketChannel.pipeline().addLast(new DelimiterBasedFrameEncoder(delimiter)); socketChannel.pipeline().addLast(new NettyClientHandler()); } });
这里只列举这种方法,还有其他方法如固定解码长度等,这里不一一列举。