写点什么

netty 系列之:netty 中的核心编码器 base64

作者:程序那些事
  • 2022 年 4 月 06 日
  • 本文字数:3459 字

    阅读完需:约 11 分钟

netty系列之:netty中的核心编码器base64

简介

我们知道数据在 netty 中传输是以 ByteBuf 的形式进行的,可以说 ByteBuf 是 netty 的数据传输基础。但是对于现代的应用程序来说,通常我们需要用到其他的数据结构或者类型。


为了方便我们在程序中的编写,一种方式就是在将数据传入到 netty 中的时候由程序员自身将数据格式进行转换,然后再调用 netty 的系统方法。另外一种方式就是定义一些 codec,由 netty 的内在编码机制将程序中用到的数据格式和 ByteBuf 进行自动转换。


很明显,使用 codec 的方式更加简捷,也更加符合程序的开发规则。


为了方便程序的开发,netty 本身在内部定义了一些核心的 codec 插件,我们在需要的时候直接选用即可。


本文将会讲解 netty 内部实现 codec 的方式和一个最核心的编码器 base64。

netty codec 的实现逻辑

所有的 netty codec 的目的就是在数据传输过程中进行数据类型的转换,换句话说就是对数据进行处理。我们知道 netty 中有两个对数据进行 handler 的类,分别是 ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter,他们分别对应 channel 中的 inbound 消息和 outbound 消息进行处理。


所以很自然的,我们的 codec 逻辑只需要在这两个地方添加即可。


netty 为我们提供了两个 HandlerAdapter 类的继承类,分别是 MessageToMessageDecoder 和 MessageToMessageEncoder:


public abstract class MessageToMessageEncoder<I> extends ChannelOutboundHandlerAdapter 
public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter
复制代码


从名字就可以看出来这两个类分别使用来编码和解码用的,所以我们的 codec 只需要分别实现这两个类即可。


以下是一个 StringToIntegerDecoder 和 IntegerToStringEncoder 的例子:


       public class StringToIntegerDecoder extends               MessageToMessageDecoder<String> {              @Override           public void decode(ChannelHandlerContext ctx, String message,                              List<Object> out) throws Exception {               out.add(message.length());           }       }
复制代码


       public class IntegerToStringEncoder extends               MessageToMessageEncoder<Integer> {              @Override           public void encode(ChannelHandlerContext ctx, Integer message, List<Object> out)                   throws Exception {               out.add(message.toString());           }       }
复制代码


最简单的实现就是分别重构这两个类的 decode 和 encode 方法。

netty 中 Base64 的实现

我们知道 JDK 中已经有了 Base64 实现的工具类叫做 java.util.Base64。但是在 netty 中又使用了一个新的实现类同样叫做 Base64,它的全称是 io.netty.handler.codec.base64.Base64。


这个 Base64 类中用到了一个 Base64Dialect 类,也就是 netty 中 Base64 支持的 Base64 编码方式。Base64Dialect 中提供了下面的几种类型:


STANDARDURL_SAFEORDERED
复制代码


其中 STANDARD 对应的是 RFC3548 也是 JDK 中的标准 Base64,URL_SAFE 对应的是 RFC3548 中的 base64url 版本,对应的 JDK 中的 getUrlEncode。


最后一个是 ORDERED,代表的是 RFC1940,这个编码实现在 JDK 中是没有的。


为什么 JDK 中已经有了 Base64 的工具类,netty 中还需要自己创建一个新的类呢?


我们可以考虑一下在 netty 中 Base64 用到的场景,通常来说我们是在 handler 中添加自定义编码,而这些 handler 主要是针对于数据流进行处理。


JDK 中自带的 Base64 实现在定长的数据上使用还是没问题的,但是如果运用于数据流的处理话,效率就会比较低。所以 Netty 才需要为 base64 在流数据的情况下重新实现一个 Base64 类。


netty 中的实现方式使用的是 Robert Harder's Public Domain Base64 Encoder/Decoder。这里就不多讲这个算法的实现逻辑了。感兴趣的朋友可以自行探索。


Base64 提供了将 ByteBuf 进行 base64 编码和解码的方法,我们选择参数最长的方法来观察,如下所示:


    public static ByteBuf encode(            ByteBuf src, int off, int len, boolean breakLines, Base64Dialect dialect, ByteBufAllocator allocator)
public static ByteBuf decode( ByteBuf src, int off, int len, Base64Dialect dialect, ByteBufAllocator allocator)
复制代码


对于 encode 方法来说,需要下面几个参数:


  1. ByteBuf 类型的 src,这是我们需要进行编码的源。

  2. int 类型的 off 和 len,表示的是 ByteBuf 中要编码数据的位置。

  3. boolean 类型的 breakLines,表示是否添加换行符。

  4. Base64Dialect 类型的 dialect,表示选择的 base64 编码类型。

  5. ByteBufAllocator 的 allocator,表示返回的 ByteBuf 的生成方式。


同样的 Decode 方法,需要下面的几个参数:


  1. ByteBuf 类型的 src,这是我们需要进行解码的源。

  2. int 类型的 off 和 len,表示的是 ByteBuf 中要解码数据的位置。

  3. Base64Dialect 类型的 dialect,表示选择的 base64 编码类型。

  4. ByteBufAllocator 的 allocator,表示返回的 ByteBuf 的生成方式。

netty 中的 base64 编码和解码器

刚刚我们介绍了 netty 中提供的新的 Base64 工具类,这个工具类提供了将 ByteBuf 中数据进行编码和解码的方法。接下来我们看一下 netty 是如何使用这个工具类实现 netty 中的 base64 编码和解码器。


netty 中提供了对 Base64 的编码和解码器,分别是 Base64Encoder 和 Base64Decoder, 先来看下 Base64 编码解码器的基本使用:


   ChannelPipeline pipeline = ...;     // Decoders   pipeline.addLast("frameDecoder", new DelimiterBasedFrameDecoder(80, Delimiters.nulDelimiter()));   pipeline.addLast("base64Decoder", new Base64Decoder());     // Encoder   pipeline.addLast("base64Encoder", new Base64Encoder());
复制代码


用起来很简单,只需要把 Base64Decoder 和 Base64Encoder 添加到 pipeline 中即可。


有时候 Base64Decoder 需要和 DelimiterBasedFrameDecoder 一起使用,尤其是在 TCP/IP 协议中,因为我们需要根据特定的 Delimiters 来判断 ByteBuf 应该被分割为几个 frames。这样才能保证数据的有效性。

Base64Encoder

首先来看 base64 的编码器,Base64Encoder 的实现比较简单,首先来看下 Base64Encoder 的定义:


public class Base64Encoder extends MessageToMessageEncoder<ByteBuf> 
复制代码


Base64Encoder 继承自 MessageToMessageEncoder,它传入的泛型 ByteBuf,表示是将 ByteBuf 编码为 ByteBuf,虽然外部的 ByteBuf 类型没有变化,但是 ByteBuf 中的数据已经被编码成为 Base64 了。


接下来是 Base64Encoder 的构造函数:


    public Base64Encoder(boolean breakLines, Base64Dialect dialect) {        this.dialect = ObjectUtil.checkNotNull(dialect, "dialect");        this.breakLines = breakLines;    }
复制代码


Base64Encoder 可以接受两个参数,分别是是否有换行符的 breakLines 和 base64 编码方式的 Base64Dialect。


它的 encode 方法也很简单:


    protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {        out.add(Base64.encode(msg, msg.readerIndex(), msg.readableBytes(), breakLines, dialect));    }
复制代码


直接使用的是我们上面讲到的 Base64 工具类的 encode 方法,然后把返回值添加到 out 对象中。

Base64Decoder

Base64Decoder 用来将 ByteBuf 中的 base64 编码的内容解码成为原始内容,先来看下 Base64Decoder 的定义:


public class Base64Decoder extends MessageToMessageDecoder<ByteBuf> 
复制代码


Base64Decoder 继承了 MessageToMessageDecoder,传入的泛型是 ByteBuf。


先看下 Base64Decoder 的构造函数:


public Base64Decoder(Base64Dialect dialect) {        this.dialect = ObjectUtil.checkNotNull(dialect, "dialect");    }
复制代码


Base64Decoder 的构造函数很简单,和 Base64Encoder 相比它只需要一个参数就是 Base64Dialect 类型的 dialect,表示的是选择的 base64 解码的方式。


接下来就是它的解码方法:


    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {        out.add(Base64.decode(msg, msg.readerIndex(), msg.readableBytes(), dialect));    }
复制代码


解码方法也是调用 Base64 工具类的 decode 方法,然后将其添加到返回的 out list 中去。

总结

本章介绍了 netty 中的核心编码器 Base64,它负责将 ByteBuf 中的消息编码为 base64 格式,同时提供了对应的解码器,大家可以在需要的时候进行使用。


本文已收录于 http://www.flydean.com/14-1-netty-codec-base64/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 2022 年 04 月 06 日阅读数: 26
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论 (1 条评论)

发布
用户头像
兄弟,换个头图吧!
2022 年 04 月 07 日 17:00
回复
没有更多了
netty系列之:netty中的核心编码器base64_Java_程序那些事_InfoQ写作平台