黏包和半包

news/2024/11/23 2:51:32/

黏包和半包

黏包:

@Slf4j
public class HelloWorldServer {public static void main(String[] args) {NioEventLoopGroup boss = new NioEventLoopGroup();NioEventLoopGroup worker = new NioEventLoopGroup();try {ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.channel(NioServerSocketChannel.class);//接收缓冲区为10serverBootstrap.option(ChannelOption.SO_RCVBUF,10);serverBootstrap.group(boss,worker);serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));}});ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {log.error("server error",e);}finally {boss.shutdownGracefully();worker.shutdownGracefully();}}
}
  • client
@Slf4j
public class HelloWorldClient {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>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter(){//连接建立完成后触发该事件@Overridepublic 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});  //16个数据ctx.writeAndFlush(buf);}super.channelActive(ctx);}});}});ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();}finally {worker.shutdownGracefully();}}
}

客户端总共发出16 * 10 = 160b的消息,我们运行一下,可以看到服务端的结果

[id: 0x4961619f, L:/127.0.0.1:8080 - R:/127.0.0.1:9619] READ: 36B+-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|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                                     |....            |
+--------+-------------------------------------------------+----------------+[id: 0x4961619f, L:/127.0.0.1:8080 - R:/127.0.0.1:9619] READ: 40B+-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|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                         |........        |
+--------+-------------------------------------------------+----------------+[id: 0x4961619f, L:/127.0.0.1:8080 - R:/127.0.0.1:9619] READ: 40B+-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 0c 0d 0e 0f 00 01 02 03 04 05 06 07 08 09 0a 0b |................|
|00000010| 0c 0d 0e 0f 00 01 02 03 04 05 06 07 08 09 0a 0b |................|
|00000020| 0c 0d 0e 0f 00 01 02 03                         |........        |
+--------+-------------------------------------------------+----------------+[id: 0x4961619f, L:/127.0.0.1:8080 - R:/127.0.0.1:9619] READ: 40B+-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|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                         |........        |
+--------+-------------------------------------------------+----------------+[id: 0x4961619f, L:/127.0.0.1:8080 - R:/127.0.0.1:9619] READ: 4B+-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 0c 0d 0e 0f                                     |....            |
+--------+-------------------------------------------------+----------------+

第一次发送36k,第二次到第四次都是40k,最后一次是4k,这已经发生了粘包

现象分析:

粘包的现象实际上和TCP网络连接的滑动窗口有关,在一个窗口内将数据全都接受到了,所以就会产生粘包

粘包原因:

  • 在应用层:接收方ByteBuf设置太大,默认1024
  • 网络层:TCP滑动窗口,假设接收方发送256byte表示一个完整报文,但由于接收方处理不及时,并且窗口大小足够大,这256byte就会缓冲在接收方的滑动窗口中,当滑动窗口缓冲了多个报文就会产生粘包
  • Nagle算法(尽可能多的发送数据,攒够再发)也会造成粘包

半包原因:

  • 应用层:接收方ByteBuf小于实际发送数据量
  • 滑动窗口:窗口放不下,需要先发一部分,等待ack之后再发另一部分
  • MSS限制:发送的数据超过MSS后,数据会切片发送,就会造成半包

解决办法:

1.给消息体长度确定,每次都发定长的内容

因为我们发送的是16b的消息,所以给服务端定长

//最小值16
serverBootstrap.childOption(ChannelOption.RCVBUF_ALLOCATOR,new AdaptiveRecvByteBufAllocator(16,16,16));
2.定长解码器

客户端于服务器约定一个最大长度,保证客户端每次发送的数据长度都不会大于该长度。若发送数据长度不足则需要补齐至该长度

服务器接收数据时,将接收到的数据按照约定的最大长度进行拆分,即使发送过程中产生了粘包,也可以通过定长解码器将数据正确地进行拆分。服务端需要用到FixedLengthFrameDecoder对数据进行定长解码,具体使用方法如下:

//采用16定长
ch.pipeline().addLast(new FixedLengthFrameDecoder(16));

解码需要放在其他处理器之前,不然永远不是解码的结果

客户端channelActive内进行消息发送:

final int len = 16;
char c = 'a';
//发送10次
for (int m = 0; m < 10; m++) {ByteBuf buffer = ctx.alloc().buffer(len);//定长数组,未使用部分用0补充byte[] bytes = new byte[len];//制造出末尾使用0补充的byte数组for (int i = 0; i < (int) (Math.random() * len -1); i++) {bytes[i] = (byte) c;}buffer.writeBytes(bytes);ctx.writeAndFlush(buffer);
}

可以解决粘包半包,但是占内存

3.行解码器

行解码器的是通过分隔符对数据进行拆分来解决粘包半包问题的

可以通过LineBasedFrameDecoder(int maxLength)来拆分以换行符(\n)为分隔符的数据,也可以通过DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf... delimiters)指定通过什么分隔符来拆分数据(可以传入多个分隔符)

两种解码器都需要传入数据的最大长度,若超出最大长度,会抛出TooLongFrameException异常

  • 客户端
ByteBuf buffer = ctx.alloc().buffer();
char c = '0';
Random r = new Random();
for (int i = 0; i < 10 ; i++) {StringBuilder builder = makeString(c, r.nextInt(256)+1);c++;buffer.writeBytes(builder.toString().getBytes());
}
ctx.writeAndFlush(buffer);
  • makeString
 public static StringBuilder makeString(char c,int len){StringBuilder builder = new StringBuilder();for (int i = 0; i < len; i++) {builder.append(c);}builder.append("\n");return builder;}
  • 服务端
//行分隔符,默认是\n 可以自定义  行预定义1024字节
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
socketChannel.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));

客户端发送的消息直接,仔细看可以看到一个点,这个点就是我们的换行符

         +-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000010| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000020| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000030| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000040| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000050| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000060| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000070| 30 30 30 30 30 30 30 30 30 0a 31 31 31 31 31 31 |000000000.111111|
|00000080| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000090| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|000000a0| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|000000b0| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|000000c0| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|000000d0| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|000000e0| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|000000f0| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000100| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000110| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000120| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000130| 31 31 31 31 31 31 31 31 31 0a 32 32 32 32 32 32 |111111111.222222|
|00000140| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|00000150| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|00000160| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|00000170| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|00000180| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|00000190| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|000001a0| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|000001b0| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|000001c0| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|000001d0| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|000001e0| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|000001f0| 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
|00000200| 32 32 32 32 32 0a 33 33 33 33 33 33 33 33 33 33 |22222.3333333333|
|00000210| 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
|00000220| 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
|00000230| 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
|00000240| 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
|00000250| 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
|00000260| 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
|00000270| 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
|00000280| 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
|00000290| 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
|000002a0| 33 33 33 33 33 33 0a 34 34 34 34 34 34 34 34 34 |333333.444444444|
|000002b0| 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 |4444444444444444|
|000002c0| 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 |4444444444444444|
|000002d0| 34 34 34 34 34 34 34 34 0a 35 35 35 35 35 35 35 |44444444.5555555|
|000002e0| 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
|000002f0| 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
|00000300| 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
|00000310| 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
|00000320| 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
|00000330| 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
|00000340| 35 35 35 35 35 35 35 35 35 35 35 35 35 35 0a 36 |55555555555555.6|
|00000350| 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 |6666666666666666|
|00000360| 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 |6666666666666666|
|00000370| 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 |6666666666666666|
|00000380| 36 36 36 36 36 36 36 36 0a 37 37 37 37 37 37 37 |66666666.7777777|
|00000390| 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 |7777777777777777|
|000003a0| 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 |7777777777777777|
|000003b0| 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 |7777777777777777|
|000003c0| 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 |7777777777777777|
|000003d0| 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 |7777777777777777|
|000003e0| 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 |7777777777777777|
|000003f0| 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 0a |777777777777777.|
|00000400| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|00000410| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|00000420| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|00000430| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|00000440| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|00000450| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|00000460| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|00000470| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|00000480| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|00000490| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|000004a0| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|000004b0| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|000004c0| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 |8888888888888888|
|000004d0| 38 38 38 38 38 38 38 38 0a 39 39 39 39 39 39 39 |88888888.9999999|
|000004e0| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|000004f0| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000500| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000510| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000520| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000530| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000540| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000550| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000560| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000570| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000580| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000590| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|000005a0| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|000005b0| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|000005c0| 39 39 0a                                        |99.             |
+--------+-------------------------------------------------+----------------+
17:15:14.170 [nioEventLoopGroup-2-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xf78387e3, L:/127.0.0.1:2743 - R:/127.0.0.1:8080] FLUSH

但是此时服务端是可以正确收到内容的:

         +-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000010| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000020| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000030| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000040| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000050| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000060| 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
|00000070| 30 30 30 30 30 30 30 30 30                      |000000000       |
+--------+-------------------------------------------------+----------------++-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000010| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000020| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000030| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000040| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000050| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000060| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000070| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000080| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|00000090| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|000000a0| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
|000000b0| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31    |111111111111111 |
+--------+-------------------------------------------------+----------------+....+-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000010| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000020| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000030| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000040| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000050| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000060| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000070| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000080| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|00000090| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|000000a0| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|000000b0| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|000000c0| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|000000d0| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 |9999999999999999|
|000000e0| 39 39 39 39 39 39 39 39 39                      |999999999       |
+--------+-------------------------------------------------+----------------+

我们可以自定义分隔符,比如"\c"

  • 服务端代码
// 将分隔符放入ByteBuf中
ByteBuf bufSet = ch.alloc().buffer().writeBytes("\\c".getB ytes(StandardCharsets.UTF_8));
// 通过行解码器对粘包数据进行拆分,以 \c 为分隔符
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(64, ch.alloc().buffer().writeBytes(bufSet)));
ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
长度字段解码器

在传送数据时可以在数据中添加一个用于表示有用数据长度的字段,在解码时读取出这个用于表明长度的字段,同时读取其他相关参数,即可知道最终需要的数据是什么样子的

LengthFieldBasedFrameDecoder解码器可以提供更为丰富的拆分方法,其构造方法有五个参数

public LengthFieldBasedFrameDecoder(int maxFrameLength,int lengthFieldOffset, int lengthFieldLength,int lengthAdjustment, int initialBytesToStrip)

参数解析

  • maxFrameLength 数据最大长度
    • 表示数据的最大长度(包括附加信息、长度标识等内容)
  • lengthFieldOffset 数据长度标识的起始偏移量
    • 用于指明数据第几个字节开始是用于标识有用字节长度的,因为前面可能还有其他附加信息
  • lengthFieldLength 数据长度标识所占字节数(用于指明有用数据的长度)
    • 数据中用于表示有用数据长度的标识所占的字节数
  • lengthAdjustment 长度表示与有用数据的偏移量
    • 用于指明数据长度标识和有用数据之间的距离,因为两者之间还可能有附加信息
  • initialBytesToStrip 数据读取起点
    • 读取起点,不读取 0 ~ initialBytesToStrip 之间的数据

参数图解

img

lengthFieldOffset   = 0
lengthFieldLength   = 2
lengthAdjustment    = 0
initialBytesToStrip = 0 (= do not strip header)BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
+--------+----------------+      +--------+----------------+
| Length | Actual Content |----->| Length | Actual Content |
| 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |
+--------+----------------+      +--------+----------------+Copy

从0开始即为长度标识,长度标识长度为2个字节

0x000C 即为后面 HELLO, WORLD的长度


lengthFieldOffset   = 0
lengthFieldLength   = 2
lengthAdjustment    = 0
initialBytesToStrip = 2 (= the length of the Length field)BEFORE DECODE (14 bytes)         AFTER DECODE (12 bytes)
+--------+----------------+      +----------------+
| Length | Actual Content |----->| Actual Content |
| 0x000C | "HELLO, WORLD" |      | "HELLO, WORLD" |
+--------+----------------+      +----------------+Copy

从0开始即为长度标识,长度标识长度为2个字节,读取时从第二个字节开始读取(此处即跳过长度标识)

因为跳过了用于表示长度的2个字节,所以此处直接读取HELLO, WORLD


lengthFieldOffset   = 2 (= the length of Header 1)
lengthFieldLength   = 3
lengthAdjustment    = 0
initialBytesToStrip = 0BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
+----------+----------+----------------+      +----------+----------+----------------+
| Header 1 |  Length  | Actual Content |----->| Header 1 |  Length  | Actual Content |
|  0xCAFE  | 0x00000C | "HELLO, WORLD" |      |  0xCAFE  | 0x00000C | "HELLO, WORLD" |
+----------+----------+----------------+      +----------+----------+----------------+Copy

长度标识前面还有2个字节的其他内容(0xCAFE),第三个字节开始才是长度标识,长度表示长度为3个字节(0x00000C)

Header1中有附加信息,读取长度标识时需要跳过这些附加信息来获取长度


lengthFieldOffset   = 0
lengthFieldLength   = 3
lengthAdjustment    = 2 (= the length of Header 1)
initialBytesToStrip = 0BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
+----------+----------+----------------+      +----------+----------+----------------+
|  Length  | Header 1 | Actual Content |----->|  Length  | Header 1 | Actual Content |
| 0x00000C |  0xCAFE  | "HELLO, WORLD" |      | 0x00000C |  0xCAFE  | "HELLO, WORLD" |
+----------+----------+----------------+      +----------+----------+----------------+Copy

从0开始即为长度标识,长度标识长度为3个字节,长度标识之后还有2个字节的其他内容(0xCAFE)

长度标识(0x00000C)表示的是从其后lengthAdjustment(2个字节)开始的数据的长度,即HELLO, WORLD,不包括0xCAFE


lengthFieldOffset   = 1 (= the length of HDR1)
lengthFieldLength   = 2
lengthAdjustment    = 1 (= the length of HDR2)
initialBytesToStrip = 3 (= the length of HDR1 + LEN)BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
+------+--------+------+----------------+      +------+----------------+
| HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
| 0xCA | 0x000C | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
+------+--------+------+----------------+      +------+----------------+Copy

长度标识前面有1个字节的其他内容,后面也有1个字节的其他内容,读取时从长度标识之后3个字节处开始读取,即读取 0xFE HELLO, WORLD

案例:

public class TestLengthFieldDecoder {public static void main(String[] args) {EmbeddedChannel channel = new EmbeddedChannel(//最大长度1024 数据长度起始从0 长度4字节 无附加信息所以偏移量为0 有版本号偏移量为1  最后处理的数据从5字节以后开始读(因为前5字节是长度+版本号占用)new LengthFieldBasedFrameDecoder(1024,0,4,1,5),new LoggingHandler(LogLevel.DEBUG));//写入数据内容: 内容长度(int 类型 4字节) + 实际内容ByteBuf buf = ByteBufAllocator.DEFAULT.buffer();send(buf, "hello, world");send(buf, "hi!");send(buf, "happy ");channel.writeInbound(buf);}private static void send(ByteBuf buf, String s) {byte[] bytes = s.getBytes();int length = bytes.length;//实际内容//先写长度buf.writeInt(length);//todo 复杂情况1:增加版本号buf.writeByte(1);//再写内容buf.writeBytes(bytes);}
}

结果:

[id: 0xembedded, L:embedded - R:embedded] READ: 12B+-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c 6c 6f 2c 20 77 6f 72 6c 64             |hello, world    |
+--------+-------------------------------------------------+----------------+
[id: 0xembedded, L:embedded - R:embedded] READ: 3B+-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 69 21                                        |hi!             |
+--------+-------------------------------------------------+----------------+
[id: 0xembedded, L:embedded - R:embedded] READ: 6B+-------------------------------------------------+|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 61 70 70 79 20                               |happy           |
+--------+-------------------------------------------------+----------------+
[id: 0xembedded, L:embedded - R:embedded] READ COMPLETE

http://www.ppmy.cn/news/68817.html

相关文章

ChatGPT使用体验

ChatGPT使用体验 前言 介绍ChatGPT 体验ChatGPT 菜谱 编程学习 出行导航 导游攻略 中英翻译 电影推荐 文章总结 总结 前言 最近关于ChatGPT的话题已经火爆了&#xff0c;我也观察和体验了一段时间。平心而论&#xff0c;这东西真的黑科技&#xff0c;大多行业都能通…

ES6中Proxy

1. Proxy 说明&#xff1a;Proxy可以理解成在目标对象架设一层拦截器&#xff0c;外界访问内部的变量都必须经过这一层&#xff0c;可以对外界的访问进行过滤和改写。 1.1例子&#xff1a; const proxynew Proxy(target,handler) 说明&#xff1a;Proxy对象的用法&#xff0…

Linux开发板安装Python环境

1. 环境介绍 硬件&#xff1a;STM32MP157&#xff0c;使用的是野火出的开发板。 软件&#xff1a;Debian ARM 架构制作的 Linux 发行版&#xff0c;版本信息如下&#xff1a; Linux发行版本&#xff1a;Debian GNU/Linux 10 内核版本&#xff1a;4.19.94 2. Python 简介…

十、Feign客户端

目录 1、在springcloud-order项目中引入Feign客户端的依赖 2、在server-order服务的启动类中添加注解EnableFeignClients 3、使用FeignClient注解声明Feign客户端需要调用的远程接口 3.1、server-pay服务提供远程接口Controller 3.2、server-member服务提供远程接口Contro…

从小白到黑客高手:一份全面详细的学习路线指南

前言 黑客从入门到精通需要经过深入的学习和实践&#xff0c;这是一个需要长时间投入和大量精力的过程。在这份学习路线中&#xff0c;我将为你介绍黑客学习的基本知识和技能&#xff0c;帮助你逐步掌握黑客技能。 黑客 一、入门阶段 1.了解计算机基础知识 学习计算机基础知…

MySQL深入浅出: order by if()与order by in()之条件排序

目录 1&#xff1a;原表数据 2&#xff1a;order by if&#xff08;&#xff09; 3&#xff1a;order by in&#xff08;&#xff09; 4&#xff1a;社区地址 1&#xff1a;原表数据 2&#xff1a;order by if&#xff08;&#xff09; SELECT * FROM people ORDER BY IF(…

使用Kubernetes进行CI/CD的最佳实践

使用Kubernetes进行CI/CD的最佳实践 一、概述1 CI/CD的定义2 Kubernetes在CI/CD中的重要性3 Kubernetes用于CI/CD的优势 二、Kubernetes的CI/CD工作流程1 代码构建1.1 使用Docker镜像构建1.2 使用Kubernetes插件构建 2 代码集成和测试2.1 使用Kubernetes的namespace分离测试环境…

【地铁上的设计模式】--行为型模式:策略模式

什么是策略模式 策略模式是一种行为型设计模式&#xff0c;它允许在运行时选择算法的行为。这种模式通过定义一系列算法&#xff0c;并将每个算法封装到一个独立的类中&#xff0c;使得它们可以相互替换。通过这种方式&#xff0c;客户端可以根据需要选择要使用的算法&#xf…