文章目录

  • 简介
  • 自定义编码器
  • 自定义解码器
  • 添加编码解码器到pipeline
  • 计算2的N次方
  • 总结

简介

在之前的netty系列文章中,我们讲到了如何将对象或者String转换成为ByteBuf,通过使用netty自带的encoder和decoder可以实现非常方便的对象和ByteBuf之间的转换,然后就可以向channel中随意写入对象和字符串了。

使用netty自带的编码器当然很好,但是如果你有些特殊的需求,比如希望在编码的过程中对数据进行变换,或者对对象的字段进行选择,那么可能就需要自定义编码解码器了。

自定义编码器

自定义编码器需要继承MessageToByteEncoder 类,并实现encode方法,在该方法中写入具体的编码逻辑。

本例我们希望计算2的N次方,据说将一张纸折叠100次可以达到地球到月亮的高度,这么大的数据普通的number肯定是装不下的,我们将会使用BigInteger来对这个巨大的数字进行保存。

那么对于被编码器来说,则需要将这个BigInteger转换成为byte数组。同时在byte数组读取的过程中,我们需要界定到底哪些byte数据是属于同一个BigInteger的,这就需要对写入的数据格式做一个约定。

这里我们使用三部分的数据结构来表示一个BigInteger。第一部分是一个magic word也就是魔法词,这里我们使用魔法词“N”,当读取到这个魔法词就表示接下来的数字是BigInteger。第二部分是表示bigInteger数字的byte数组的长度,获取到这个长度值,就可以读取到所有的byte数组值,最后将其转换成为BigInteger。

因为BigInteger是Number的子类,为了更加泛化编码器,我们使用Number作为MessageToByteEncoder的泛型,核心编码代码如下:

 protected void encode(ChannelHandlerContext ctx, Number msg, ByteBuf out) {// 将number编码成为ByteBufBigInteger v;if (msg instanceof BigInteger) {v = (BigInteger) msg;} else {v = new BigInteger(String.valueOf(msg));}// 将BigInteger转换成为byte[]数组byte[] data = v.toByteArray();int dataLength = data.length;// 将Number进行编码out.writeByte((byte) 'N'); // 魔法词out.writeInt(dataLength);  // 数组长度out.writeBytes(data);      // 最终的数据}

自定义解码器

有了编码之后的byte数组,就可以在解码器中对其解码了。

上一节介绍了,编码过后的数据格式是魔法词N+数组长度+真正的数据。

其中魔法词长度是一个字节,数组长度是四个字节,前面部分总共是5个字节。所以在解码的时候,首先判断ByteBuf中可读字节的长度是否小于5,如果小于5说明数据是无效的,可以直接return。

如果可读字节的长度大于5,则表示数据是有效的,可以进行数据的解码了。

解码过程中需要注意的是,并不是所有的数据都是我们所希望的格式,如果在读取的过程中读到了我们不认识的格式,那么说明这个数据并不是我们想要的,则可以交由其他的handler进行处理。

但是对于ByteBuf来说,一旦调用read方法,就会导致reader index移动位置,所以在真正的读取数据之前需要调用ByteBuf的markReaderIndex方法,对readerIndex进行记录。然后分别读取魔法词、数组长度和剩余的数据,最后将数据转换成为BigInteger,如下所示:

 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {// 保证魔法词和数组长度有效if (in.readableBytes() < 5) {return;}in.markReaderIndex();// 检查魔法词int magicNumber = in.readUnsignedByte();if (magicNumber != 'N') {in.resetReaderIndex();throw new CorruptedFrameException("无效的魔法词: " + magicNumber);}// 读取所有的数据int dataLength = in.readInt();if (in.readableBytes() < dataLength) {in.resetReaderIndex();return;}// 将剩下的数据转换成为BigIntegerbyte[] decoded = new byte[dataLength];in.readBytes(decoded);out.add(new BigInteger(decoded));}

添加编码解码器到pipeline

有了两个编码解码器,还需要将其添加到pipeline中进行调用。

在实现ChannelInitializer中的initChannel中,可以对ChannelPipeline进行初始化,本例中的初始化代码如下:

// 对流进行压缩pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP));pipeline.addLast(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));// 添加number编码解码器pipeline.addLast(new NumberDecoder());pipeline.addLast(new NumberEncoder());// 添加业务处理逻辑pipeline.addLast(new CustomProtocolServerHandler());

其中最后一行是真正的业务处理逻辑,NumberDecoder和NumberEncoder是编码和解码器。这里我们还使用了一个ZlibEncoder用于对流数据进行压缩,这里使用的压缩方式是GZIP。

压缩的好处就是可以减少数据传输的数量,提升传输效率。其本质也是一个编码解码器。

计算2的N次方

计算2的N次方的逻辑是这样的,首先客户端发送2给服务器端,服务器端接收到该消息和结果1相乘,并将结果写回给客户端,客户端收到消息之后再发送2给服务器端,服务器端将上次的计算结果乘以2,再发送给客户端,以此类推直到执行N次。

首先看下客户端的发送逻辑:

// 最大计算2的1000次方ChannelFuture future = null;for (int i = 0; i < 1000 && next <= CustomProtocolClient.COUNT; i++) {future = ctx.write(2);next++;}

当next小于等于要计算的COUNT时,就将2写入到channel中。

对于服务器来说,在channelRead0方法中,读取消息,并将其和结果相乘,再把结果写回给客户端。

    public void channelRead0(ChannelHandlerContext ctx, BigInteger msg) throws Exception {// 将接收到的msg乘以2,然后返回给客户端count++;result = result.multiply(msg);ctx.writeAndFlush(result);}

客户端统计读取到的消息个数,如果消息个数=COUNT,说明计算完毕,就可以将结果保存起来供后续使用,其核心代码如下:

    public void channelRead0(ChannelHandlerContext ctx, final BigInteger msg) {receivedMessages ++;if (receivedMessages == CustomProtocolClient.COUNT) {// 计算完毕,将结果放入answer中ctx.channel().close().addListener(future -> {boolean offered = answer.offer(msg);assert offered;});}}

总结

本文实现了一个Number的编码解码器,事实上你可以自定义实现任何对象的编码解码器。

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

本文已收录于 http://www.flydean.com/13-netty-customprotocol/

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

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

netty系列之:自定义编码解码器相关推荐

  1. netty系列之:自定义编码和解码器要注意的问题

    文章目录 简介 自定义编码器和解码器的实现 ReplayingDecoder 总结 简介 在之前的系列文章中,我们提到了netty中的channel只接受ByteBuf类型的对象,如果不是ByteBu ...

  2. Netty 自定义编码解码器

    Netty 自定义编码解码器 入栈:InboundHandler ,出栈:OutboundHandler.Netty 构建 chain 来处理业务. 自定义一个解码器 解码器主要是对客户端发送的消息进 ...

  3. netty 系列之:netty 中常用的字符串编码解码器

    简介 字符串是我们程序中最常用到的消息格式,也是最简单的消息格式,但是正因为字符串 string 太过简单,不能附加更多的信息,所以在 netty 中选择的是使用 byteBuf 作为最底层的消息传递 ...

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

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

  5. java nio attachment_7. 彤哥说netty系列之Java NIO核心组件之Selector

    --日拱一卒,不期而至! 你好,我是彤哥,本篇是netty系列的第七篇. 简介 上一章我们一起学习了Java NIO的核心组件Buffer,它通常跟Channel一起使用,但是它们在网络IO中又该如何 ...

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

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

  7. 【Netty系列_3】Netty源码分析之服务端channel

    highlight: androidstudio 前言 学习源码要有十足的耐性!越是封装完美的框架,内部就越复杂,源码很深很长!不过要抓住要点分析,实在不行多看几遍,配合debug,去一窥优秀框架的精 ...

  8. Netty系列(三):说说NioEventLoop

    前言 本来想先写下NioServerSocketChannel以及NioSocketChannel的注册流程的,但是最后发现始终离不开NioEventLoop这个类,所以在这之前必须得先讲解下NioE ...

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

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

最新文章

  1. Docker核心技术之网络管理
  2. 思科OSPF辅助地址
  3. java恶作剧小程序_一个Java恶搞小程序
  4. .NET C/S(WinForm)开发技巧点滴(转)
  5. find 是区分大小写的。对于不区分大小写的写法(转载)
  6. android sim卡分析,Android 判断SIM卡属于哪个移动运营商详解及实例
  7. 一元线性回归中loss值的变化分析
  8. [RL] 配置 gym 与 atari 游戏
  9. VC++信息安全编程(14)PNP溢出代码漏洞扫描检测
  10. 如何用公众号关联认证小程序
  11. 数据库+PullToRefreshListView
  12. 智能科学与技术是不是属于计算机专业,智能科学与技术专业属于什么类别
  13. [渝粤教育] 信阳师范学院 视听语言 参考 资料
  14. 关于使用selenium+python简单获取前程无忧数据
  15. 新版迅雷与360浏览器
  16. 红外线测温仪解决方案开发
  17. ubuntu 连接显示器
  18. 什么时候该考虑线程安全问题 侵立删
  19. 如何root安卓手机_安卓虚拟大师 可以手机运行免root带xp框架模拟器
  20. xp系统安装sql 2000企业版,并打上sp4补丁

热门文章

  1. UVA12325Zombie's Treasure Chest 宝箱
  2. HDU2888(二维RMQ)
  3. Windows平台下Makefile学习笔记
  4. STL 之includes,set_intersection,set_union,set_difference,set_symmetric_difference
  5. 让互联网更快的协议,QUIC在腾讯的实践及性能优化
  6. 大规模微服务利器:eBPF + Kubernetes
  7. StackOverflow 上面最流行的 7 个 Java 问题!| 值得一看
  8. 高并发之API接口,分布式,防刷限流,如何做?
  9. RabbitMQ之监控(1)
  10. 175 道面试必考 Go 语言题目详细解答