1

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

 3 years ago
source link: https://www.cnblogs.com/flydean/p/16178625.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

我们知道数据在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中提供了下面的几种类型:

STANDARD
URL_SAFE
ORDERED

其中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/

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

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


Recommend

  • 8
    • www.flydean.com 3 years ago
    • Cache

    netty系列之:java中的base64编码器

    netty系列之:java中的base64编码器 什么是Base64编码呢?在回答这个问题之前,我们需要了解一下计算机中文件的分类,对于计算机来说文件可以分为两类,一类是文本文件,一类是二进制文件。 对于二进制文件来说,其内容是...

  • 4

    我们知道数据在netty中传输是以ByteBuf的形式进行的,可以说ByteBuf是netty的数据传输基础。但是对于现代的应用程序来说,通常我们需要用到其他的数据结构或者类型。 为了方便我们在程序中的编写,一种方式就是在将数据传入到netty中的时候由程序员自身将...

  • 4

    netty系列之:netty中的核心MessageToMessage编码器 在netty中我们需要传递各种类型的消息,这些message可以是字符串,可以是数组,也可以是自定义的对象。不同的对象之间可能需要互相转换,这样就需要一个可以自由进行转换的转换...

  • 3

    之前的文章中,我们讲解了netty中从一个message转换成为另外一个message的框架叫做MessageToMessage编码器。但是message to message只考虑了channel中消息在处理过程中的转换,但是我们知道channel中最终传输的数据一定是ByteBuf,所以我们还需要一个message和B...

  • 3

    我们知道netty中数据传输的核心是ByteBuf,ByteBuf提供了多种数据读写的方法,包括基本类型和byte数组的读写方法。如果要在netty中传输这些数据,那么需要构建ByteBuf,然后调用ByteBuf中对应的方法写入对应的数据,接着套用netty中标准的模板即可使用。 ...

  • 5

    我们知道数据在 netty 中传输是以 ByteBuf 的形式进行的,可以说 ByteBuf 是 netty 的数据传输基础。但是对于现代的应用程序来说,通常我们需要用到其他的数据结构或者类型。为了方便我们在程序中的编写,一种方式就是在将数据传入到 netty 中的时候由程...

  • 4

    之前的文章中,我们讲解了netty中从一个message转换成为另外一个message的框架叫做MessageToMessage编码器。但是message to message只考虑了channel中消息在处理过程中的转换,但是我们知道channel中最终传输的数据一定是ByteBuf,所以我们还需要一个message和B...

  • 5

    我们知道netty中数据传输的核心是ByteBuf,ByteBuf提供了多种数据读写的方法,包括基本类型和byte数组的读写方法。如果要在netty中传输这些数据,那么需要构建ByteBuf,然后调用ByteBuf中对应的方法写入对应的数据,接着套用netty中标准的模板即可使用。 ...

  • 8

    程序和程序之间的数据传输方式有很多,可以通过二进制协议来传输,比较流行的像是thrift协议或者google的protobuf。这些二进制协议可以实现数据的有效传输,并且通过二进制的形式可以节省数据的体积,在某些速度和效率优先的情况下是非常有效的。并且如果不同的...

  • 4

    对于IO来说,除了传统的block IO,使用最多的就是NIO了,通常我们在netty程序中最常用到的就是NIO,比如NioEventLoopGroup,NioServerSocketChannel等。 我们也知道在IO中有比NIO更快的IO方式,比如kqueue和epoll,但是这两种方式需要native方法的支持,也就是说...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK