1. ByteToMessageDecoder解码器

是一个抽象类,仅提供了一个框架,继承关系如下:

image-20220511205449542

解码流程:

实现一个自己的ByteBuf解码器:

  1. 继承ByteToMessageDecoder抽象类
  2. 实现基类的decode抽象方法。将ByteBuf到POJO解码的逻辑写入此方法。将ByteBuf二进制数据,解码成一个个的Java对象。
  3. 在子类的decode方法中,需要将解码后的Java POJO对象,放入decode的List< Object >实参中,一个个的传递到下一站的Inbound入站处理器。
public class Byte2IntegerDecoder extends ByteToMessageDecoder {@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {while (in.readableBytes() >= 4) {int i = in.readInt();System.out.println("解码出一个整数: " + i);out.add(i);}}
}public class IntegerProcessHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext context, Object msg) throws Exception {Integer integer = (Integer) msg;System.out.println("打印出一个整数: " + integer);}
}@Test
public void byteDecoderTest() {ChannelInitializer initializer = new ChannelInitializer() {@Overrideprotected void initChannel(Channel ch) throws Exception {// 入站处理器,从前向后的顺序执行ch.pipeline().addLast(new Byte2IntegerDecoder());ch.pipeline().addLast(new IntegerProcessHandler());}};EmbeddedChannel channel = new EmbeddedChannel(initializer);for (int i = 0; i < 100; i++) {ByteBuf buf = Unpooled.buffer();buf.writeInt(i);channel.writeInbound(buf);}try {Thread.sleep(Integer.MAX_VALUE);} catch (Exception e) {e.printStackTrace();}
}

2. ReplayingDecoder解码器

上面使用Byte2IntegerDecoder解码器会有一个问题:需要对ByteBuf长度进行检查,如果有足够的字节,才进行整数的读取。

使用ReplayingDecoder解码器可以省去长度的判断。

ReplayingDecoder类是ByteToMessageDecoder的子类。作用如下:

  • 在读取ByteBuf缓冲区的数据之前,需要检查缓冲区是否有足够的字节。
  • 若BytrBuf中有足够的字节,则会正常读取。否则,会停止解码。

原理如下:

在其内部,定义了一个新的二进制缓冲区类,对ByteBuf缓冲区进行了装饰,类名为ReplayingDecoderByteBuf。特点是:在缓冲区真正读取数据之前,首先进行长度的判断,如果长度合格,则读取数据;否则,抛出ReplayError。ReplayingDecoder捕获到ReplayError后,会留着数据,等待下一次IO事件到来时再获取。

ReplayingDecoderByteBuf继承了ByteBuf,所以该解码器实际上是把ByteBuf里面的数据转换成了ReplayingDecoderByteBuf,增强了对二进制数据的判断,长度不足就抛出异常

可以通过ReplayingDecoder分包ByteBuf,但也有缺点:

当ByteBuf中长度不够时,ReplayingDecoder会捕获异常,这时会把ByteBuf中的读指针还原到之前的读断点指针,然后结束这次解析操作,这需要消耗CPU。

2.1 整数的分包传输

public class IntegerAddDecoder extends ReplayingDecoder<IntegerAddDecoder.Status> {enum Status {PARSE_1, PARSE_2}private Integer first;private Integer second;public IntegerAddDecoder() {//构造函数中,需要初始化父类的state 属性,表示当前阶段super(Status.PARSE_1);}@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {switch (state()) {case PARSE_1:// 从ByteBuf中读取数据first = in.readInt();System.out.println("first = " + first);// 进入第二步,设置读指针断点为当前读取位置checkpoint(Status.PARSE_2);break;case PARSE_2:second = in.readInt();System.out.println("second = " + second);Integer sum = first + second;System.out.println("sum = " + sum);out.add(sum);checkpoint(Status.PARSE_1);break;}}}@Test
public void testIntegerAddDecoder() {ChannelInitializer i = new ChannelInitializer<EmbeddedChannel>() {protected void initChannel(EmbeddedChannel ch) {// 先解码ch.pipeline().addLast(new IntegerAddDecoder());// 再输出ch.pipeline().addLast(new IntegerProcessHandler());}};EmbeddedChannel channel = new EmbeddedChannel(i);for (int j = 0; j < 100; j++) {ByteBuf buf = Unpooled.buffer();buf.writeInt(j);channel.writeInbound(buf);}try {Thread.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}
}
// 可以看到,最终输出的只有sum值,输入的数字只有经历一个完整Status才会输出

checkpoint(status)方法的作用:

  • 设置state属性的值,更新当前的状态
  • 设置读断点指针

读断点指针保存着装饰器内部ReplayingDecoderBuffer成员的起始读指针。当读数据时,如果可读数据不够,ReplayingDecoderBuffer在抛出ReplayError异常之前,还会把读指针的值还原到之前的checkpoint方法设置的读断点指针。所以,在ReplayingDecoder下一次读取时,还会从之前设置的断点位置开始。

所以ReplayingDecoder类型和所有子类都需要保存状态信息,都有状态的,都不适合在不同的通道之间共享。

2.2 字符串的分包传输

public class StringReplayDecoder extends ReplayingDecoder<StringReplayDecoder.Status> {enum Status {PARSE_1, PARSE_2}private int length;private byte[] inBytes;public StringReplayDecoder() {//构造函数中,需要初始化父类的state 属性,表示当前阶段super(Status.PARSE_1);}@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in,List<Object> out) throws Exception {switch (state()) {case PARSE_1://第一步,从装饰器ByteBuf 中读取长度length = in.readInt();inBytes = new byte[length];// 进入第二步,读取内容// 并且设置“读指针断点”为当前的读取位置checkpoint(Status.PARSE_2);break;case PARSE_2://第二步,从装饰器ByteBuf 中读取内容数组in.readBytes(inBytes, 0, length);out.add(new String(inBytes, StandardCharsets.UTF_8));// 第二步解析成功,// 进入第一步,读取下一个字符串的长度// 并且设置“读指针断点”为当前的读取位置checkpoint(Status.PARSE_1);break;default:break;}}
}public class StringProcessHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {String s = (String) msg;System.out.println("打印: " + s);}
}@Test
public void testStringReplayDecoder() {ChannelInitializer i = new ChannelInitializer<EmbeddedChannel>() {protected void initChannel(EmbeddedChannel ch) {ch.pipeline().addLast(new StringReplayDecoder());ch.pipeline().addLast(new StringProcessHandler());}};EmbeddedChannel channel = new EmbeddedChannel(i);byte[] bytes = content.getBytes(StandardCharsets.UTF_8);for (int j = 0; j < 100; j++) {//1-3之间的随机数Random randoms = new Random();// 复制几次int random = Math.abs(randoms.nextInt(3)) + 1;ByteBuf buf = Unpooled.buffer();// 总长度buf.writeInt(bytes.length * random);for (int k = 0; k < random; k++) {buf.writeBytes(bytes);}channel.writeInbound(buf);}try {Thread.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}
}

3. MessageToMessageDecoder解码器

作用:可以将一种POJO对象解码成另一种POJO对象。

需要继承MessageToMessageDecoder< T >,明确泛型T,这个实参的作用就是指定入站消息的Java POJO类型。

// 一个简单的实例
public class Integer2StringDecoder extends MessageToMessageDecoder<Integer> {@Overrideprotected void decode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {// 实参为Integer,明确了入站类型为Integer,然后在解码时将Integer转为字符串,发送给下一个入站处理器out.add(String.valueOf(msg));}
}@Test
public void testIntegerToStringDecoder() {ChannelInitializer i = new ChannelInitializer<EmbeddedChannel>() {protected void initChannel(EmbeddedChannel ch) {// 二进制到Integer处理器ch.pipeline().addLast(new Byte2IntegerDecoder());// Integer到String处理器ch.pipeline().addLast(new Integer2StringDecoder());ch.pipeline().addLast(new StringProcessHandler());}};EmbeddedChannel channel = new EmbeddedChannel(i);for (int j = 0; j < 100; j++) {ByteBuf buf = Unpooled.buffer();buf.writeInt(j);channel.writeInbound(buf);}try {Thread.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}
}

Decoder原理和浅解相关推荐

  1. 跳帧的计算机原理,光电鼠标基础知识浅解(22页)-原创力文档

    光电鼠标基础知识浅解(1) 内容概要 关键词:光电 鼠标 导言:介绍光电鼠标工作的基本原理及构成部件,作一般性知识了解 光电鼠标的工作原理与参数 光电鼠标的内部构成 光电鼠标的外部设计 讨论 与传统的 ...

  2. 白加黑加载方式_“白加黑减”即曝光补偿的应用方法及原理全面详解——致新手新新手...

    "白加黑减"即曝光补偿的应用方法及原理全面详解 --致新手新新手 本文对曝光补偿这一摄影基本技术及原理做一全面详细的分析和解释,旨在让受此困惑的新手能够从原理上彻底理解" ...

  3. 从最大似然到EM算法浅解 http://blog.csdn.net/zouxy09/article/details/8537620

    1. EM blog的举例就是group 然后就是每个group的function很有效地串联所学的知识,看到的论文,所有的思考,都是有一定的逻辑关系,如何逐渐develop你的想法,都是有一定的源头 ...

  4. 实践浅解free的具体作用(C语言)

    实践浅解free的具体作用 实践free()在C语言中的作用 一.问题背景 二.实践 (一).实践代码 (二).gcc version 9.2.0编译运行结果 (三).Dev C++中TDM-GCC ...

  5. (转载)机器学习知识点(十五)从最大似然到EM算法浅解

    从最大似然到EM算法浅解 机器学习十大算法之一:EM算法.能评得上十大之一,让人听起来觉得挺NB的.什么是NB啊,我们一般说某个人很NB,是因为他能解决一些别人解决不了的问题.神为什么是神,因为神能做 ...

  6. [crypto]-02-非对称加解密RSA原理概念详解

    说明:本文使用的数据来自网络,重复的太多了,也不知道哪篇是原创. 算法原理介绍 step 说明 描述 备注 1 找出质数 P .Q - 2 计算公共模数 N = P * Q - 3 欧拉函数 φ(N) ...

  7. [crypto]-01-对称加解密AES原理概念详解

    1.对称加解密 术语:P是明文,C是密文,K是密钥,E是加密算法,D是解密算 (1).常用的对称加解密有哪些? (2).加解密的模式 [ecb]这种模式是将整个明文分成若干段相同的小段,然后对每一小段 ...

  8. 4个mos管驱动的全桥电路原理_最经典MOS管电路工作原理及详解没有之一

    欢迎加入技术交流QQ群(2000人):电力电子技术与新能源 1105621549 高可靠新能源行业顶尖自媒体 在这里有电力电子.新能源干货.行业发展趋势分析.最新产品介绍.众多技术达人与您分享经验,欢 ...

  9. php dom xml解析,Php Xml解析之DOMDocument使用方法浅解

    Php Xml解析之DOMDocument使用方法浅解 用到的XML文件还以"Php Xml处理之simplexml使用方法浅谈"一文中的XML为例,文件名为:me.xml.代码如 ...

  10. elisa数据处理过程图解_ELISA原理示意图详解.ppt

    ELISA原理示意图详解.ppt 免疫酶技术及其应用--ELISA 一.实验目的 了解和掌握免疫酶技术的测定原理. 掌握酶联免疫吸附测定技术的操作步骤,学会利用竞争ELISA的方法,定量测定抗体或抗原 ...

最新文章

  1. 移远NB-IOT BC28模块模组简介和实际应用方向详解
  2. python硬件驱动_Python学习:计算机基础之计算机硬件
  3. .net每隔几秒去请求接口 怎么做_C# .net 中 Timeout 的处理及遇到的问题
  4. Java自定义Exception
  5. Qt--在.pro文件中添加链接库的写法
  6. defaultView与currentStyle的区别_获取CSS样式值
  7. 1个鼠标和1个键盘控制2台电脑(windows和linux系统)
  8. 三菱Q系列plc串口通讯四台台达变频器通讯程序
  9. 乔姆斯基生成语法_乔姆斯基(乔姆斯基转换生成语法理论)
  10. 面试中面试官问的一些问题总结
  11. 网易MuMu模拟器连接不上Android Studio
  12. 安卓应用方法数超过64k解决办法:分割Dex
  13. API day02 IO流
  14. 蓝牙3.0/4.0/5.0联系与区别
  15. Windows 11快捷键功能大全 28个Windows 11快捷键功能介绍
  16. intel第6代服务器芯片,Intel第六代处理器 Skylake CPU、GPU、主板完全解析
  17. 计算机学院档案馆教育部,我系本科生团队参加全国高校档案学专业大学生创新性课外科技作品展...
  18. Fiddler 基本使用及自动化测试
  19. php中文汉字与16进制编码转换三种方法
  20. 城管视频ai智能分析系统

热门文章

  1. 一个简单的连续变焦红外镜头的从零开始的设计过程(zemax)(二)进一步优化,公差分析
  2. SEO是什么?前端如何进行SEO优化
  3. 银河麒麟V10 远程桌面
  4. Kindle使用的一些方法
  5. android flash插件下载地址,adobe flash player
  6. 李沐的动手学深度学习环境配置
  7. android 圆形头像方案,android圆形头像实现
  8. Toony Colors Pro 2项目分析——身体其他部位shader
  9. Apache NiFi简介
  10. 小白运维linux命令总结,linux常用运维命令总结