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

news2024/11/24 8:32:13

目录

前言

编解码概述

编解码器概述

Netty 内嵌的编码器

解码器

ByteToMessageDecoder 抽象类

ReplayingDecoder 抽象类

MessageToMessageDecoder 抽象类

总结


前言

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

处理编码和解码的程序通常被称为编码器和解码器。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/4386
        decodeRemovalReentryProtection(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 {
    @Override
   public 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> {

    @Override
    protected 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);
    }

    @Override
    public 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> {
    @Override
    public 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.coloradmin.cn/o/685303.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【VulnHub系列】BrokenGallery

因为是从PDF转换过来偶尔可能会出现内容缺少&#xff0c;可以看原版PDF&#xff1a;有道云笔记 实验信息 Broken&#xff1a;192.168.10.111 Kali&#xff1a;192.168.10.106 实验过程 sudo arp-scan --interface eth0 192.168.10.1/24 然后对靶机进行端口探测 nmap -sT -…

定积分的应用—所围图形的面积、绕轴旋转所围成立体的体积、旋转曲面的面积、弧长

本篇本章&#xff0c;将从几个简单的例子带大家分析总结定积分的应用中常用的方法和思想&#xff0c;一起学习进入定积分的世界&#x1f61c;&#x1f61c; 一、求所围图形的面积 1.求由抛物线 y x 2 与 y 2 − x 2 所 围 图 形 的 面 积 yx^2与y2-x^2所围图形的面积 yx2与y2…

Windows下Android studio 搭建 android NDK 搭建 OLLVM 踩坑记录

1. 编译 ollvm-9.0.1 下载源码进行编译 https://github.com/heroims/obfuscator/tree/llvm-9.0.1 编译 这里有坑要注意 不能使用最新的 mingw 8.0.1 编译会报错 报错内容如下: PS D:\OLLVM\obfuscator-llvm-9.0.1\build> cmake -G "MinGW Makefiles" -DCMA…

浏览器 HTTPS 协议的相关知识点有哪些?

&#x1f482; 个人网站:【海拥】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言HTTPS协议的概念HT…

腾讯云私有云平台运维面试

文章目录 概述JD 岗位描述一面二面三面HR面 概述 根据会议将面试问题进行总结&#xff0c;很多问题感觉当时没回答好&#xff0c;这是为啥呢&#xff1f;应该还是不熟练吧&#xff0c;或者不善于表达。将次经历分享出来&#xff0c;大家多练练。 JD 岗位描述 私有云平台运维…

io.netty学习(十四)Netty 编码器

目录 前言 MessageToByteEncoder 抽象类 MessageToMessageEncoder 抽象类 总结 前言 上一篇我们讲解了解码器的相关知识&#xff0c;其中也提到了编码器的定义。 编码器就是用来把出站&#xff08;针对本身来讲&#xff0c;发送都是出站&#xff0c;接收都是入站&#xf…

TIA博途_字符转换为字符串以及截取字符串有效字符的具体方法示例

TIA博途_字符转换为字符串以及截取字符串有效字符的具体方法示例 情景再现: 在做某些通信相关的项目时,可能会遇到通信接收到的字符串是以字节的形式传送过来的字符,此时就需要我们对接收到的这些字符进行处理,从而获取我们实际需要的字符或字符串。 如下图所示,打开博途…

一文了解什么是Dubbo

Dubbo学习 Dubbo应用架构的演变RPC&#xff08;远程过程调用&#xff09;RPC介绍RPC组件RPC调用 Apache Dubbo概述Dubbo简介Dubbo的架构 服务注册中心ZookeeperZookeeper介绍运行zookeeper Dubbo的快速开发Dubbo管理控制台治理中心 Dubbo Dubbo是一个分布式服务框架&#xff0…

基于Servlet+JDBC实现的基础博客系统>>系列3 -- Servlet后端服务器搭建

目录 前言 1. 前期准备 2. Model层 2.1 数据库的设计 2.2 数据库表对应的实体类实现 User类 Blog类 2.3 JDBC 工具类实现 2.4 UserDao 的实现 2.5 BlogDao 的实现 3. Controller 层实现 3.1 博客列表页 3.1.1 约定前后端交互接口 3.1.2 编写后端代码 3.1.3 编写…

【Shell1】shell语法,ssh/build/scp/upgrade,环境变量

文章目录 1.shell语法&#xff1a;shell是用C语言编写的程序&#xff0c;是用户使用Linux的桥梁&#xff0c;硬件>内核(os)>shell>文件系统1.1 变量&#xff1a;readonly定义只读变量&#xff0c;unset删除变量1.2 函数&#xff1a;shell脚本传递的参数中包含空格&…

聊天GPT如何运作?| 景联文科技

什么是聊天 GPT&#xff1f; Chat GPT 被定义为一种生成语言模型。在实践中&#xff0c;它被理解为经过训练和设计以进行自然对话的人工智能聊天。 聊天 GPT 的用途是什么&#xff1f; 1.借助 GPT&#xff0c;您可以生成各种风格、主题和语言的连贯且写得很好的文本。此外&a…

【比赛writeup】2023省赛-Reverse-Re2

2023省赛-Reverse-Re2 一、概要 1、标题&#xff1a;Re2 2、关键字&#xff1a;换表BASE64 3、比赛&#xff1a;2023省赛 4、工具&#xff1a;IDAx64、python 二、开始 1、题目分析 逆向的题目&#xff0c;找到关键字符串&#xff0c;找到关键函数&#xff0c;分析函数逻辑…

ISO15765-2 网络层,通俗易懂,最佳入门

I. 简介 ISO15765-2协议网络 ISO15765-2协议网络是一种用于汽车电子系统中的通信协议。它定义了在控制区域网络&#xff08;CAN&#xff09;上使用的诊断通信的协议规范&#xff0c;包括物理层、数据链路层、网络层和传输层。该协议的主要目的是使汽车制造商和维修技术人员能够…

Vue2,Vue3 computed计算属性用法

计算属性就是当依赖的属性的值发生变化的时候&#xff0c;才会触发他的更改&#xff0c;如果依赖的值&#xff0c;不发生变化的时候&#xff0c;使用的是缓存中的属性值。 computed购物车案例 1.Vue2版 <template><div><div><input v-model"keywo…

linux系统Nginx网站服务

文章目录 一、Nginx简介二、Nginx 相对于 Apache 的优点三、nginx 应用场景1.同步与异步2.阻塞与非阻塞 四、Nginx安装及运行控制1、编译安装2、访问控制1、访问状态统计2、基于授权的访问控制3、基于客户端的访问控制4、基于域名的 Nginx 虚拟主机5、基于IP 的 Nginx 虚拟主机…

2.3ORBSLAM3之相机模型与畸变模型

1.简介 主要内容&#xff1a; 1. 对SLAM中常见的相机模型进行介绍&#xff0c;包括针孔相机模型和鱼眼相机模型 2. 对每种相机模型的畸变模型进行介绍 3. 对VSLAM中常见的几种去畸变方法进行介绍 4. 对常见的几种相机标定方法进行总结对于VSLAM来说关于相机投影模型和畸变模型…

Mybatis如何解决循环依赖问题

1、Mybatis如何解决循环依赖问题 mybatis的循环依赖&#xff0c;即是mapper.xml里面的A查询的resultMap包含了B属性&#xff08;B属性是通过子查询得到的&#xff09;&#xff0c;而B属性中又包含了A&#xff08;B查询的resultMap中又包含了A的查询&#xff09;&#xff0c;就…

【系统架构】第五章-软件工程基础知识(软件测试、净室软件工程、基于构件的软件工程、 软件项目管理)

软考-系统架构设计师知识点提炼-系统架构设计师教程&#xff08;第2版&#xff09; 软件测试 一、测试方法 以测试过程中程序执行状态为依据可分为静态测试&#xff08;ST&#xff09;和动态测试&#xff08;DT&#xff09; 以具体实现算法细节和系统内部结构的相关情况为根据…

【Leetcode60天带刷】day35——452. 用最少数量的箭引爆气球,435. 无重叠区间,763.划分字母区间

​ 题目&#xff1a; 452. 用最少数量的箭引爆气球 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中points[i] [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。 一支弓箭可以沿着…