Netty粘包&半包解决方案

Java
325
0
0
2022-04-20

短链接

发送数据后断开连接,下次发送时重新建立连接
// 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解码器

源代码案例
Netty粘包&半包解决方案
参数解释:
  1. maxFrameLength: 一条数据的最大长度
  2. lengthFieldOffset: 长度字段的偏移量
  3. lengthFieldLength: 长度字段所占的字节数
  4. lengthAdjustment: 在长度字段和数据字段之间插入的附加字段的所占字节数
  5. 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);
}