写点什么

netty 系列之:netty 中的核心编码器 bytes 数组

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

    阅读完需:约 10 分钟

netty系列之:netty中的核心编码器bytes数组

简介

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


对于 byte 数组来说,如果每次都将其封装进 ByteBuf 中,再进行传输显得有些麻烦。于是 netty 提供了一个基于 bytes 的核心编码解码器。

byte 是什么

那么 byte 是什么呢? byte 表示的是一个字节,也就是 8bits。用二进制表示就是-128-127 的范围。byte 是 JAVA 中的基础类型。


同时它还有一个 wrap 类型叫做 Byte。


先看下 Byte 的定义:


public final class Byte extends Number implements Comparable<Byte>
复制代码


Byte 中定义了 byte 的取值访问:


    public static final byte   MIN_VALUE = -128;
public static final byte MAX_VALUE = 127;
复制代码


并且还提供了一些基本的工具方法。


因为 byte 表示的是一个 8bits 的二进制,如果不算位运算的话,byte 基本上是 JAVA 中最小的数据存储单位了。所以 JAVA 中所有的对象都可以转换成为 byte。


基础类型的转换这里就不多讲了。这里主要看一下字符串 String 和对象 Object 和 byte 数组之间的转换。


先来看下字符串 String 和 byte 数组之间的转换,也就是 String 和二进制之间的转换。


基本的转换思路就是将 String 中的字符进行编码,然后将编码过后的字符进行存储即可。


String 类本身提供了一个 getBytes 方法,可以接受编码类型,以 UTF-8 来说,我们来看下转换方法的调用:


    public static byte[] stringToBytes(String str) throws UnsupportedEncodingException {       return str.getBytes("utf-8");    }
public static String bytesToString(byte[] bs) throws UnsupportedEncodingException { return new String(bs, "utf-8"); }
复制代码


直接调用 String 中的方法即可。


如果是 Object 对象的话,因为 Object 本身并没有提供转换的方法,所以我们需要借助于 ByteArrayOutputStream 的 toByteArray 方法和 ByteArrayInputStream 的 readObject 方法来实现 byte 数组和 Object 之间的转换,如下所示:


    //对象转数组    public byte[] toByteArray (Object obj) throws IOException {        try(ByteArrayOutputStream bos = new ByteArrayOutputStream();            ObjectOutputStream oos = new ObjectOutputStream(bos)) {            oos.writeObject(obj);            oos.flush();            return  bos.toByteArray();        }    }
//数组转对象 public Object toObject (byte[] bytes) throws IOException, ClassNotFoundException { try ( ByteArrayInputStream bis = new ByteArrayInputStream (bytes); ObjectInputStream ois = new ObjectInputStream (bis)) { return ois.readObject(); } }
复制代码

netty 中的 byte 数组的工具类

netty 中的核心是 ByteBuf,ByteBuf 提供了大部分基础数据类型的 read 和 write 方法。当然如果要读取对象,那么还是需要将对象转换成为 byte 然后再写入或者从 ByteBuf 中读出。


当然,netty 中不需要这么复杂,netty 提供了一个 Unpooled 的工具类用来方便的将 byte 数组和 ByteBuf 进行转换。


先看下 Unpooled 方法提供的 ByteBuff 构建方法:


   ByteBuf heapBuffer    = buffer(128);   ByteBuf directBuffer  = directBuffer(256);   ByteBuf wrappedBuffer = wrappedBuffer(new byte[128], new byte[256]);   ByteBuf copiedBuffer  = copiedBuffer(ByteBuffer.allocate(128));
复制代码


这是 Unpooled 提供的几种 ByteBuf 的构建方式,其中 heapBuffer 表示的是在用户空间构建的 buff,directBuffer 表示的是直接在系统空间构建的 buff。wrappedBuffer 是对现有的 byte 数组和 ByteBuf 之上构建的视图,而 copiedBuffer 是对 byte 数组,byteBuf 和字符串的拷贝。


这里我们需要用到 wrappedBuffer 方法,将 byte 数组封装到 ByteBuf 中:


    public static ByteBuf wrappedBuffer(byte[] array) {        if (array.length == 0) {            return EMPTY_BUFFER;        }        return new UnpooledHeapByteBuf(ALLOC, array, array.length);    }
复制代码


wrappedBuffer 返回了一个 UnpooledHeapByteBuf 对象,这个对象本身就是一个 ByteBuf。这里将 byte 数组作为构造函数传入 UnpooledHeapByteBuf 中。


这里的 array 是 UnpooledHeapByteBuf 中的私有变量:


byte[] array;
复制代码


除了构造函数,UnpooledHeapByteBuf 还提供了一个 setArray 的方法用来设置 byte 数组:


    private void setArray(byte[] initialArray) {        array = initialArray;        tmpNioBuf = null;    }
复制代码


下面是如何从 array 中构建 ByteBuf:


    public ByteBuf setBytes(int index, ByteBuffer src) {        ensureAccessible();        src.get(array, index, src.remaining());        return this;    }
复制代码


从 ByteBuf 中读取 byte 数组,可以调用 ByteBufUtil 的 getBytes 方法:


    public static byte[] getBytes(ByteBuf buf) {        return getBytes(buf,  buf.readerIndex(), buf.readableBytes());    }
复制代码

netty 中 byte 的编码器

万事俱备只欠东风,有了上面 netty 提供的工具类,我们就可以使用这些工具类构建基于 byte 的编码器了。


netty 中基于 byte 的编码解码器分别叫做 ByteArrayEncoder 和 ByteArrayDecoder。


先来看下这两个类是如何使用的,这里以一个典型的 TCP/IP 应用为例:


   ChannelPipeline pipeline = ...;     // Decoders   pipeline.addLast("frameDecoder",                    new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));   pipeline.addLast("bytesDecoder",                    new ByteArrayDecoder());     // Encoder   pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));   pipeline.addLast("bytesEncoder", new ByteArrayEncoder());
复制代码


这里的 LengthFieldBasedFrameDecoder 和 LengthFieldPrepender 是以消息长度为分割标准的 frame 分割器。这里我们主要关注 ChannelPipeline 中添加的 ByteArrayDecoder 和 ByteArrayEncoder。


添加了 byte 的编码和解码器之后,就可以直接在 handler 中直接使用 byte 数组,如下所示:


   void channelRead(ChannelHandlerContext ctx, byte[] bytes) {       ...   }
复制代码


先来看下 ByteArrayEncoder,这是一个编码器,它的实现很简单:


public class ByteArrayEncoder extends MessageToMessageEncoder<byte[]> {    @Override    protected void encode(ChannelHandlerContext ctx, byte[] msg, List<Object> out) throws Exception {        out.add(Unpooled.wrappedBuffer(msg));    }}
复制代码


具体就是使用 Unpooled.wrappedBuffer 方法 byte 数组封装成为 ByteBuf,然后将其添加到 out list 中。


同样的,我们观察一下 ByteArrayDecoder,这是一个解码器,实现也比较简单:


public class ByteArrayDecoder extends MessageToMessageDecoder<ByteBuf> {    @Override    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {         // copy the ByteBuf content to a byte array        out.add(ByteBufUtil.getBytes(msg));    }}
复制代码


具体的实现就是调用 ByteBufUtil.getBytes 方法,将 ByteBuf 转换成为 byte 数组,然后添加到 list 对象中。

总结

如果要在 netty 中传输二进制数据,netty 提供的 byte 编码和解码器已经封装了繁琐的细节,大家可以放心使用。


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

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

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

发布于: 23 小时前阅读数: 33
用户头像

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

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

评论

发布
暂无评论
netty系列之:netty中的核心编码器bytes数组_Java_程序那些事_InfoQ写作平台