【Netty】Netty 解码器(十二)

这篇具有很好参考价值的文章主要介绍了【Netty】Netty 解码器(十二)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

回顾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】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中。

整个例子的处理流程如下图所示:
【Netty】Netty 解码器(十二)

对于编码器和解码器来说,整个过程非常简单。一旦一个消息被编码或者解码,它自动被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】Netty 解码器(十二)

总结

上述我们重点讲解了 Netty 中的解码器相关知识。下节我们就来讲解一下编码器。文章来源地址https://www.toymoban.com/news/detail-467234.html

到了这里,关于【Netty】Netty 解码器(十二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Transformer系列(1)】encoder(编码器)和decoder(解码器)

    前言 这个专栏我们开始学习transformer,自推出以来transformer在深度学习中占有重要地位,不仅在NLP领域,在CV领域中也被广泛应用,尤其是2021年,transformer在CV领域可谓大杀四方。 在论文的学习之前,我们先来介绍一些专业术语。本篇就让我们先来认识一下encoder和decoder吧!

    2024年03月25日
    浏览(61)
  • 解码器 | 基于 Transformers 的编码器-解码器模型

    基于 transformer 的编码器-解码器模型是 表征学习 和 模型架构 这两个领域多年研究成果的结晶。本文简要介绍了神经编码器-解码器模型的历史,更多背景知识,建议读者阅读由 Sebastion Ruder 撰写的这篇精彩 博文。此外,建议读者对 自注意力 (self-attention) 架构 有一个基本了解

    2024年02月08日
    浏览(48)
  • 【计算机视觉 | 目标检测】术语理解9:AIGC的理解,对比学习,解码器,Mask解码器,耦合蒸馏,半耦合,图像编码器和组合解码器的耦合优化

    AIGC指的是使用人工智能技术自动生成的各类数字内容,包括文本、图像、音频、视频等。它利用机器学习模型进行智能化内容生成。 主要的技术手段包括: 自然语言生成(NLG):使用RNN、GPT等语言模型生成文本。 生成对抗网络(GAN):使用GAN生成高质量图片。 自动语音合成(TTS):使用

    2024年02月04日
    浏览(67)
  • 编码器 | 基于 Transformers 的编码器-解码器模型

    基于 transformer 的编码器-解码器模型是 表征学习 和 模型架构 这两个领域多年研究成果的结晶。本文简要介绍了神经编码器-解码器模型的历史,更多背景知识,建议读者阅读由 Sebastion Ruder 撰写的这篇精彩 博文。此外,建议读者对 自注意力 (self-attention) 架构 有一个基本了解

    2024年02月08日
    浏览(60)
  • ffmpeg中的avs解码器综述

    最近拿了一个avs的视频流,用硬件可以解码,但是ffmpeg自带的却无法解码。 所以研究了一下,首先看ffmpeg的avs解码器: 可以看到avs有两个,第一个是avs 第二个是cavs. 我们先用avs来解码,解码的视频是通过【 avs编码器 】编码的: 结果发现有问题,尺寸本来是640 360,结果被强

    2024年02月08日
    浏览(51)
  • flutter 视频解码器fijkplayer使用

           本人做视频监控项目的时候,需要去展示视频流到用户端,一开始使用flutter自带的VideoPlayer播放监控视频,一开始没有发现有什么问题,因为使用多的是Android模拟器,一直没有使用iso模拟器或者真机测试能不能播放,直到开发接近尾声,在ios模拟器上测试的时候发现

    2023年04月10日
    浏览(45)
  • 深入了解Transformer:从编码器到解码器的神经网络之旅

    自2017年问世以来,Transformer模型在自然语言处理(NLP)领域引发了一场革命。它的独特设计和高效性能使其成为了解决复杂语言任务的关键工具。 (1)自注意力机制 Transformer的核心在于自注意力机制。它允许模型在处理每个词时考虑句子中的所有其他词,从而有效捕获长距离依

    2024年01月17日
    浏览(49)
  • 【NLP概念源和流】 06-编码器-解码器模型(6/20 部分)

            在机器翻译等任务中,我们必须从一系列输入词映射到一系列输出词。读者必须注意,这与“序列标记”不同,在“序列标记”中,该任务是将序列中的每个单词映射到预定义的类,如词性或命名实体任务。 作者生成         在上面的

    2024年02月14日
    浏览(45)
  • ffmpeg视频解码器的配置选项含义

    lowres 是 AVCodecContext 结构体中的一个成员变量,用于指定编解码器的降低分辨率级别。 在某些情况下,为了加快编解码的速度或减少计算资源的消耗,可以通过设置 lowres 参数来降低编解码器的分辨率级别。这将导致编解码器在处理视频时使用较低的分辨率,从而减少计算量

    2024年02月22日
    浏览(40)
  • 解码Transformer:自注意力机制与编解码器机制详述与代码实现

    本文全面探讨了Transformer及其衍生模型,深入分析了自注意力机制、编码器和解码器结构,并列举了其编码实现加深理解,最后列出基于Transformer的各类模型如BERT、GPT等。文章旨在深入解释Transformer的工作原理,并展示其在人工智能领域的广泛影响。 作者 TechLead,拥有10+年互

    2024年02月13日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包