Netty中的DelimiterBasedFrameDecoder使用方法详解

 更新时间:2023年12月15日 10:41:58   作者:mzjnumber1  
这篇文章主要介绍了Netty中的DelimiterBasedFrameDecoder使用方法详解,DelimiterBasedFrameDecoder与LineBasedFrameDecoder类似,只不过更加通用,允许我们指定任意特殊字符作为分隔符,我们还可以同时指定多个分隔符,需要的朋友可以参考下

1 DelimiterBasedFrameDecoder介绍

前面介绍了LineBasedFrameDecoder,其以换行符\n或者\r\n作为依据,遇到\n或者\r\n都认为是一条完整的消息。

而DelimiterBasedFrameDecoder与LineBasedFrameDecoder类似,只不过更加通用,允许我们指定任意特殊字符作为分隔符。我们还可以同时指定多个分隔符,如果在请求中发的确有多个分隔符,将会选择内容最短的一个分隔符作为依据。 例如:

   +--------------+
   | ABC\nDEF\r\n |
   +--------------+

如果我们指定分隔符为\n,那么将会解码出来2个消息

   +-----+-----+
   | ABC | DEF |
   +-----+-----+

如果我们指定\r\n作为分隔符,那么只会解码出来一条消息

   +----------+
   | ABC\nDEF |
   +----------+

DelimiterBasedFrameDecoder提供了多个构造方法,最终调用的都是以下构造方法:

public DelimiterBasedFrameDecoder(
            int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters)

其中:

maxLength:

表示一行最大的长度,如果超过这个长度依然没有检测到\n或者\r\n,将会抛出TooLongFrameException

failFast:

与maxLength联合使用,表示超过maxLength后,抛出TooLongFrameException的时机。如果为true,则超出maxLength后立即抛出TooLongFrameException,不继续进行解码;如果为false,则等到完整的消息被解码后,再抛出TooLongFrameException异常。

stripDelimiter:

解码后的消息是否去除分隔符。

delimiters:

分隔符。我们需要先将分割符,写入到ByteBuf中,然后当做参数传入。

需要注意的是,netty并没有提供一个DelimiterBasedFrameDecoder对应的编码器实现(笔者没有找到),因此在发送端需要自行编码,添加分隔符。

2 Base64编解码

对于以特殊字符作为报文分割条件的协议的解码器,如:LineBasedFrameDecoder、DelimiterBasedFrameDecoder。

都存在一个典型的问题,如果发送数据当中本身就包含了分隔符,怎么办?如:我们要发送的内容为:

hello1\nhello2\nhello3\n

我们需要把这个内容整体当做一个有效报文来处理,而不是拆分成hello1、hello2、hello3。一些同学可能想到那可以换其他的特殊字符,但是如果内容中又包含你想指定的其他特殊字符怎么办呢?

因此我们通常需要发送的内容进行base64编码,base64中总共只包含了64个字符。

我们可以指定这64个字符之外的其他字符作为特殊分割字符;而接收端对应的进行base64解码,得到对应的原始的二进制流,然后进行处理。Netty提供了Base64Encoder/Base64Decoder来帮我们处理这个问题。需要注意的是,只需要对内容进行base64编码,分隔符不需要编码。

3 DelimiterBasedFrameDecoder结合Base64编解码案例

Server端:DelimiterBasedFrameDecoderServer

public class DelimiterBasedFrameDecoderServer {
   public static void main(String[] args) throws Exception {
      EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
      EventLoopGroup workerGroup = new NioEventLoopGroup();
      try {
         ServerBootstrap b = new ServerBootstrap(); // (2)
         b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // (3)
               .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                  @Override
                  public void initChannel(SocketChannel ch) throws Exception {
                     ByteBuf delemiter= Unpooled.buffer();
                     delemiter.writeBytes("&".getBytes());
                    //先使用DelimiterBasedFrameDecoder解码,以&作为分割符
                    ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, true, true,delemiter));
                    //之后使用Base64Decoder对数据进行解码,得到报文的原始的二进制流
                    ch.pipeline().addLast(new Base64Decoder());
                    //对请求报文进行处理
                     ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                           if (msg instanceof ByteBuf) {
                              ByteBuf packet = (ByteBuf) msg;
                              System.out.println(
                                    new Date().toLocaleString() + ":" + packet.toString(Charset.defaultCharset()));
                           }
                        }
                     });
                  }
               });
         // Bind and start to accept incoming connections.
         ChannelFuture f = b.bind(8080).sync(); // (7)
         System.out.println("DelimiterBasedFrameDecoderServer Started on 8080...");
         f.channel().closeFuture().sync();
      } finally {
         workerGroup.shutdownGracefully();
         bossGroup.shutdownGracefully();
      }
   }
}

Server接受到的数据,首先我们要去除分隔符,然后才能进行base64解码。因此首选我们添加了DelimiterBasedFrameDecoder根据&处理粘包办包问题,之后使用Base64Decoder进行解码,最后通过一个自定义ChannelInboundHandler打印请求的数据。client端:DelimiterBasedFrameDecoderClient

public class DelimiterBasedFrameDecoderClient {
   public static void main(String[] args) throws Exception {
      EventLoopGroup workerGroup = new NioEventLoopGroup();
      try {
         Bootstrap b = new Bootstrap(); // (1)
         b.group(workerGroup); // (2)
         b.channel(NioSocketChannel.class); // (3)
         b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
         b.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
               ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                   //在于server建立连接后,即发送请求报文
                  public void channelActive(ChannelHandlerContext ctx) {
                     //先对要发送的原始内容进行base64编码
                     ByteBuf content = Base64.encode(Unpooled.buffer().writeBytes("hello&tianshouzhi&".getBytes
                           ()));
                     //之后添加分隔符
                     ByteBuf req = Unpooled.copiedBuffer(content);
                     req.writeBytes("&".getBytes());
                     ctx.writeAndFlush(req);
                        }
               });
            }
         });
         // Start the client.
         ChannelFuture f = b.connect("127.0.0.1",8080).sync(); // (5)
         // Wait until the connection is closed.
         f.channel().closeFuture().sync();
      } finally {
         workerGroup.shutdownGracefully();
      }
   }
}

在编写client端代码时我们先对原始内容进行base64编码,然后添加分割符之后进行输出。

需要注意,虽然Netty提供了Base64Encoder进行编码,这里并没有直接使用,如果直接使用Base64Encoder,那么会对我们输出的所有内容进行编码,意味着分隔符也会被编码,这显然不符合我们的预期,所以这里直接使用了Netty提供了Base64工具类来处理。

如果一定要使用Base64Encoder,那么代码需要进行相应的修改,自定义的ChannelInboundHandler只输出原始内容,之后通过Base64Encoder进行编码,然后需要额外再定义一个ChannelOutboundHandler添加分隔符,如:

ch.pipeline().addLast(new ChannelOutboundHandlerAdapter() {
   @Override
   public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
      if(msg instanceof ByteBuf){
         ((ByteBuf) msg).writeBytes("&".getBytes());
      }
   }
});
ch.pipeline().addLast(new Base64Encoder());
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
    //在于server建立连接后,即发送请求报文
   public void channelActive(ChannelHandlerContext ctx) {
      ByteBuf req = Unpooled.buffer().writeBytes("hello&tianshouzhi&".getBytes());
      ctx.writeAndFlush(req);
                   }
});

上述两种方案效果是等价的。

当我们先后启动server和client后,会看到server端控制台输出:

DelimiterBasedFrameDecoderServer Started on 8080...
2018-9-8 13:56:32:hello&tianshouzhi&

说明经过base64编码后,我们的请求中可以包含分隔符作为内容。

如果我们将base64编解码相关逻辑去掉,你将会看到的输出是:

DelimiterBasedFrameDecoderServer Started on 8080...
2018-9-8 14:13:31:hello
2018-9-8 14:13:31:tianshouzhi

也就是说,原始内容也被误分割了,解码失败,读者可以自行验证。

到此这篇关于Netty中的DelimiterBasedFrameDecoder使用方法详解的文章就介绍到这了,更多相关DelimiterBasedFrameDecoder使用方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java项目常见工具类详解

    Java项目常见工具类详解

    这篇文章主要为大家总结了平时在Java项目中使用的工具类:JWT工具类、MD5工具类、视频点播工具类、公共常量工具类、日期操作工具类、Http客户端工具类和获取IP工具类。需要的可以参考一下
    2021-12-12
  • 浅谈JMeter engine启动原理

    浅谈JMeter engine启动原理

    本文主要介绍jmeter在控制台在点击执行之后底层所做的一些主要事情及内容,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 使用IDEA创建servlet JavaWeb 应用及使用Tomcat本地部署的实现

    使用IDEA创建servlet JavaWeb 应用及使用Tomcat本地部署的实现

    本文主要介绍了使用IDEA创建servlet JavaWeb 应用及使用Tomcat本地部署
    2022-01-01
  • Mybatis 连接mysql数据库底层运行的原理分析

    Mybatis 连接mysql数据库底层运行的原理分析

    这篇文章主要介绍了Mybatis 连接mysql数据库底层运行的原理分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • SpringBoot使用AOP统一日志管理的方法详解

    SpringBoot使用AOP统一日志管理的方法详解

    这篇文章主要为大家分享一个干货:超简洁SpringBoot使用AOP统一日志管理,文中的示例代码讲解详细,感兴趣的小伙伴快跟随小编一起学习学习吧
    2022-05-05
  • Eclipse创建JavaWeb工程的完整步骤记录

    Eclipse创建JavaWeb工程的完整步骤记录

    很多新手不知道Eclipse怎么创建Java Web项目,一起来看看吧,这篇文章主要给大家介绍了关于Eclipse创建JavaWeb工程的完整步骤,需要的朋友可以参考下
    2023-10-10
  • Spring Bean 依赖注入常见错误问题

    Spring Bean 依赖注入常见错误问题

    这篇文章主要介绍了Spring Bean 依赖注入常见错误问题,文中提到value的工作大体分为三个核心步骤,具体内容详情跟随小编一起看看吧
    2021-09-09
  • SpringBoot统计、监控SQL运行情况的方法详解

    SpringBoot统计、监控SQL运行情况的方法详解

    这篇文章主要给大家介绍了关于SpringBoot统计、监控SQL运行情况的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-02-02
  • 设计模式在Spring框架中的应用汇总

    设计模式在Spring框架中的应用汇总

    这篇文章主要介绍了设计模式在Spring框架中的应用汇总,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • SpringBoot项目打成war和jar的区别说明

    SpringBoot项目打成war和jar的区别说明

    这篇文章主要介绍了SpringBoot项目打成war和jar的区别说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06

最新评论