io.netty学习(十三)Netty 解码器

目录

前言

编解码概述

编解码器概述

Netty 内嵌的编码器

解码器

ByteToMessageDecoder 抽象类

ReplayingDecoder 抽象类

MessageToMessageDecoder 抽象类

总结


前言

编码和解码:数据从一种特定协议格式到另一种格式的转换。

处理编码和解码的程序通常被称为编码器和解码器。Netty 提供了一些组件,利用它们可以很容易地为各种不同协议编写编解码器。

io.netty学习使用汇总

编解码概述

编解码其实可以分为两块,即编码和解码。要知道,在网络中数据都是以字节码的形式来传输的,而我们只能识别文本、图片这些格式,因此编写网络应用程序不可避免地需要操作字节,将我们能够识别的数据转换成网络能够识别的程序,这个过程称之为编解码。

编解码器概述

编码也称为序列化,它将对象序列化为字节数组,用于网络传输、数据持久化或者其他用途。

解码称为反序列化,它把从网络、磁盘等读取的字节数组还原成原始对象(通常是原始对象的拷贝),以方便后续的业务逻辑操作。

实现编解码功能的程序也被称为编解码器,编解码器的作用就是将原始字节数组与目标程序数据格式进行互转。

编解码器由两部分组成:解码器(decoder)和编码器(encoder)。

大家可以想象发送消息的这个过程。消息是一个结构化的应用程序中的数据。编码器转换消息格式为适合传输的数据格式,而相应的解码器是将传输数据转换回程序中的消息格式。逻辑上来说,转换消息格式为适合传输的数据格式是当作操作出站(outbound)数据,而将传输数据转换回程序中的消息格式是处理入站(inbound)数据。

Netty 内嵌的编码器

Netty 内嵌了众多的编解码器来简化开发。下图展示了Netty 的内嵌编解码器。

 可以看出,Netty 的内嵌编解码器基本上囊括了网络编程中可能需要涉及的编解码工作,包括以下内容:

  • 支持字节与消息的转换、Base64的转换、解压缩文件。

  • HTTPHTTP2DNSSMTPSTOMPMQTTSocks等协议的支持。

  • XMLJSON 、Redis、 MemcachedProtobuf等流行格式的支持。

编码器和解码器的结构很简单,消息被编码、解码后自动通过 ReferenceCountUtil.release(message)释放。如果不想释放消息可以使用ReferenceCountUtil.retain(message),主要区别是retain会使引用数量增加而不会发生消息,大多数时候不需要这么做。

解码器

解码器的主要职责是负责将入站数据从一种格式转换到另一种格式。Netty 提供了丰富的解码器抽象基类。方便开发者自定义解码器。

这些基类主要分为以下两类:

  • 解码从字节到消息(ByteToMessageDecoder 和 ReplayingDecoder)。

  • 解码从消息到消息(MessageToMessageDecoder)。

Netty 的解码器是ChannelInboundHandler的抽象实现。在实际应用中使用解码器很简单,就是将入站数据转换格式后传递到ChannelPipeline中的下一个ChannelInboundHandler进行处理。将解码器放在ChannelPipeline中,会使整个程序变得灵活,同时也能方便重用逻辑。

ByteToMessageDecoder 抽象类

ByteToMessageDecoder 抽象类用于将字节转为消息(或其他字节序列)。ByteToMessageDecoder 继承自ChannelInboundHandlerAdapterChannelInboundHandlerAdapter以类似流的方法将字节从ByteBuf解码为另一种消息类型。

1、常用方法

在处理网络数据时,有时数据比较大,不能一次性发送完毕,会分配发送。那么又如何获知数据已经发送完毕了呢?这个ByteToMessageDecoder抽象类会缓存入站的数据,并提供了以下几个方法,方便开发者使用。

这些方法的核心源码如下:

protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {if (in.isReadable()) {// Only call decode() if there is something left in the buffer to decode.// See https://github.com/netty/netty/issues/4386decodeRemovalReentryProtection(ctx, in, out);}
}final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)throws Exception {decodeState = STATE_CALLING_CHILD_DECODE;try {decode(ctx, in, out);} finally {boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING;decodeState = STATE_INIT;if (removePending) {fireChannelRead(ctx, out, out.size());out.clear();handlerRemoved(ctx);}}
}

对上述方法说明如下:

  • decode():这是必须要实现的唯一抽象方法。decode()方法被调用时将会传入一个包含了传入数据的ByteBuf,以及一个用来添加解码消息的List对这个方法的调用将会重复执行,直到确定没有新的元素被添加到该List,或者该ByteBuf中没有更多可读的字节时为止。然后,如果List不为空,那么它的内容将会被传递给ChannelPipeline中的下一个ChannelInboundHandler

  • decodeLast():Netty 提供的这个默认实现只是简单地调用了decode()方法。当Channel的状态变为非活泼时,这个方法会被调用一次。可以重写该方法以提供特殊的处理。

2、将字节转为整形的解码器示例

该示例中,每次从入站的ByteBuf读取4个字节,解码成整形,并添加到一个List中。当不能再添加数据的List时,它所包含的内容就会被发送到下一个ChannelInboundHandler

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

在上述代码中,步骤如下:

  1. 实现了继承ByteToMessageDecoder,用于将字节解码为消息。

  2. 检查可读的字节是否至少有 4 个(一个 int 是4个字节长度)。

  3. 从入站的ByteBuf读取 int ,添加到解码消息的List中。

整个例子的处理流程如下图所示:

 对于编码器和解码器来说,整个过程非常简单。一旦一个消息被编码或者解码,它自动被ReferenceCountUtil.release(message)调用。如果不想释放消息可以使用ReferenceCountUtil.retain(message)

ReplayingDecoder 抽象类

ReplayingDecoder抽象类是ByteToMessageDecoder的一个子类,ByteToMessageDecoder解码读取缓冲区的数据之前需要检查缓冲区是否有足够的字节,使用ReplayingDecoder就无需自己检查;若ByteBuf中有足够的字节,则会正常读取;若没有足够的字节则会停止解码。

也正因为这样的包装使得ReplayingDecode带有一定的局限性。

  • 不是所有的标准ByteBuf操作都被支持,如果调用一个不支持的操作会抛出UnReplayableOperationException

  • 性能上,使用ReplayingDecode要略慢与ByteToMessageDecoder

如果你能忍受上面列出的限制,相比ByteToMessageDecoder,你可能更喜欢ReplayingDecoder。在满足需求的情况下推荐使用ByteToMessageDecoder,因为它的处理比较简单,没有ReplayingDecoder实现的那么复杂。

下面代码是ReplayingDecoder的实现:

/*** Integer解码器,ReplayingDecoder实现*/
public class ToIntegerReplayingDecoder extends ReplayingDecoder<Void> {@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {out.add(in.readInt());}
}

MessageToMessageDecoder 抽象类

MessageToMessageDecoder 抽象类用于从一种消息解码为另外一种消息。

核心源码如下:

public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter {private final TypeParameterMatcher matcher;protected MessageToMessageDecoder() {matcher = TypeParameterMatcher.find(this, MessageToMessageDecoder.class, "I");}protected MessageToMessageDecoder(Class<? extends I> inboundMessageType) {matcher = TypeParameterMatcher.get(inboundMessageType);}public boolean acceptInboundMessage(Object msg) throws Exception {return matcher.match(msg);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {CodecOutputList out = CodecOutputList.newInstance();try {if (acceptInboundMessage(msg)) {@SuppressWarnings("unchecked")I cast = (I) msg;try {decode(ctx, cast, out);} finally {ReferenceCountUtil.release(cast);}} else {out.add(msg);}} catch (DecoderException e) {throw e;} catch (Exception e) {throw new DecoderException(e);} finally {try {int size = out.size();for (int i = 0; i < size; i++) {ctx.fireChannelRead(out.getUnsafe(i));}} finally {out.recycle();}}}protected abstract void decode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;
}

MessageToMessageDecoder decode是需要实现的唯一抽象方法。每个入站消息都被解码为另外一种格式,然后将解码后的消息传递给管道中的下一个ChannelInboundHandler

以下是一个MessageToMessageDecoder 的使用示例:

public class IntegerToStringDecoder extends MessageToMessageDecoder<Integer> {@Overridepublic void decode(ChannelHandlerContext ctx, Integer msg List<Object> out) throws Exception {out.add(String.valueOf(msg));}
}

上述代码中,IntegerToStringDecoder继承自MessageToMessageDecoder,用于将 Integer 转为 String。分为两步:

  • IntegerToStringDecoder继承自MessageToMessageDecoder

  • 通过tring.valueOf()转换 Integer 消息的字符串。

入站消息是按照在类定义中声明的参数(这里是Integer)而不是ByteBuf来解析的。在例子中,解码消息(这里是String)将被添加到List<Object>,并传递到下一个ChannelInboundHandler

整个例子的处理流程图如下:

总结

上述我们重点讲解了 Netty 中的解码器相关知识。下节我们就来讲解一下编码器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/3950.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uin-app项目实现pdf文件预览以及下载

由于项目需要&#xff0c;需要对于pdf格式的文件进行预览由用户进行选择性下载&#xff0c;查阅相关文档后方知针对于这种 pdf.js有奇效 一、下载 官网地址https://mozilla.github.io/pdf.js/getting_started/#download 文档下载解压成功后&#xff0c;按照这种格式放入uin-…

计算机基本组成和冯诺依曼机

计算机基本组成和冯诺依曼机 计算机的基本组成 计算机硬件组成 软件与硬件的逻辑等价性 冯诺依曼计算机硬件结构 冯诺依曼计算机工作原理 程序存储控制原理 计算机采用二进制的优势 高电平与低电平电压波动受影响的可能性会降低&#xff0c;抗干扰能力强 什么是冯诺依曼计算机…

DBeaver连接GaussDB

DBeaver 官网&#xff1a;https://dbeaver.io/打开DBeaver&#xff0c;点击菜单栏 “数据库”>“驱动管理” 点击“新建” 填入下面内容&#xff1a; 驱动名称&#xff1a;GS 驱动类型&#xff1a;Generic 类名&#xff1a;org.postgresql.Driver URL模板&#xff1a;jdbc…

在pycharm上导出Anaconda3的环境配置文件

目录 1.原理&#xff1a; ​2.亲身实践&#xff1a; 1.原理&#xff1a; 要在PyCharm中导出Anaconda3环境的配置文件&#xff0c;可以使用conda命令行工具来完成。请按照以下步骤进行操作&#xff1a; 打开PyCharm&#xff0c;并确保项目使用的是Anaconda3环境。 在PyCha…

Linux训练营(文件和目录操作)

文章目录 前言一、ls命令二、cd命令三、mkdir命令四、cp命令五、rm命令总结 前言 本篇文章我们来讲解Linux中的文件和目录操作&#xff0c;在这里我们主要使用的是Linux中的命令来操作这些文件和目录&#xff0c;命令是Linux中最基础的部分。 一、ls命令 ls是一个常用的命令…

css基础知识十一:CSS3新增了哪些新特性?

一、是什么 css&#xff0c;即层叠样式表&#xff08;Cascading Style Sheets&#xff09;的简称&#xff0c;是一种标记语言&#xff0c;由浏览器解释执行用来使页面变得更为美观 css3是css的最新标准&#xff0c;是向后兼容的&#xff0c;CSS1/2的特性在CSS3 里都是可以使用…

SpringBoot源码分析(三):SpringBoot的事件分发机制

文章目录 通过源码明晰的几个问题Spring 中的事件Springboot 是怎么做到事件监听的另外两种注册的Listener 源码解析加载listenerSpringApplicationRunListenerEventPublishingRunListenerSimpleApplicationEventMulticaster判断 listener 是否可以接收事件Java 泛型获取 整体流…

解决 mac 系统报zsh: command not found: npm 问题

文章目录 1、报错zsh: command not found: npm2、解决办法 1、报错zsh: command not found: npm 根据提示&#xff1a;zsh: command not found: npm。说明没有找到 npm 命令&#xff0c;这说明有两种情况&#xff1a; 一是&#xff1a;你根本就没有安装 nodejs 的环境&#xf…

轻量服务器带宽流量和云服务器带宽流量有什么区别?

轻量服务器带宽流量和云服务器带宽流量有什么区别?虽然轻量服务器是轻量化云服务器&#xff0c;但与云服务器的差别还是有一些的&#xff0c;比如这令很多人好奇的轻量服务器带宽和流量和云服务器的区别在哪。下面我们就仔细聊聊关于轻量服务器和云服务器各自的带宽流量差异&a…

JetBrains goland、pycharm、webstorm、phpstorm 对比两文件内容是否一致

对比文件 JetBrains goland、pycharm、webstorm、phpstorm 对比两文件内容是否一致 第一种 打开文件&#xff0c;按住键盘上的CTRL键&#xff0c;然后鼠标右键&#xff0c;点击菜单中的”Compare with Clipboard”&#xff0c;左侧就可以粘贴文件内容对比 第二种 在编辑器窗口中…

【国产虚拟仪器】基于ZYNQ7045+V7 FPGA的多通道数据同步采集设计方案(一)

多通道数据采集设备在当前信息数字化的时代应用广泛&#xff0c;各种被测量的信息 如光线、温度、压力、湿度、位置等&#xff0c;都需要经过多通道信号采集系统的采样和 处理&#xff0c;才能被我们进一步分析利用[37]。在一些对采集速率要求较高的军事、航天、 航空、工业制造…

Python基本操作

前言 啦啦啦&#xff0c;现在开始,打算做一期Python基础教程&#xff0c;欢迎大家来看哦&#xff01; 导读 这期文章真的是Python基础中的基础&#xff0c;相信有一定编程基础的小伙伴们都一定能看懂的… 本文共分为以下几个部分&#xff1a; 数与运算符基本输入输出注释模…