ToB企服应用市场:ToB评测及商务社交产业平台

标题: Netty高级利用与源码详解 [打印本页]

作者: 万有斥力    时间: 2025-2-12 13:07
标题: Netty高级利用与源码详解
粘包与半包

粘包现象

粘包的问题出现是因为不知道一个用户消息的边界在哪,如果知道了边界在哪,吸收方就可以通过边界来划分出有效的用户消息。
服务端代码
  1. public class HelloWorldServer {
  2.     static final Logger log = LoggerFactory.getLogger(HelloWorldServer.class);
  3.     void start() {
  4.         NioEventLoopGroup boss = new NioEventLoopGroup(1);
  5.         NioEventLoopGroup worker = new NioEventLoopGroup();
  6.         try {
  7.             ServerBootstrap serverBootstrap = new ServerBootstrap();
  8.             serverBootstrap.channel(NioServerSocketChannel.class);
  9.             serverBootstrap.group(boss, worker);
  10.             serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
  11.                 @Override
  12.                 protected void initChannel(SocketChannel ch) throws Exception {
  13.                     ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
  14.                     ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
  15.                         @Override
  16.                         public void channelActive(ChannelHandlerContext ctx) throws Exception {
  17.                             log.debug("connected {}", ctx.channel());
  18.                             super.channelActive(ctx);
  19.                         }
  20.                         @Override
  21.                         public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  22.                             log.debug("disconnect {}", ctx.channel());
  23.                             super.channelInactive(ctx);
  24.                         }
  25.                     });
  26.                 }
  27.             });
  28.             ChannelFuture channelFuture = serverBootstrap.bind(8080);
  29.             log.debug("{} binding...", channelFuture.channel());
  30.             channelFuture.sync();
  31.             log.debug("{} bound...", channelFuture.channel());
  32.             channelFuture.channel().closeFuture().sync();
  33.         } catch (InterruptedException e) {
  34.             log.error("server error", e);
  35.         } finally {
  36.             boss.shutdownGracefully();
  37.             worker.shutdownGracefully();
  38.             log.debug("stoped");
  39.         }
  40.     }
  41.     public static void main(String[] args) {
  42.         new HelloWorldServer().start();
  43.     }
  44. }
复制代码
客户端代码盼望发送 10 个消息,每个消息是 16 字节
  1. public class HelloWorldClient {
  2.     static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);
  3.     public static void main(String[] args) {
  4.         NioEventLoopGroup worker = new NioEventLoopGroup();
  5.         try {
  6.             Bootstrap bootstrap = new Bootstrap();
  7.             bootstrap.channel(NioSocketChannel.class);
  8.             bootstrap.group(worker);
  9.             bootstrap.handler(new ChannelInitializer<SocketChannel>() {
  10.                 @Override
  11.                 protected void initChannel(SocketChannel ch) throws Exception {
  12.                     log.debug("connetted...");
  13.                     ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
  14.                         @Override
  15.                         //会在连接channel建立成功后,会触发Active事件
  16.                         public void channelActive(ChannelHandlerContext ctx) throws Exception {
  17.                             log.debug("sending...");
  18.                             Random r = new Random();
  19.                             char c = 'a';
  20.                             for (int i = 0; i < 10; i++) {
  21.                                 ByteBuf buffer = ctx.alloc().buffer();
  22.                                 buffer.writeBytes(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
  23.                                 ctx.writeAndFlush(buffer);
  24.                             }
  25.                         }
  26.                     });
  27.                 }
  28.             });
  29.             ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();
  30.             channelFuture.channel().closeFuture().sync();
  31.         } catch (InterruptedException e) {
  32.             log.error("client error", e);
  33.         } finally {
  34.             worker.shutdownGracefully();
  35.         }
  36.     }
  37. }
复制代码
服务器端的某次输出,可以看到一次就吸收了 160 个字节,而期望的是一次16字节,分 10 次吸收。这就出现了粘包现象
  1. 08:24:46 [DEBUG] [main] c.i.n.HelloWorldServer - [id: 0x81e0fda5] binding...
  2. 08:24:46 [DEBUG] [main] c.i.n.HelloWorldServer - [id: 0x81e0fda5, L:/0:0:0:0:0:0:0:0:8080] bound...
  3. 08:24:55 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x94132411, L:/127.0.0.1:8080 - R:/127.0.0.1:58177] REGISTERED
  4. 08:24:55 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x94132411, L:/127.0.0.1:8080 - R:/127.0.0.1:58177] ACTIVE
  5. 08:24:55 [DEBUG] [nioEventLoopGroup-3-1] c.i.n.HelloWorldServer - connected [id: 0x94132411, L:/127.0.0.1:8080 - R:/127.0.0.1:58177]
  6. 08:24:55 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x94132411, L:/127.0.0.1:8080 - R:/127.0.0.1:58177] READ: 160B
  7.          +-------------------------------------------------+
  8.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  9. +--------+-------------------------------------------------+----------------+
  10. |00000000| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
  11. |00000010| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
  12. |00000020| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
  13. |00000030| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
  14. |00000040| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
  15. |00000050| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
  16. |00000060| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
  17. |00000070| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
  18. |00000080| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
  19. |00000090| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
  20. +--------+-------------------------------------------------+----------------+
  21. 08:24:55 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x94132411, L:/127.0.0.1:8080 - R:/127.0.0.1:58177] READ COMPLETE
复制代码
半包现象

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

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

接下来看下Netty如何解决以上问题的:
方法1:短链接(极不保举)

以解决粘包为例
  1. public class HelloWorldClient {
  2.     static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);
  3.     public static void main(String[] args) {
  4.         // 分 10 次发送
  5.         for (int i = 0; i < 10; i++) {
  6.             send();
  7.         }
  8.     }
  9.     private static void send() {
  10.         NioEventLoopGroup worker = new NioEventLoopGroup();
  11.         try {
  12.             Bootstrap bootstrap = new Bootstrap();
  13.             bootstrap.channel(NioSocketChannel.class);
  14.             bootstrap.group(worker);
  15.             bootstrap.handler(new ChannelInitializer<SocketChannel>() {
  16.                 @Override
  17.                 protected void initChannel(SocketChannel ch) throws Exception {
  18.                     log.debug("conneted...");
  19.                     ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
  20.                     ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
  21.                         @Override
  22.                         public void channelActive(ChannelHandlerContext ctx) throws Exception {
  23.                             log.debug("sending...");
  24.                             ByteBuf buffer = ctx.alloc().buffer();
  25.                             buffer.writeBytes(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
  26.                             ctx.writeAndFlush(buffer);
  27.                             // 发完即关
  28.                             ctx.close();
  29.                         }
  30.                     });
  31.                 }
  32.             });
  33.             ChannelFuture channelFuture = bootstrap.connect("localhost", 8080).sync();
  34.             channelFuture.channel().closeFuture().sync();
  35.         } catch (InterruptedException e) {
  36.             log.error("client error", e);
  37.         } finally {
  38.             worker.shutdownGracefully();
  39.         }
  40.     }
  41. }
复制代码
输出,略
半包用这种办法照旧欠好解决,因为吸收方的缓冲区大小是有限的
方法2:固定长度

让所有数据包长度固定(假设长度为 8 字节),服务器端参加
  1. ch.pipeline().addLast(new FixedLengthFrameDecoder(8));
复制代码
客户端测试代码,留意, 接纳这种方法后,客户端什么时候 flush 都可以
  1. public class HelloWorldClient {
  2.     static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);
  3.     public static void main(String[] args) {
  4.         NioEventLoopGroup worker = new NioEventLoopGroup();
  5.         try {
  6.             Bootstrap bootstrap = new Bootstrap();
  7.             bootstrap.channel(NioSocketChannel.class);
  8.             bootstrap.group(worker);
  9.             bootstrap.handler(new ChannelInitializer<SocketChannel>() {
  10.                 @Override
  11.                 protected void initChannel(SocketChannel ch) throws Exception {
  12.                     log.debug("connetted...");
  13.                     ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
  14.                     ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
  15.                         @Override
  16.                         public void channelActive(ChannelHandlerContext ctx) throws Exception {
  17.                             log.debug("sending...");
  18.                             // 发送内容随机的数据包
  19.                             Random r = new Random();
  20.                             char c = 'a';
  21.                             ByteBuf buffer = ctx.alloc().buffer();
  22.                             for (int i = 0; i < 10; i++) {
  23.                                 byte[] bytes = new byte[8];
  24.                                 for (int j = 0; j < r.nextInt(8); j++) {
  25.                                     bytes[j] = (byte) c;
  26.                                 }
  27.                                 c++;
  28.                                 buffer.writeBytes(bytes);
  29.                             }
  30.                             ctx.writeAndFlush(buffer);
  31.                         }
  32.                     });
  33.                 }
  34.             });
  35.             ChannelFuture channelFuture = bootstrap.connect("192.168.0.103", 9090).sync();
  36.             channelFuture.channel().closeFuture().sync();
  37.         } catch (InterruptedException e) {
  38.             log.error("client error", e);
  39.         } finally {
  40.             worker.shutdownGracefully();
  41.         }
  42.     }
  43. }
复制代码
客户端输出
  1. 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] c.i.n.HelloWorldClient - connetted...
  2. 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x3c2ef3c2] REGISTERED
  3. 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x3c2ef3c2] CONNECT: /192.168.0.103:9090
  4. 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x3c2ef3c2, L:/192.168.0.103:53155 - R:/192.168.0.103:9090] ACTIVE
  5. 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] c.i.n.HelloWorldClient - sending...
  6. 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x3c2ef3c2, L:/192.168.0.103:53155 - R:/192.168.0.103:9090] WRITE: 80B
  7.          +-------------------------------------------------+
  8.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  9. +--------+-------------------------------------------------+----------------+
  10. |00000000| 61 61 61 61 00 00 00 00 62 00 00 00 00 00 00 00 |aaaa....b.......|
  11. |00000010| 63 63 00 00 00 00 00 00 64 00 00 00 00 00 00 00 |cc......d.......|
  12. |00000020| 00 00 00 00 00 00 00 00 66 66 66 66 00 00 00 00 |........ffff....|
  13. |00000030| 67 67 67 00 00 00 00 00 68 00 00 00 00 00 00 00 |ggg.....h.......|
  14. |00000040| 69 69 69 69 69 00 00 00 6a 6a 6a 6a 00 00 00 00 |iiiii...jjjj....|
  15. +--------+-------------------------------------------------+----------------+
  16. 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x3c2ef3c2, L:/192.168.0.103:53155 - R:/192.168.0.103:9090] FLUSH
复制代码
服务端输出
  1. 12:06:51 [DEBUG] [main] c.i.n.HelloWorldServer - [id: 0xe3d9713f] binding...
  2. 12:06:51 [DEBUG] [main] c.i.n.HelloWorldServer - [id: 0xe3d9713f, L:/192.168.0.103:9090] bound...
  3. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] REGISTERED
  4. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] ACTIVE
  5. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] c.i.n.HelloWorldServer - connected [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155]
  6. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
  7.          +-------------------------------------------------+
  8.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  9. +--------+-------------------------------------------------+----------------+
  10. |00000000| 61 61 61 61 00 00 00 00                         |aaaa....        |
  11. +--------+-------------------------------------------------+----------------+
  12. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
  13.          +-------------------------------------------------+
  14.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  15. +--------+-------------------------------------------------+----------------+
  16. |00000000| 62 00 00 00 00 00 00 00                         |b.......        |
  17. +--------+-------------------------------------------------+----------------+
  18. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
  19.          +-------------------------------------------------+
  20.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  21. +--------+-------------------------------------------------+----------------+
  22. |00000000| 63 63 00 00 00 00 00 00                         |cc......        |
  23. +--------+-------------------------------------------------+----------------+
  24. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
  25.          +-------------------------------------------------+
  26.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  27. +--------+-------------------------------------------------+----------------+
  28. |00000000| 64 00 00 00 00 00 00 00                         |d.......        |
  29. +--------+-------------------------------------------------+----------------+
  30. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
  31.          +-------------------------------------------------+
  32.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  33. +--------+-------------------------------------------------+----------------+
  34. |00000000| 00 00 00 00 00 00 00 00                         |........        |
  35. +--------+-------------------------------------------------+----------------+
  36. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
  37.          +-------------------------------------------------+
  38.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  39. +--------+-------------------------------------------------+----------------+
  40. |00000000| 66 66 66 66 00 00 00 00                         |ffff....        |
  41. +--------+-------------------------------------------------+----------------+
  42. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
  43.          +-------------------------------------------------+
  44.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  45. +--------+-------------------------------------------------+----------------+
  46. |00000000| 67 67 67 00 00 00 00 00                         |ggg.....        |
  47. +--------+-------------------------------------------------+----------------+
  48. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
  49.          +-------------------------------------------------+
  50.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  51. +--------+-------------------------------------------------+----------------+
  52. |00000000| 68 00 00 00 00 00 00 00                         |h.......        |
  53. +--------+-------------------------------------------------+----------------+
  54. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
  55.          +-------------------------------------------------+
  56.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  57. +--------+-------------------------------------------------+----------------+
  58. |00000000| 69 69 69 69 69 00 00 00                         |iiiii...        |
  59. +--------+-------------------------------------------------+----------------+
  60. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
  61.          +-------------------------------------------------+
  62.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  63. +--------+-------------------------------------------------+----------------+
  64. |00000000| 6a 6a 6a 6a 00 00 00 00                         |jjjj....        |
  65. +--------+-------------------------------------------------+----------------+
  66. 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ COMPLETE
复制代码
缺点是,数据包的大小欠好把握
方法3:固定分隔符

服务端参加,默认以 \n 或 \r\n 作为分隔符,如果超出指定长度仍未出现分隔符,则抛出异常
  1. ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
复制代码
客户端在每条消息之后,参加 \n 分隔符
  1. public class HelloWorldClient {
  2.     static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);
  3.     public static void main(String[] args) {
  4.         NioEventLoopGroup worker = new NioEventLoopGroup();
  5.         try {
  6.             Bootstrap bootstrap = new Bootstrap();
  7.             bootstrap.channel(NioSocketChannel.class);
  8.             bootstrap.group(worker);
  9.             bootstrap.handler(new ChannelInitializer<SocketChannel>() {
  10.                 @Override
  11.                 protected void initChannel(SocketChannel ch) throws Exception {
  12.                     log.debug("connetted...");
  13.                     ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
  14.                     ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
  15.                         @Override
  16.                         public void channelActive(ChannelHandlerContext ctx) throws Exception {
  17.                             log.debug("sending...");
  18.                             Random r = new Random();
  19.                             char c = 'a';
  20.                             ByteBuf buffer = ctx.alloc().buffer();
  21.                             for (int i = 0; i < 10; i++) {
  22.                                 for (int j = 1; j <= r.nextInt(16)+1; j++) {
  23.                                     buffer.writeByte((byte) c);
  24.                                 }
  25.                                 buffer.writeByte(10);
  26.                                 c++;
  27.                             }
  28.                             ctx.writeAndFlush(buffer);
  29.                         }
  30.                     });
  31.                 }
  32.             });
  33.             ChannelFuture channelFuture = bootstrap.connect("192.168.0.103", 9090).sync();
  34.             channelFuture.channel().closeFuture().sync();
  35.         } catch (InterruptedException e) {
  36.             log.error("client error", e);
  37.         } finally {
  38.             worker.shutdownGracefully();
  39.         }
  40.     }
  41. }
复制代码
增长设置类和设置文件
  1. 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] c.i.n.HelloWorldClient - connetted...
  2. 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x1282d755] REGISTERED
  3. 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x1282d755] CONNECT: /192.168.0.103:9090
  4. 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x1282d755, L:/192.168.0.103:63641 - R:/192.168.0.103:9090] ACTIVE
  5. 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] c.i.n.HelloWorldClient - sending...
  6. 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x1282d755, L:/192.168.0.103:63641 - R:/192.168.0.103:9090] WRITE: 60B
  7.          +-------------------------------------------------+
  8.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  9. +--------+-------------------------------------------------+----------------+
  10. |00000000| 61 0a 62 62 62 0a 63 63 63 0a 64 64 0a 65 65 65 |a.bbb.ccc.dd.eee|
  11. |00000010| 65 65 65 65 65 65 65 0a 66 66 0a 67 67 67 67 67 |eeeeeee.ff.ggggg|
  12. |00000020| 67 67 0a 68 68 68 68 0a 69 69 69 69 69 69 69 0a |gg.hhhh.iiiiiii.|
  13. |00000030| 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a 0a             |jjjjjjjjjjj.    |
  14. +--------+-------------------------------------------------+----------------+
  15. 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x1282d755, L:/192.168.0.103:63641 - R:/192.168.0.103:9090] FLUSH
复制代码
设置文件
  1. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] c.i.n.HelloWorldServer - connected [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641]
  2. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 1B
  3.          +-------------------------------------------------+
  4.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  5. +--------+-------------------------------------------------+----------------+
  6. |00000000| 61                                              |a               |
  7. +--------+-------------------------------------------------+----------------+
  8. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 3B
  9.          +-------------------------------------------------+
  10.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  11. +--------+-------------------------------------------------+----------------+
  12. |00000000| 62 62 62                                        |bbb             |
  13. +--------+-------------------------------------------------+----------------+
  14. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 3B
  15.          +-------------------------------------------------+
  16.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  17. +--------+-------------------------------------------------+----------------+
  18. |00000000| 63 63 63                                        |ccc             |
  19. +--------+-------------------------------------------------+----------------+
  20. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 2B
  21.          +-------------------------------------------------+
  22.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  23. +--------+-------------------------------------------------+----------------+
  24. |00000000| 64 64                                           |dd              |
  25. +--------+-------------------------------------------------+----------------+
  26. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 10B
  27.          +-------------------------------------------------+
  28.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  29. +--------+-------------------------------------------------+----------------+
  30. |00000000| 65 65 65 65 65 65 65 65 65 65                   |eeeeeeeeee      |
  31. +--------+-------------------------------------------------+----------------+
  32. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 2B
  33.          +-------------------------------------------------+
  34.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  35. +--------+-------------------------------------------------+----------------+
  36. |00000000| 66 66                                           |ff              |
  37. +--------+-------------------------------------------------+----------------+
  38. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 7B
  39.          +-------------------------------------------------+
  40.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  41. +--------+-------------------------------------------------+----------------+
  42. |00000000| 67 67 67 67 67 67 67                            |ggggggg         |
  43. +--------+-------------------------------------------------+----------------+
  44. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 4B
  45.          +-------------------------------------------------+
  46.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  47. +--------+-------------------------------------------------+----------------+
  48. |00000000| 68 68 68 68                                     |hhhh            |
  49. +--------+-------------------------------------------------+----------------+
  50. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 7B
  51.          +-------------------------------------------------+
  52.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  53. +--------+-------------------------------------------------+----------------+
  54. |00000000| 69 69 69 69 69 69 69                            |iiiiiii         |
  55. +--------+-------------------------------------------------+----------------+
  56. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 11B
  57.          +-------------------------------------------------+
  58.          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
  59. +--------+-------------------------------------------------+----------------+
  60. |00000000| 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a                |jjjjjjjjjjj     |
  61. +--------+-------------------------------------------------+----------------+
  62. 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ COMPLETE
复制代码
修改编解码器
[code]/** * 必须和 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[length];        in.readBytes(bytes, 0, length);        // 找到反序列化算法        Serializer.Algorithm algorithm = Serializer.Algorithm.values()[serializerAlgorithm];        // 确定具体消息类型        Class, Object> options = options0();    synchronized (options) {        setChannelOptions(channel, options, logger);    }    final Map, Object>[] currentChildOptions;    final Entry




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4