Netty网络框架学习笔记-9(编码与解码器机制)

Netty网络框架学习笔记-9(编码与解码器机制)

数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码

codec(编解码器) 的组成部分有两个:

decoder(解码器)和 encoder(编码器)。

encoder 负责把业务数据转换成字节码数据,decoder 负责把字节码数据转换成业务数据

编解码的方式要一致, 假设你以UTF-8字符集编码成为字符串、 那解码也是需要UTF-8字符集解码成为字符串

Netty本身的编解码

编解码器负责出入站操作,那么也一定要实现ChannelInboundHandlerChannelOutboundHandler接口,所以解码器本质 上也是ChannelHandler

1.0 Netty 提供的编码器

netty中编码的抽象基类MessageToByteEncoderMessageToMessageEncoder 可以在其下面的继承关系查看对应提供的解码器

  • StringEncoder,对字符串数据进行编码
  • HttpObjectEncoder http对象编码器
  • ObjectEncoder 将对象(需要实现Serializable接⼝)编码为字节流

还有很多提供的编码器, 自行从抽象基类MessageToByteEncoderMessageToMessageEncoder 继承关系找

2.0 Netty 提供的解码器

netty中编码的抽象基类MessageToMessageDecoderByteToMessageDecoder 可以在其下面的继承关系查看对应提供的解码器

StringDecoder 对字符串数据进行解码
ObjectDecoder 将对象(需要实现Serializable接⼝)解码为对象
RedisDecoder 基于Redis协议的解码器
XmlDecoder 基于XML格式的解码器
JsonObjectDecoder 基于json数据格式的解码器
HttpObjectDecoder 基于http协议的解码器

还有很多提供的编码器, 自行从抽象基类MessageToMessageDecoderByteToMessageDecoder 继承关系找

Netty中的ObjectDecoderObjectEncoder 底层使用的还是JDK的序列化方式, 缺点很多!

  1. 无法跨语言 …
  2. 易被攻击 …
  3. 序列化后的流太大 …
  4. 序列化性能太差

推荐使用扩展介绍的 Google 的 Protobuf

Netty 编解码器

1.0 ChannelHandler 说明

ChannelHandler 下的ChannelInboundHandler (入站)ChannelOutboundHandler (出站)

Netty 的主要组件有 Channel、EventLoop、ChannelFuture、ChannelHandler、ChannelPipe 等

ChannelHandler 充当了处理入站和出站数据的应用程序逻辑的容器。

实现 ChannelInboundHandler 接口(或 ChannelInboundHandlerAdapter),你就可以接收入站事件和数据,这些数据会被业务逻辑处理。

当要给客户端 发 送 响 应 时 , 也 可 以 从 ChannelInboundHandler 冲 刷 数 据 。 业 务 逻 辑 通 常 写 在 一 个 或 者 多 个 ChannelInboundHandler 中。ChannelOutboundHandler 原理一样,只不过它是用来处理出站数据的

ChannelPipeline 提供了 ChannelHandler 链的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到

服务端的,那么我们称这些事件为出站的,即客户端发送给服务端的数据会通过 pipeline 中的一系列

ChannelOutboundHandler,并被这些 Handler 处理,反之则称为入站的

2.0 编码解码器

本质上Netty 中的编解码器也是处理器的一种。

当 Netty 发送或者接受一个消息的时候,就将会发生一次数据转换。入站消息会被解码:从字节转换为另一种

格式(比如 java 对象); 如果是出站消息,它会被编码成字节。

Netty 提供一系列实用的编解码器,他们都实现了 ChannelInboundHadnler 或者 ChannelOutboundHandler 接口。

在这些类中,channelRead 方法已经被重写了。以入站为例,对于每个从入站 Channel 读取的消息,这个方法会

被调用。随后,它将调用由解码器所提供的 decode()方法进行解码,并将已经解码的字节转发给 ChannelPipeline

中的下一个 ChannelInboundHandler (处理器)。

3.0 解码器以ByteToMessageDecoder学习

Netty网络框架学习笔记-9(编码与解码器机制)_第1张图片

由于不可能知道发送方是否会一次性发送一个完整的信息,tcp 有可能出现粘包拆包的问题,这个类会对入站数据进行缓冲,直到它准备好被处理.

@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof ByteBuf) {
            CodecOutputList out = CodecOutputList.newInstance();
            try {
                first = cumulation == null;
                cumulation = cumulator.cumulate(ctx.alloc(),
                        first ? Unpooled.EMPTY_BUFFER : cumulation, (ByteBuf) msg);
                callDecode(ctx, cumulation, out);
            } catch (DecoderException e) {
                ........

本身有判断该msg是否需要进行解码, 如果需要解码则会调用callDecode(ctx, cumulation, out);

否则调用ctx.fireChannelRead(msg); 传递给下一个处理器(解码器)

4.0 编码器以MessageToByteEncoder学习

Netty网络框架学习笔记-9(编码与解码器机制)_第2张图片

 @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ByteBuf buf = null;
        try {
            if (acceptOutboundMessage(msg)) {
                @SuppressWarnings("unchecked")
                I cast = (I) msg;
                buf = allocateBuffer(ctx, cast, preferDirect);
                try {
                    encode(ctx, cast, buf);
                    ........

编码器本身也有判断是否需要处理编码: 如果msg不满足, 则会传递给下一个处理器(编码器)

总结:

如果使用了编解码器, 或者处理器, 泛型指定了类型 , 如: MessageToByteEncoder

那么只有msg 是string类型时候才会进行处理编解码。 否则传递给下一个处理器, 最终输出/输入

扩展

Protobuf (参考文档 、其他作者介绍 、其他作者的入门案例)

Protobuf 是 Google 发布的开源项目,全称 Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,

可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC[远程过程调用 remote procedure call ] 数据交换格式 。

Protobuf 是以 message 的方式来管理数据的

支持跨平台、跨语言,即[客户端和服务器端可以是不同的语言编写的] (支持目前绝大多数语言,例如 C++、C#、Java、python 等)

高性能,高可靠性 ,

使用 protobuf 编译器能自动生成代码,Protobuf 是将类的定义使用.proto 文件进行描述。

说明,在 idea 中编写 .proto 文件时,会自动提示是否下载 .ptotot 编写插件. 可以让语法高亮。 然后通过 protoc.exe 编译器根据.proto 自动生成.java 文件

使用方式:

  • 在文件中定义消息格式.proto
  • 使用协议缓冲区编译器。
  • 使用 Java 协议缓冲区 API 来写入和读取消息。

Netty使用Protobuf 作为传输数据格式需要做的东西

  • 依赖:
<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.12.0</version>
</dependency>
  • 安装插件 maven_pom文件加上
<plugin>
    <groupId>org.xolstice.maven.pluginsgroupId>
    <artifactId>protobuf-maven-pluginartifactId>
    <version>0.6.1version>
    <configuration>
        <protocArtifact>
            com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}
        protocArtifact>
        <pluginId>grpc-javapluginId>
    configuration>
plugin>
  • 服务端和客户端添加对应的编解码器
 pipeline.addLast(new ProtobufDecoder()); // 服务端解码器
 pipeline.addLast(new ProtobufEncoder()); // 客户端编码器

其他一些可能使用的编码器

  • LineBasedFrameDecoder:这个类在 Netty 内部也有使用,它使用行尾控制字符(\n 或者\r\n)作为分隔符来解 析数据。
  • DelimiterBasedFrameDecoder:使用自定义的特殊字符作为消息的分隔符。
  • LengthFieldBasedFrameDecoder:通过指定长度来标识整包消息,这样就可以自动的处理黏包和半包消息。
  • ReplayingDecoder : ReplayingDecoder 扩展了 ByteToMessageDecoder 类,使用这个类,我们不必调用 **readableBytes()**方法。

ReplayingDecoder 不需要判断数据是否足够读取,内部会进行处理判断,

并 不 是 所 有 的 ByteBuf 操 作 都 被 支 持 , 如 果 调 用 了 一 个 不 被 支 持 的 方 法 , 将 会 抛 出 一 个

UnsupportedOperationException。

ReplayingDecoder 在某些情况下可能稍慢于 ByteToMessageDecoder,例如网络缓慢并且消息格式复杂时,

消息会被拆成了多个碎片,速度变慢

1

你可能感兴趣的:(Netty,网络,学习,java)