Netty粘包&半包解決方案

calong發表於2021-06-04

短連結

傳送資料後斷開連線,下次傳送時重新建立連線

// 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);
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章