简介

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

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

byte是什么

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

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

先看下Byte的定义:

  1. public final class Byte extends Number implements Comparable<Byte>

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

  1. public static final byte MIN_VALUE = -128;
  2. public static final byte MAX_VALUE = 127;

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

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

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

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

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

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

  1. public static byte[] stringToBytes(String str) throws UnsupportedEncodingException {
  2. return str.getBytes("utf-8");
  3. }
  4. public static String bytesToString(byte[] bs) throws UnsupportedEncodingException {
  5. return new String(bs, "utf-8");
  6. }

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

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

  1. //对象转数组
  2. public byte[] toByteArray (Object obj) throws IOException {
  3. try(ByteArrayOutputStream bos = new ByteArrayOutputStream();
  4. ObjectOutputStream oos = new ObjectOutputStream(bos)) {
  5. oos.writeObject(obj);
  6. oos.flush();
  7. return bos.toByteArray();
  8. }
  9. }
  10. //数组转对象
  11. public Object toObject (byte[] bytes) throws IOException, ClassNotFoundException {
  12. try (
  13. ByteArrayInputStream bis = new ByteArrayInputStream (bytes);
  14. ObjectInputStream ois = new ObjectInputStream (bis)) {
  15. return ois.readObject();
  16. }
  17. }

netty中的byte数组的工具类

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

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

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

  1. ByteBuf heapBuffer = buffer(128);
  2. ByteBuf directBuffer = directBuffer(256);
  3. ByteBuf wrappedBuffer = wrappedBuffer(new byte[128], new byte[256]);
  4. ByteBuf copiedBuffer = copiedBuffer(ByteBuffer.allocate(128));

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

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

  1. public static ByteBuf wrappedBuffer(byte[] array) {
  2. if (array.length == 0) {
  3. return EMPTY_BUFFER;
  4. }
  5. return new UnpooledHeapByteBuf(ALLOC, array, array.length);
  6. }

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

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

  1. byte[] array;

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

  1. private void setArray(byte[] initialArray) {
  2. array = initialArray;
  3. tmpNioBuf = null;
  4. }

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

  1. public ByteBuf setBytes(int index, ByteBuffer src) {
  2. ensureAccessible();
  3. src.get(array, index, src.remaining());
  4. return this;
  5. }

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

  1. public static byte[] getBytes(ByteBuf buf) {
  2. return getBytes(buf, buf.readerIndex(), buf.readableBytes());
  3. }

netty中byte的编码器

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

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

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

  1. ChannelPipeline pipeline = ...;
  2. // Decoders
  3. pipeline.addLast("frameDecoder",
  4. new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
  5. pipeline.addLast("bytesDecoder",
  6. new ByteArrayDecoder());
  7. // Encoder
  8. pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
  9. pipeline.addLast("bytesEncoder", new ByteArrayEncoder());

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

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

  1. void channelRead(ChannelHandlerContext ctx, byte[] bytes) {
  2. ...
  3. }

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

  1. public class ByteArrayEncoder extends MessageToMessageEncoder<byte[]> {
  2. @Override
  3. protected void encode(ChannelHandlerContext ctx, byte[] msg, List<Object> out) throws Exception {
  4. out.add(Unpooled.wrappedBuffer(msg));
  5. }
  6. }

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

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

  1. public class ByteArrayDecoder extends MessageToMessageDecoder<ByteBuf> {
  2. @Override
  3. protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
  4. // copy the ByteBuf content to a byte array
  5. out.add(ByteBufUtil.getBytes(msg));
  6. }
  7. }

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

总结

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

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

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

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

netty系列之:netty中的核心编码器bytes数组的更多相关文章

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

    简介 什么是Base64编码呢?在回答这个问题之前,我们需要了解一下计算机中文件的分类,对于计算机来说文件可以分为两类,一类是文本文件,一类是二进制文件. 对于二进制文件来说,其内容是用二进制来表示的 ...

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

    目录 简介 netty codec的实现逻辑 netty中Base64的实现 netty中的base64编码和解码器 Base64Encoder Base64Decoder 总结 简介 我们知道数据在 ...

  3. 【读后感】Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ?

    [读后感]Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ? 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商 ...

  4. Netty 系列之 Netty 高性能之道 高性能的三个主题 Netty使得开发者能够轻松地接受大量打开的套接字 Java 序列化

    Netty系列之Netty高性能之道 https://www.infoq.cn/article/netty-high-performance 李林锋 2014 年 5 月 29 日 话题:性能调优语言 ...

  5. netty系列之:netty中的核心解码器json

    目录 简介 java中对json的支持 netty对json的解码 总结 简介 程序和程序之间的数据传输方式有很多,可以通过二进制协议来传输,比较流行的像是thrift协议或者google的proto ...

  6. netty系列之:netty中的懒人编码解码器

    目录 简介 netty中的内置编码器 使用codec要注意的问题 netty内置的基本codec base64 bytes compression json marshalling protobuf ...

  7. netty系列之:netty实现http2中的流控制

    目录 简介 http2中的流控制 netty对http2流控制的封装 Http2FlowController Http2LocalFlowController Http2RemoteFlowContr ...

  8. netty系列之:netty中各不同种类的channel详解

    目录 简介 ServerChannel和它的类型 Epoll和Kqueue AbstractServerChannel ServerSocketChannel ServerDomainSocketCh ...

  9. netty系列之:netty架构概述

    目录 简介 netty架构图 丰富的Buffer数据机构 零拷贝 统一的API 事件驱动 其他优秀的特性 总结 简介 Netty为什么这么优秀,它在JDK本身的NIO基础上又做了什么改进呢?它的架构和 ...

随机推荐

  1. 《手把手教你》系列基础篇(八十三)-java+ selenium自动化测试-框架设计基础-TestNG测试报告-下篇(详解教程)

    1.简介 其实前边好像简单的提到过测试报告,宏哥觉得这部分比较重要,就着重讲解和介绍一下.报告是任何测试执行中最重要的部分,因为它可以帮助用户了解测试执行的结果.失败点和失败原因.另一方面,日志记录对 ...

  2. tea加密算法及其变种的研究

    tea 介绍 "TEA" 的全称为"Tiny Encryption Algorithm" 是1994年由英国剑桥大学的David j.wheeler发明的. T ...

  3. 如果你的Serializable类包含一个不可序列化的成员,会发生什么?你是如何解决的?

    任何序列化该类的尝试都会因NotSerializableException而失败,但这可以通过在 Java中 为 static 设置瞬态(trancient)变量来轻松解决. Java 序列化相关的常 ...

  4. Mybatis的XML文件调用静态方法

    如果需要在Mapper文件中调用静态方法,需要 <choose> // 需要静态方法返回true还是false <when test="@staticClass@stati ...

  5. 面试问题之C++语言:mutable关键字

    转载于:https://www.cnblogs.com/xkfz007/articles/2419540.html mutable关键字 mutable的中文意思是"可变的,易变的" ...

  6. kafka的message格式是什么样的?

    一个Kafka的Message由一个固定长度的header和一个变长的消息体body组成 header部分由一个字节的magic(文件格式)和四个字节的CRC32(用于判断body消息体是否正常)构成 ...

  7. redis 持久化有几种方式?

    面试题 redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的? 面试官心理分析 redis 如果仅仅只是将数据缓存在内存里面,如果 redis 宕机了再重启 ...

  8. 在 centos6 安装 MySQL5.7 官方文档

    Adding the MySQL Yum Repository First, add the MySQL Yum repository to your system's repository list ...

  9. 攻防世界supersqli

    supersqli 补充知识点 rename 命令格式: rename table 原表名 to 新表名 例如,在表myclass名字更改为youclass: mysql>rename tabl ...

  10. C语言类型转换原理

    C语言类型转换 int a; a=1.23 这里把1.23赋值给a发生了隐式转换,原理如下: int a; float b=3.14; a=b; b赋值给a的过程:首先找一个中间变量是a的类型(该例中 ...