乐趣区

关于java: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/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

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

退出移动版