【Netty】Netty 解码器(十二)

news2024/11/27 5:30:44

文章目录

  • 前言
  • 一、编解码概述
    • 1.1、编解码器概述
    • 1.2、Netty 内嵌的编码器
  • 二、解码器
    • 2.1、ByteToMessageDecoder 抽象类
      • 2.1.1、常用方法
      • 2.1.2、将字节转为整形的解码器示例
  • 三、ReplayingDecoder 抽象类
  • 四、MessageToMessageDecoder 抽象类
  • 总结

前言

回顾Netty系列文章:

  • Netty 概述(一)
  • Netty 架构设计(二)
  • Netty Channel 概述(三)
  • Netty ChannelHandler(四)
  • ChannelPipeline源码分析(五)
  • 字节缓冲区 ByteBuf (六)(上)
  • 字节缓冲区 ByteBuf(七)(下)
  • Netty 如何实现零拷贝(八)
  • Netty 程序引导类(九)
  • Reactor 模型(十)
  • 工作原理详解(十一)

编码和解码:数据从一种特定协议格式到另一种格式的转换。
处理编码和解码的程序通常被称为编码器和解码器。Netty 提供了一些组件,利用它们可以很容易地为各种不同协议编写编解码器。

一、编解码概述

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

1.1、编解码器概述

编码也称为序列化,它将对象序列化为字节数组,用于网络传输、数据持久化或者其他用途。
解码称为反序列化,它把从网络、磁盘等读取的字节数组还原成原始对象(通常是原始对象的拷贝),以方便后续的业务逻辑操作。

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

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

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

1.2、Netty 内嵌的编码器

Netty 内嵌了众多的编解码器来简化开发。下图展示了Netty 的内嵌编解码器。
在这里插入图片描述

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

  • 支持字节与消息的转换、Base64的转换、解压缩文件。
  • 对HTTP、HTTP2、DNS、SMTP、STOMP、MQTT、Socks等协议的支持。
  • -对XML、JSON 、Redis、 Memcached、Protobuf等流行格式的支持。

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

二、解码器

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

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

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

2.1、ByteToMessageDecoder 抽象类

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

2.1.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.1.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/573389.html

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

相关文章

K-Means算法实现鸢尾花数据集聚类

目录 1. 作者介绍2. K-Means聚类算法2.1 基本概念2.2 算法流程 3. K-Means聚类算法实现3.1 鸢尾花数据集3.2 准备工作3.3 代码实现3.4 结果展示 4. 问题与解析参考链接 1. 作者介绍 张勇&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2022级研究生 研究方向…

第3章“程序的机器级表示”:算术和逻辑操作

文章目录 3.5 算术和逻辑操作3.5.1 加载有效地址3.5.2 一元和二元操作3.5.3 移位操作3.5.4 讨论3.5.5 特殊的算术操作 3.5 算术和逻辑操作 下图列出了一些双字整数操作&#xff0c;分为四类。 二元操作有两个操作数&#xff0c;而一元操作只有一个操作数。 描述这些操作数的…

极光笔记 | EngageLab Push的多时区解决方案

01、引言 多时区问题一直是全球客户和终端用户面临的挑战之一。EngageLab Push 致力于解决这个问题&#xff0c;确保全球各地的终端用户可以平等地享受到同样的推送服务&#xff0c;同时让客户能够更好地管理不同时区的应用和对应的终端用户。 02、解决多时区问题的总体方案 1…

软件测试----软件开发模型

1、瀑布模型 &#xff08;1&#xff09;瀑布模型如下 &#xff08;2&#xff09;瀑布模型的缺点&#xff1a; 在瀑布模型中&#xff0c;测试是在编码结束后才介入&#xff0c;对软件开发流程前期质量是没有保障的 &#xff08;3&#xff09;采用瀑布模型的场景&#xff1a; …

31 KVM管理系统资源-管理虚拟内存NUMA

文章目录 31 KVM管理系统资源-管理虚拟内存NUMA31.1 NUMA简介31.2 配置Host-NUMA操作步骤 31.3 配置Guest-NUMA操作步骤 31 KVM管理系统资源-管理虚拟内存NUMA 31.1 NUMA简介 传统的多核运算使用SMP&#xff08;Symmetric Multi-Processor&#xff09;模式&#xff1a;将多个…

Flume系列:Flume 自定义Interceptor拦截器

目录 Apache Hadoop生态-目录汇总-持续更新 1&#xff1a;Interceptor拦截器的使用场景 2&#xff1a;Interceptor拦截器在采集流程中的位置 3&#xff1a;自定义Interceptor拦截器 pom.xml 拦截器java代码 打包上传 4&#xff1a;使用自定义的拦截器 方式一&#xff1…

Springboot +spring security,自定义认证和授权异常处理器

一.简介 在Spring Security中异常分为两种&#xff1a; AuthenticationException 认证异常AccessDeniedException 权限异常 我们先给大家演示下如何自定义异常处理器&#xff0c;然后再结合源码帮助大家进行分析 二.创建项目 如何创建一个SpringSecurity项目&#xff0c;前…

分布式锁和事务关系的细节

使用redssion在redis上以及结合自定义注解利用spring的环绕切面来实现分布式锁功能 代码示例 controller、service层 RequestMapping("insertNumber/{number}/{id}") public boolean insertNumber(PathVariable Long number,PathVariable Long id){return testSer…

rust 中protobuf生成与使用

首先创建一个项目proto 进入到这个文件夹中 创建我们的proto文件 初始化的项目结构是这个样子的 新建一个hello.proto文件内容如下 syntax "proto3";package hello;service Greeter {rpc SayHello (HelloRequest) returns (HelloReply) {} }message HelloRequest …

干货 | 师兄手把手教你如何踏上科研道路

Hello&#xff0c;大家好&#xff01; 这里是壹脑云科研圈&#xff0c;我是喵君姐姐&#xff5e; 今天&#xff0c;邀请到鲁小白&#xff0c;给大家分享一下他踏上科研道路的心路历程。 大家好&#xff0c;我是鲁小白&#xff0c;我真正进入科研的时间&#xff0c;研究生3年再…

【C++】类和对象——类的引入、类的访问限定符、类的作用域、类的实例化、类的储存、this指针的引出和特性

文章目录 1.类的引入2.类的访问限定符3.类的作用域4.类的实例化5.类的储存6.this指针6.1this指针的引出6.2this指针的特性 1.类的引入 C是在C的基础上加以扩展。 在C语言中&#xff0c;我们想要让一个类型含有多种成员变量&#xff0c;我们使用结构体&#xff1b;而在C中我们可…

Doris节点扩容及数据表

扩容和缩容 上篇文章简单讲了doris的安装&#xff0c;本章分享的是doris中fe和be节点的扩容缩容以及doris的数据表1、FE 扩容和缩容 使用 MySQL 登录客户端后&#xff0c;可以使用 sql 命令查看 FE 状态&#xff0c;目前就一台 FE mysql -h linux -P 9030 -uroot -p mysql&…

python+django乡村居民数据的可视化平台

本论文主要论述了如何使用Django框架开发一个乡村振兴数据的可视化平台 &#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述乡村振兴数据的可视化平台的当前背景以…

拼多多二面,原来是我对自动化测试的理解太浅了

如果你入职一家新的公司&#xff0c;领导让你开展自动化测试&#xff0c;作为一个新人&#xff0c;你肯定会手忙脚乱&#xff0c;你会如何落地自动化测试呢&#xff1f; 01 什么是自动化 有很多人做了很长时间的自动化但却连自动化的概念都不清楚&#xff0c;这样的人也是很悲…

Android之 MVC到MVVM架构发展和封装

一 简介 1.1 软件架构发展趋势是解耦&#xff0c;即分离数据层和视图层&#xff0c;使得数据层专注于业务的数据和逻辑处理。从而提高代码的可读可编辑效率&#xff0c;提高团队协作能力&#xff0c;项目的生产能力&#xff0c;降低后期维护成本。 1.2 Android架构发展MVC -…

计算机组成原理实验四 微程序控制器实验报告

我班算是几乎最后一个做实验的班级了&#xff0c;报告参考了一些朋友提供的数据加上一些自己的主观拙见&#xff0c;本人水平有限加之制作仓促难免有错误&#xff0c;望大家批评指正。 4.1 微程序控制器实验 一、实验目的 (1) 掌握微程序控制器的组成原理。 (2) 掌握微程序的…

【蓝桥杯计算思维题】少儿编程 蓝桥杯青少组计算思维真题及详细解析第5套

少儿编程 蓝桥杯青少组计算思维真题及详细解析第5套 1、北京冬奥会经历 17( ),中国体育代表团收获的金牌数和奖牌数均创历史新高 A、年 B、月 C、天 D、小时 答案:C 考点分析:主要考查小朋友们对时事的了解,北京冬奥会总共经历了17天,所以答案C 2、下面图形的周长是…

Python系列模块之标准库json详解

感谢点赞和关注 &#xff0c;每天进步一点点&#xff01;加油&#xff01; 目录 一、Json介绍 二、JSON 函数 2.1 json.dumps 2.2 json.loads 2.3 实战案例&#xff1a;钉钉消息发送 一、Json介绍 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它使得人们…

2023年21个最佳的Ruby测试框架

作者 | Veethee Dixit 测试人员总是在寻找最好的自动化测试框架&#xff0c;它能提供丰富的功能&#xff0c;并且语法简单、兼容性好、执行速度快。如果你选择将Ruby与Selenium结合起来进行web测试&#xff0c;那么可能需要搜索基于Ruby的测试框架进行web应用程序测试。 Ruby…

【Python】函数式编程第二弹

知识目录 一、写在前面✨二、最小公倍数三、移除数字四、总结撒花&#x1f60a; 一、写在前面✨ 大家好&#xff01;我是初心&#xff0c;希望我们一路走来能坚守初心&#xff01; 今天跟大家分享的文章是 Python函数式编程第二弹&#xff0c;再次以两个简单的例子带大家更好…