万有斥力 发表于 2025-2-12 13:07:05

Netty高级利用与源码详解

粘包与半包

粘包现象

粘包的问题出现是因为不知道一个用户消息的边界在哪,如果知道了边界在哪,吸收方就可以通过边界来划分出有效的用户消息。
服务端代码
public class HelloWorldServer {
    static final Logger log = LoggerFactory.getLogger(HelloWorldServer.class);
    void start() {
      NioEventLoopGroup boss = new NioEventLoopGroup(1);
      NioEventLoopGroup worker = new NioEventLoopGroup();
      try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.group(boss, worker);
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                  ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                  ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            log.debug("connected {}", ctx.channel());
                            super.channelActive(ctx);
                        }

                        @Override
                        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                            log.debug("disconnect {}", ctx.channel());
                            super.channelInactive(ctx);
                        }
                  });
                }
            });
            ChannelFuture channelFuture = serverBootstrap.bind(8080);
            log.debug("{} binding...", channelFuture.channel());
            channelFuture.sync();
            log.debug("{} bound...", channelFuture.channel());
            channelFuture.channel().closeFuture().sync();
      } catch (InterruptedException e) {
            log.error("server error", e);
      } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
            log.debug("stoped");
      }
    }

    public static void main(String[] args) {
      new HelloWorldServer().start();
    }
}客户端代码盼望发送 10 个消息,每个消息是 16 字节
public class HelloWorldClient {
    static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);
    public static void main(String[] args) {
      NioEventLoopGroup worker = new NioEventLoopGroup();
      try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.group(worker);
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                  log.debug("connetted...");
                  ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        //会在连接channel建立成功后,会触发Active事件
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            log.debug("sending...");
                            Random r = new Random();
                            char c = 'a';
                            for (int i = 0; i < 10; i++) {
                              ByteBuf buffer = ctx.alloc().buffer();
                              buffer.writeBytes(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
                              ctx.writeAndFlush(buffer);
                            }
                        }
                  });
                }
            });
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();
            channelFuture.channel().closeFuture().sync();

      } catch (InterruptedException e) {
            log.error("client error", e);
      } finally {
            worker.shutdownGracefully();
      }
    }
}服务器端的某次输出,可以看到一次就吸收了 160 个字节,而期望的是一次16字节,分 10 次吸收。这就出现了粘包现象
08:24:46 c.i.n.HelloWorldServer - binding...
08:24:46 c.i.n.HelloWorldServer - bound...
08:24:55 i.n.h.l.LoggingHandler - REGISTERED
08:24:55 i.n.h.l.LoggingHandler - ACTIVE
08:24:55 c.i.n.HelloWorldServer - connected
08:24:55 i.n.h.l.LoggingHandler - READ: 160B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|00000010| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|00000020| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|00000030| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|00000040| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|00000050| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|00000060| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|00000070| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|00000080| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|00000090| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
+--------+-------------------------------------------------+----------------+
08:24:55 i.n.h.l.LoggingHandler - READ COMPLETE半包现象

半包是指 吸收端只收到了部门数据,而非完备的数据的情况
客户端代码盼望发送 1 个消息,这个消息是 160 字节,代码改为
ByteBuf buffer = ctx.alloc().buffer();
for (int i = 0; i < 10; i++) {
    buffer.writeBytes(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
}
ctx.writeAndFlush(buffer);为现象显着,服务端修改一下吸收缓冲区,其它代码不变
serverBootstrap.option(ChannelOption.SO_RCVBUF, 10);服务器端的某次输出,可以看到吸收的消息被分为两节,如 第一次 20 字节,第二次 140 字节
08:43:49 c.i.n.HelloWorldServer - binding...
08:43:49 c.i.n.HelloWorldServer - bound...
08:44:23 i.n.h.l.LoggingHandler - REGISTERED
08:44:23 i.n.h.l.LoggingHandler - ACTIVE
08:44:23 c.i.n.HelloWorldServer - connected
08:44:24 i.n.h.l.LoggingHandler - READ: 20B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|00000010| 00 01 02 03                                     |....            |
+--------+-------------------------------------------------+----------------+
08:44:24 i.n.h.l.LoggingHandler - READ COMPLETE
08:44:24 i.n.h.l.LoggingHandler - READ: 140B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
|00000010| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
|00000020| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
|00000030| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
|00000040| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
|00000050| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
|00000060| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
|00000070| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
|00000080| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f             |............    |
+--------+-------------------------------------------------+----------------+
08:44:24 i.n.h.l.LoggingHandler - READ COMPLETE留意:serverBootstrap.option(ChannelOption.SO_RCVBUF, 10) 影响的底层吸收缓冲区(即滑动窗口)大小,仅决定了 netty 读取的最小单位,netty 实际每次读取的一般是它的整数倍
现象分析

这里出现的粘包半包问题,并非是JavaNIO或Netty的问题,本质是TCP是流失协议,消息无边界。
粘包:

[*]现象,发送 abc def,吸收 abcdef
[*]原因

[*]应用层:吸收方 ByteBuf 设置太大(Netty 默认 1024)
[*]滑动窗口:假设发送方 256 bytes 表现一个完备报文,但由于吸收方处置惩罚不及时且窗口大小足够大,这 256 bytes 字节就会缓冲在吸收方的滑动窗口中,当滑动窗口中缓冲了多个报文就会粘包
[*]Nagle 算法:会造成粘包

半包

[*]现象,发送 abcdef,吸收 abc def
[*]原因

[*]应用层:吸收方 ByteBuf 小于实际发送数据量
[*]滑动窗口:假设吸收方的窗口只剩了 128 bytes,发送方的报文大小是 256 bytes,这时放不下了,只能先发送前 128 bytes,等待 ack 后才能发送剩余部门,这就造成了半包
[*]MSS 限制:当发送的数据凌驾 MSS 限制后,会将数据切分发送,就会造成半包

解决方案

接下来看下Netty如何解决以上问题的:

[*]短链接,发一个包建立一次连接,这样连接建立到连接断开之间就是消息的边界,缺点效率太低
[*]每一条消息接纳固定长度,缺点浪费空间
[*]每一条消息接纳分隔符,例如 \n,缺点需要转义
[*]每一条消息分为 head 和 body,head 中包罗 body 的长度
方法1:短链接(极不保举)

以解决粘包为例
public class HelloWorldClient {
    static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);

    public static void main(String[] args) {
      // 分 10 次发送
      for (int i = 0; i < 10; i++) {
            send();
      }
    }

    private static void send() {
      NioEventLoopGroup worker = new NioEventLoopGroup();
      try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.group(worker);
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                  log.debug("conneted...");
                  ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                  ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            log.debug("sending...");
                            ByteBuf buffer = ctx.alloc().buffer();
                            buffer.writeBytes(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
                            ctx.writeAndFlush(buffer);
                            // 发完即关
                            ctx.close();
                        }
                  });
                }
            });
            ChannelFuture channelFuture = bootstrap.connect("localhost", 8080).sync();
            channelFuture.channel().closeFuture().sync();

      } catch (InterruptedException e) {
            log.error("client error", e);
      } finally {
            worker.shutdownGracefully();
      }
    }
}输出,略
半包用这种办法照旧欠好解决,因为吸收方的缓冲区大小是有限的
方法2:固定长度

让所有数据包长度固定(假设长度为 8 字节),服务器端参加
ch.pipeline().addLast(new FixedLengthFrameDecoder(8));客户端测试代码,留意, 接纳这种方法后,客户端什么时候 flush 都可以
public class HelloWorldClient {
    static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);

    public static void main(String[] args) {
      NioEventLoopGroup worker = new NioEventLoopGroup();
      try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.group(worker);
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                  log.debug("connetted...");
                  ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                  ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            log.debug("sending...");
                            // 发送内容随机的数据包
                            Random r = new Random();
                            char c = 'a';
                            ByteBuf buffer = ctx.alloc().buffer();
                            for (int i = 0; i < 10; i++) {
                              byte[] bytes = new byte;
                              for (int j = 0; j < r.nextInt(8); j++) {
                                    bytes = (byte) c;
                              }
                              c++;
                              buffer.writeBytes(bytes);
                            }
                            ctx.writeAndFlush(buffer);
                        }
                  });
                }
            });
            ChannelFuture channelFuture = bootstrap.connect("192.168.0.103", 9090).sync();
            channelFuture.channel().closeFuture().sync();

      } catch (InterruptedException e) {
            log.error("client error", e);
      } finally {
            worker.shutdownGracefully();
      }
    }
}客户端输出
12:07:00 c.i.n.HelloWorldClient - connetted...
12:07:00 i.n.h.l.LoggingHandler - REGISTERED
12:07:00 i.n.h.l.LoggingHandler - CONNECT: /192.168.0.103:9090
12:07:00 i.n.h.l.LoggingHandler - ACTIVE
12:07:00 c.i.n.HelloWorldClient - sending...
12:07:00 i.n.h.l.LoggingHandler - WRITE: 80B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 61 61 61 61 00 00 00 00 62 00 00 00 00 00 00 00 |aaaa....b.......|
|00000010| 63 63 00 00 00 00 00 00 64 00 00 00 00 00 00 00 |cc......d.......|
|00000020| 00 00 00 00 00 00 00 00 66 66 66 66 00 00 00 00 |........ffff....|
|00000030| 67 67 67 00 00 00 00 00 68 00 00 00 00 00 00 00 |ggg.....h.......|
|00000040| 69 69 69 69 69 00 00 00 6a 6a 6a 6a 00 00 00 00 |iiiii...jjjj....|
+--------+-------------------------------------------------+----------------+
12:07:00 i.n.h.l.LoggingHandler - FLUSH服务端输出
12:06:51 c.i.n.HelloWorldServer - binding...
12:06:51 c.i.n.HelloWorldServer - bound...
12:07:00 i.n.h.l.LoggingHandler - REGISTERED
12:07:00 i.n.h.l.LoggingHandler - ACTIVE
12:07:00 c.i.n.HelloWorldServer - connected
12:07:00 i.n.h.l.LoggingHandler - READ: 8B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 61 61 61 61 00 00 00 00                         |aaaa....      |
+--------+-------------------------------------------------+----------------+
12:07:00 i.n.h.l.LoggingHandler - READ: 8B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 62 00 00 00 00 00 00 00                         |b.......      |
+--------+-------------------------------------------------+----------------+
12:07:00 i.n.h.l.LoggingHandler - READ: 8B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 63 63 00 00 00 00 00 00                         |cc......      |
+--------+-------------------------------------------------+----------------+
12:07:00 i.n.h.l.LoggingHandler - READ: 8B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 64 00 00 00 00 00 00 00                         |d.......      |
+--------+-------------------------------------------------+----------------+
12:07:00 i.n.h.l.LoggingHandler - READ: 8B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 00 00 00 00 00 00                         |........      |
+--------+-------------------------------------------------+----------------+
12:07:00 i.n.h.l.LoggingHandler - READ: 8B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 66 66 66 66 00 00 00 00                         |ffff....      |
+--------+-------------------------------------------------+----------------+
12:07:00 i.n.h.l.LoggingHandler - READ: 8B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 67 67 67 00 00 00 00 00                         |ggg.....      |
+--------+-------------------------------------------------+----------------+
12:07:00 i.n.h.l.LoggingHandler - READ: 8B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 68 00 00 00 00 00 00 00                         |h.......      |
+--------+-------------------------------------------------+----------------+
12:07:00 i.n.h.l.LoggingHandler - READ: 8B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 69 69 69 69 69 00 00 00                         |iiiii...      |
+--------+-------------------------------------------------+----------------+
12:07:00 i.n.h.l.LoggingHandler - READ: 8B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 6a 6a 6a 6a 00 00 00 00                         |jjjj....      |
+--------+-------------------------------------------------+----------------+
12:07:00 i.n.h.l.LoggingHandler - READ COMPLETE缺点是,数据包的大小欠好把握

[*]长度定的太大,浪费
[*]长度定的太小,对某些数据包又显得不够
方法3:固定分隔符

服务端参加,默认以 \n 或 \r\n 作为分隔符,如果超出指定长度仍未出现分隔符,则抛出异常
ch.pipeline().addLast(new LineBasedFrameDecoder(1024));客户端在每条消息之后,参加 \n 分隔符
public class HelloWorldClient {
    static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);

    public static void main(String[] args) {
      NioEventLoopGroup worker = new NioEventLoopGroup();
      try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.group(worker);
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                  log.debug("connetted...");
                  ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                  ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            log.debug("sending...");
                            Random r = new Random();
                            char c = 'a';
                            ByteBuf buffer = ctx.alloc().buffer();
                            for (int i = 0; i < 10; i++) {
                              for (int j = 1; j <= r.nextInt(16)+1; j++) {
                                    buffer.writeByte((byte) c);
                              }
                              buffer.writeByte(10);
                              c++;
                            }
                            ctx.writeAndFlush(buffer);
                        }
                  });
                }
            });
            ChannelFuture channelFuture = bootstrap.connect("192.168.0.103", 9090).sync();
            channelFuture.channel().closeFuture().sync();

      } catch (InterruptedException e) {
            log.error("client error", e);
      } finally {
            worker.shutdownGracefully();
      }
    }
}增长设置类和设置文件
14:08:18 c.i.n.HelloWorldClient - connetted...
14:08:18 i.n.h.l.LoggingHandler - REGISTERED
14:08:18 i.n.h.l.LoggingHandler - CONNECT: /192.168.0.103:9090
14:08:18 i.n.h.l.LoggingHandler - ACTIVE
14:08:18 c.i.n.HelloWorldClient - sending...
14:08:18 i.n.h.l.LoggingHandler - WRITE: 60B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 61 0a 62 62 62 0a 63 63 63 0a 64 64 0a 65 65 65 |a.bbb.ccc.dd.eee|
|00000010| 65 65 65 65 65 65 65 0a 66 66 0a 67 67 67 67 67 |eeeeeee.ff.ggggg|
|00000020| 67 67 0a 68 68 68 68 0a 69 69 69 69 69 69 69 0a |gg.hhhh.iiiiiii.|
|00000030| 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a 0a             |jjjjjjjjjjj.    |
+--------+-------------------------------------------------+----------------+
14:08:18 i.n.h.l.LoggingHandler - FLUSH设置文件
14:08:18 c.i.n.HelloWorldServer - connected
14:08:18 i.n.h.l.LoggingHandler - READ: 1B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 61                                              |a               |
+--------+-------------------------------------------------+----------------+
14:08:18 i.n.h.l.LoggingHandler - READ: 3B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 62 62 62                                        |bbb             |
+--------+-------------------------------------------------+----------------+
14:08:18 i.n.h.l.LoggingHandler - READ: 3B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 63 63 63                                        |ccc             |
+--------+-------------------------------------------------+----------------+
14:08:18 i.n.h.l.LoggingHandler - READ: 2B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 64 64                                           |dd            |
+--------+-------------------------------------------------+----------------+
14:08:18 i.n.h.l.LoggingHandler - READ: 10B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 65 65 65 65 65 65 65 65 65 65                   |eeeeeeeeee      |
+--------+-------------------------------------------------+----------------+
14:08:18 i.n.h.l.LoggingHandler - READ: 2B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 66 66                                           |ff            |
+--------+-------------------------------------------------+----------------+
14:08:18 i.n.h.l.LoggingHandler - READ: 7B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 67 67 67 67 67 67 67                            |ggggggg         |
+--------+-------------------------------------------------+----------------+
14:08:18 i.n.h.l.LoggingHandler - READ: 4B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 68 68 68 68                                     |hhhh            |
+--------+-------------------------------------------------+----------------+
14:08:18 i.n.h.l.LoggingHandler - READ: 7B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 69 69 69 69 69 69 69                            |iiiiiii         |
+--------+-------------------------------------------------+----------------+
14:08:18 i.n.h.l.LoggingHandler - READ: 11B
         +-------------------------------------------------+
         |0123456789abcdef |
+--------+-------------------------------------------------+----------------+
|00000000| 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a                |jjjjjjjjjjj   |
+--------+-------------------------------------------------+----------------+
14:08:18 i.n.h.l.LoggingHandler - READ COMPLETE修改编解码器
/** * 必须和 LengthFieldBasedFrameDecoder 一起利用,确保接到的 ByteBuf 消息是完备的 */public class MessageCodecSharable extends MessageToMessageCodec {    @Override    public void encode(ChannelHandlerContext ctx, Message msg, List outList) throws Exception {      ByteBuf out = ctx.alloc().buffer();      // 1. 4 字节的魔数      out.writeBytes(new byte[]{1, 2, 3, 4});      // 2. 1 字节的版本,      out.writeByte(1);      // 3. 1 字节的序列化方式 jdk 0 , json 1      out.writeByte(Config.getSerializerAlgorithm().ordinal());      // 4. 1 字节的指令类型      out.writeByte(msg.getMessageType());      // 5. 4 个字节      out.writeInt(msg.getSequenceId());      // 无意义,对齐填充      out.writeByte(0xff);      // 6. 获取内容的字节数组      byte[] bytes = Config.getSerializerAlgorithm().serialize(msg);      // 7. 长度      out.writeInt(bytes.length);      // 8. 写入内容      out.writeBytes(bytes);      outList.add(out);    }    @Override    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {      int magicNum = in.readInt();      byte version = in.readByte();      byte serializerAlgorithm = in.readByte(); // 0 或 1      byte messageType = in.readByte(); // 0,1,2...      int sequenceId = in.readInt();      in.readByte();      int length = in.readInt();      byte[] bytes = new byte;      in.readBytes(bytes, 0, length);      // 找到反序列化算法      Serializer.Algorithm algorithm = Serializer.Algorithm.values();      // 确定具体消息类型      Class, Object> options = options0();    synchronized (options) {      setChannelOptions(channel, options, logger);    }    final Map, Object>[] currentChildOptions;    final Entry
页: [1]
查看完整版本: Netty高级利用与源码详解