文章目录

  • 简介
  • 自定义编码器和解码器的实现
  • ReplayingDecoder
  • 总结

简介

在之前的系列文章中,我们提到了netty中的channel只接受ByteBuf类型的对象,如果不是ByteBuf对象的话,需要用编码和解码器对其进行转换,今天来聊一下netty自定义的编码和解码器实现中需要注意的问题。

自定义编码器和解码器的实现

在介绍netty自带的编码器和解码器之前,告诉大家怎么实现自定义的编码器和解码器。

netty中所有的编码器和解码器都是从ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter衍生而来的。

对于ChannelOutboundHandlerAdapter来说,最重要的两个类是MessageToByteEncoder 和 MessageToMessageEncoder

MessageToByteEncoder是将消息编码成为ByteBuf,这个类也是我们自定义编码最常用的类,直接继承这个类并实现encode方法即可。注意到这个类有一个泛型,这个泛型指定的就是消息的对象类型。

例如我们想将Integer转换成为ByteBuf,可以这样写:

       public class IntegerEncoder extends MessageToByteEncoder<Integer> {@Overridepublic void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out)throws Exception {out.writeInt(msg);}}

MessageToMessageEncoder是在消息和消息之间进行转换,因为消息并不能直接写入到channel中,所以需要和MessageToByteEncoder配合使用。

下面是一个Integer到String的例子:

       public class IntegerToStringEncoder extendsMessageToMessageEncoder<Integer> {@Overridepublic void encode(ChannelHandlerContext ctx, Integer message, List<Object> out)throws Exception {out.add(message.toString());}}

对于ChannelInboundHandlerAdapter来说,最重要的两个类是ByteToMessageDecoder和MessageToMessageDecoder

ByteToMessageDecoder是将ByteBuf转换成对应的消息类型,我们需要继承这个类,并实现decode方法,下面是一个从ByteBuf中读取所有可读的字节,并将结果放到一个新的ByteBuf中,

       public class SquareDecoder extends ByteToMessageDecoder {@Overridepublic void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)throws Exception {out.add(in.readBytes(in.readableBytes()));}}

MessageToMessageDecoder是消息和消息之间的转换,同样的只需要实现decode方法即可,如下从String转换到Integer:

       public class StringToIntegerDecoder extendsMessageToMessageDecoder<String> {@Overridepublic void decode(ChannelHandlerContext ctx, String message,List<Object> out) throws Exception {out.add(message.length());}}

ReplayingDecoder

上面的代码看起来很简单,但是在实现的过程中还有一些问题要注意。

对于Decoder来说,我们从ByteBuf中读取数据,然后进行转换。但是在读取的过程中,并不知道ByteBuf中数据的变动情况,有可能在读取的过程中ByteBuf还没有准备好,那么就需要在读取的时候对ByteBuf中可读字节的大小进行判断。

比如我们需要解析一个数据结构,这个数据结构的前4个字节是一个int,表示后面byte数组的长度,我们需要先判断ByteBuf中是否有4个字节,然后读取这4个字节作为Byte数组的长度,然后再读取这个长度的Byte数组,最终得到要读取的结果,如果其中的某一步出现问题,或者说可读的字节长度不够,那么就需要直接返回,等待下一次的读取。如下所示:

   public class IntegerHeaderFrameDecoder extends ByteToMessageDecoder {@Overrideprotected void decode(ChannelHandlerContext ctx,ByteBuf buf, List<Object> out) throws Exception {if (buf.readableBytes() < 4) {return;}buf.markReaderIndex();int length = buf.readInt();if (buf.readableBytes() < length) {buf.resetReaderIndex();return;}out.add(buf.readBytes(length));}}

这种判断是比较复杂同时也是可能出错的,为了解决这个问题,netty提供了 ReplayingDecoder用来简化上面的操作,在ReplayingDecoder中,假设所有的ByteBuf已经处于准备好的状态,直接从中间读取即可。

上面的例子用ReplayingDecoder重写如下:

   public class IntegerHeaderFrameDecoderextends ReplayingDecoder<Void> {protected void decode(ChannelHandlerContext ctx,ByteBuf buf, List<Object> out) throws Exception {out.add(buf.readBytes(buf.readInt()));}}

它的实现原理是去尝试读取对应的字节信息,如果没有读到,则抛出异常,ReplayingDecoder接收到异常之后,会重新调用decode方法。

虽然ReplayingDecoder使用起来非常简单,但是它有两个问题。

第一个问题是性能问题,因为会去重复调用decode方法,如果ByteBuf本身并没有变化,就会导致重复decode同一个ByteBuf,照成性能的浪费。解决这个问题就是在在decode的过程中分阶段进行,比如上面的例子中,我们需要先读取Byte数组的长度,然后再读取真正的byte数组。所以在读完byte数组长度之和,可以调用checkpoint()方法做一个保存点,下次再执行decode方法的时候就可以跳过这个保存点,继续后续的执行过程,如下所示:

   public enum MyDecoderState {READ_LENGTH,READ_CONTENT;}public class IntegerHeaderFrameDecoderextends ReplayingDecoder<MyDecoderState> {private int length;public IntegerHeaderFrameDecoder() {// Set the initial state.super(MyDecoderState.READ_LENGTH);}@Overrideprotected void decode(ChannelHandlerContext ctx,ByteBuf buf, List<Object> out) throws Exception {switch (state()) {case READ_LENGTH:length = buf.readInt();checkpoint(MyDecoderState.READ_CONTENT);case READ_CONTENT:ByteBuf frame = buf.readBytes(length);checkpoint(MyDecoderState.READ_LENGTH);out.add(frame);break;default:throw new Error("Shouldn't reach here.");}}}

第二个问题是同一个实例的decode方法可能会被调用多次,如果我们在ReplayingDecoder中有私有变量的话,则需要考虑对这个私有变量的清洗工作,避免多次调用造成的数据污染。

总结

通过继承上面的几个类,我们就可以自己实现编码和解码的逻辑了。但是好像还有点问题,自定义编码和解码器是不是太复杂了?还需要判断要读取的byte数组的大小。有没有更加简单的方法呢?

有的,敬请期待netty系列的下一篇文章:netty自带的编码器和解码器.

本文的例子可以参考:learn-netty4

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

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

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

netty系列之:自定义编码和解码器要注意的问题相关推荐

  1. netty系列之:自定义编码解码器

    文章目录 简介 自定义编码器 自定义解码器 添加编码解码器到pipeline 计算2的N次方 总结 简介 在之前的netty系列文章中,我们讲到了如何将对象或者String转换成为ByteBuf,通过 ...

  2. 【Apache Mina2.0开发之二】自定义实现Server/Client端的编解码工厂(自定义编码与解码器)!...

    本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/apache-mina/831.html ☞ ...

  3. netty系列之:内置的Frame detection

    文章目录 简介 Frame detection DelimiterBasedFrameDecoder FixedLengthFrameDecoder LengthFieldBasedFrameDeco ...

  4. netty系列之:netty中的frame解码器

    简介 netty中的数据是通过ByteBuf来进行传输的,一个ByteBuf中可能包含多个有意义的数据,这些数据可以被称作frame,也就是说一个ByteBuf中可以包含多个Frame. 对于消息的接 ...

  5. RNN 循环神经网络系列 5: 自定义单元

    原文地址:RECURRENT NEURAL NETWORK (RNN) – PART 5: CUSTOM CELLS 原文作者:GokuMohandas 译文出自:掘金翻译计划 本文永久链接:gith ...

  6. 【Netty】什么是编码器和解码器?

    什么是编码器和解码器? 从网络传输的角度来讲,数组总是以字节的格式在网络之中进行传输的. 每当源主机发送数据到目标主机时,数据会从本地格式被转换成字节进行传输,这种转换被称为编码,编码的逻辑由编码器处 ...

  7. netty系列之:一口多用,使用同一端口运行不同协议

    文章目录 简介 SocksPortUnificationServerHandler 自定义PortUnificationServerHandler 总结 简介 在之前的文章中,我们介绍了在同一个net ...

  8. netty系列之:使用netty搭建websocket服务器

    文章目录 简介 netty中的websocket websocket的版本 FrameDecoder和FrameEncoder WebSocketServerHandshaker WebSocketF ...

  9. 【读后感】Netty 系列之 Netty 高性能之道 - 相比 Mina 如何 ?

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

最新文章

  1. JAVA并发编程实战---第三章:对象的共享
  2. jar打包 剔除第三方依赖以及它的依赖_maven打包成第三方jar包且把pom依赖包打入进来的方法...
  3. linux下交叉编译libusb的方法及编译一个使用了libusb库的test程序的方法
  4. 使用Eclipse创建一个Android程序方法
  5. vue 父组件与子组件之间的传值(主动传值)
  6. bin文件怎么转换成文本文档_怎么把pdf文件转换成word文档?这样转很简单
  7. Java - 多线程Callable、Executors、Future
  8. 详解分布式系统与消息投递(架构师必备)
  9. TCP和TCP/IP的区别
  10. 通信原理学习笔记-第二章《基础知识》
  11. 27款优秀的Android逆向工程工具
  12. 第一章 Maxwell 概述
  13. 【字符编码】Unicode字符集与字符编码方式
  14. java语音识别毕业设计,HMM的语音识别技术的毕业设计
  15. 你缺的不是一个“大牛” 而是一个透视宝
  16. Interactive natural language question answering over knowledge graphs论文导读
  17. datagirdview的单元格双击事件
  18. 安卓编程 app图标自定义
  19. github上能找到中文博主吗_Lyx的安装流程(windows10系统)及配置中文环境
  20. 飞机上的飞行数据记录处理运算 ReXgen 2 IMU:达到新的高度 惯性导航 航位运算

热门文章

  1. 【项目源码分享】基于C++实现的小型数据库(Windows/Linux环境)
  2. Cannot add or update a child row: a foreign key constraint fails (`university`.`instructor`, CONSTRA
  3. C++ Boost库简介
  4. 一些学习cocos2d的网站
  5. LXC C API 使用
  6. 顶级极客技术挑战赛,你敢来挑战吗?| 大神登峰造极
  7. 高薪进大厂 | 面试指南
  8. 实战:如何对磁盘和网络IO进行评估、监控、定位和优化?
  9. Java中由substring方法引发的内存泄漏
  10. Go 如何利用 Linux 内核的负载均衡能力?