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

107 篇文章 22 订阅
96 篇文章 6 订阅

简介

我们知道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/

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

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
对于使用Netty 4.1和Protobuf的WebSocket编码器,你可以按照以下步骤进行设置: 1. 首先,确保你已经添加了Netty和Protobuf的依赖到你的项目。 2. 创建一个WebSocket编码器类,该类将负责将Protobuf消息编码为WebSocket帧。下面是一个示例代码: ```java import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; public class ProtobufWebSocketEncoder extends MessageToByteEncoder<MessageLite> { @Override protected void encode(ChannelHandlerContext ctx, MessageLite msg, ByteBuf out) throws Exception { byte[] bytes = msg.toByteArray(); out.writeBytes(bytes); } } ``` 3. 在你的Netty初始化代码,添加WebSocket编码器到你的ChannelPipeline。下面是一个示例代码: ```java import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast(new HttpServerCodec()) .addLast(new HttpObjectAggregator(65536)) .addLast(new WebSocketServerProtocolHandler("/websocket")) .addLast(new ProtobufWebSocketEncoder()) .addLast(new YourCustomWebSocketHandler()); } } ``` 在上面的代码,`YourCustomWebSocketHandler`是你自己实现的处理WebSocket消息的处理器。 4. 最后,在你的Netty服务器启动代码,绑定正确的端口并启动服务器。下面是一个示例代码: ```java import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; public class WebSocketServer { public static void main(String[] args) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new WebSocketServerInitializer()); ChannelFuture f = b.bind(8080).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } ``` 确保将端口号8080更改为你实际使用的端口号。 以上就是使用Netty 4.1和Protobuf的WebSocket编码器的基本设置。请根据你的实际需求进行适当的修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flydean程序那些事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值