Netty粘包&半包解决方案
短链接
发送数据后断开连接,下次发送时重新建立连接
// Client端 public static void main (String[] args) { for (int i = 0; i < 10; i++) { send(); } } // 发送数据 public void send () { NioEventLoopGroup worker = new NioEventLoopGroup(); try { new Bootstrap().channel(NioSocketChannel.class) .group(worker) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { // channel连接建立好后触发 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { for (int i = 0; i < 10; i++) { ByteBuf buf = ctx.alloc().buffer(16); buf.writeBytes(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}); ctx.writeAndFlush(buf); // 发送完毕断开连接 ctx.channel().close(); } } }); } }).connect("127.0.0.1", 8080).sync() .channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { worker.shutdownGracefully(); } }
定长解码器
在客户端发送的数据长度固定时将服务端的解码器设置为定长解码器
FixedLengthFrameDecoder
可避免粘包和半包的问题// Server端 try { new ServerBootstrap() .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_RCVBUF, 10) .group(new NioEventLoopGroup(1), new NioEventLoopGroup()); .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast("decoder", new FixedLengthFrameDecoder(Demo2Client.LENGTH)); ch.pipeline().addLast("logging", new LoggingHandler(LogLevel.DEBUG)); } }) .bind(8080).sync().channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); }
行解码器
行解码器``就是服务端在解析客户端发送的数据时将固定的字符
\n
作为一个数据包的分隔符进行解析// Server端 try { new ServerBootstrap() .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_RCVBUF, 10) .group(new NioEventLoopGroup(1), new NioEventLoopGroup()); .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast("line", // 参数位一行数据的最大长度 new LineBasedFrameDecoder(1024)); ch.pipeline().addLast("logging", new LoggingHandler(LogLevel.DEBUG)); } }) .bind(8080).sync().channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); }
LTC解码器
源代码案例
参数解释:
- maxFrameLength: 一条数据的最大长度
- lengthFieldOffset: 长度字段的偏移量
- lengthFieldLength: 长度字段所占的字节数
- lengthAdjustment: 在长度字段和数据字段之间插入的附加字段的所占字节数
- initialBytesToStrip: 解码后剥离的字节数
LengthFieldBasedFrameDecoder
的构造函数public LengthFieldBasedFrameDecoder( int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) { ...... }
演示案例
public static void main(String[] args) { EmbeddedChannel channel = new EmbeddedChannel( new LengthFieldBasedFrameDecoder(512, 5, 4, 1, 10), new LoggingHandler(LogLevel.DEBUG) ); ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(); send(buf, "Hello, World"); send(buf, "Hi"); channel.writeInbound(buf); } // 发送方法 private static void send (ByteBuf buf, String content) { byte[] header = "NETTY".getBytes(); byte[] bytes = content.getBytes(); int length = bytes.length; buf.writeBytes(header); buf.writeInt(length); buf.writeByte(1); buf.writeBytes(bytes); }
本作品采用《CC 协议》,转载必须注明作者和本文链接