Java的编解码

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

image.png

java序列化对象只需要实现java.io.Serializable接口并生成序列化ID,这个类就能够通过 java.io.ObjectInput和java.io.ObjectOutput序列化和反序列化。

Java序列化目的:1.网络传输。2.对象持久化。
Java序列化缺点:1.无法跨语言。 2.序列化后码流太大。3.序列化性能太低。 Java序列化仅仅是Java编解码技术的一种,由于它的种种缺陷,衍生出了多种编解码技术和框 架,这些编解码框架实现消息的高效序列化。

Netty编解码器

网络 中都是以字节码的数据形式来传输数据的,服务器编码数据后发送到客户端,客户端需要对数据进行解码。
对于Netty而言,编解码器由两部分组成:编码器、解码器。
解码器:负责将消息从字节或其他序列形式转成指定的消息对象。
编码器:将消息对象转成字节或其他序列形式在网络上传输。

Netty 的编(解)码器实现了 ChannelHandlerAdapter,也是一种特殊的 ChannelHandler,所 以依赖于 ChannelPipeline可以将多个编(解)码器链接在一起,以实现复杂的转换逻辑

Netty里面的编解码:
解码器:负责处理“入站 InboundHandler”数据。
编码器:负责“出站 OutboundHandler” 数据。

解码器(Decoder)

解码器负责 解码“入站”数据从一种格式到另一种格式,解码器处理入站数据是抽象 ChannelInboundHandler的实现。需要将解码器放在ChannelPipeline中。对于解码器,Netty中 主要提供了抽象基类ByteToMessageDecoder和MessageToMessageDecoder
image.png

抽象解码器

  1. ByteToMessageDecoder: 用于将字节转为消息,需要检查缓冲区是否有足够的字节
  2. ReplayingDecoder: 继承ByteToMessageDecoder,不需要检查缓冲区是否有足够的字节,
  3. 但是 ReplayingDecoder速度略慢于 ByteToMessageDecoder,同时不是所有的ByteBuf都支持。
  4. 项目复杂性高则使用ReplayingDecoder,否则使用ByteToMessageDecoder
  5. MessageToMessageDecoder: 用于从一种消息解码为另外一种消息(例如POJOPOJO

核心方法

decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out)

解码器代码实现

消息入站一定会经过解码器
自定义的解码器需要继承于MessageToMessageDecoder类
自定义解码器一定要放在自定义的业务处理handler之前,因为经过层层编解码后,最后才到自定义handler,

public class MessageDecoder extends MessageToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
        System.out.println("正在进行消息解码....");
        //收到的消息,在网络过程中一定是一个字节码
        ByteBuf byteBuf = (ByteBuf) msg;
        out.add(byteBuf.toString(CharsetUtil.UTF_8));//传递到下一个handler
    }
}

编码器(Encoder)

与ByteToMessageDecoder和MessageToMessageDecoder相对应,Netty提供了对应的编码器 实现MessageToByteEncoder和MessageToMessageEncoder,二者都实现 ChannelOutboundHandler接口。
image.png

抽象编码器

MessageToByteEncoder: 将消息转化成字节
MessageToMessageEncoder: 用于从一种消息编码为另外一种消息(例如POJO到POJO)

核心方法

完成消息编码的工作

encode(ChannelHandlerContext ctx, String msg, List<Object> out)

编码器代码实现

处理消息的编码(消息出站),是通过 ctx.writeAndFlush(“你好呀.我是Netty客户端”);发送消息
编码器会先街道writeAndFlush发出的消息,然后进行编码,在传给下一个handler

public class MessageEncoder extends MessageToMessageEncoder {
    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
        System.out.println("消息正在进行编码....");
        // msg就是传过来的string的值
        String str = (String) msg;
        // 将消息转换为copiedBuffer,然后向下传递
        out.add(Unpooled.copiedBuffer(str, CharsetUtil.UTF_8)); 
    }
}

出站是:从 ctx.writeAndFlush() 的信息给到 自定义编码器,然后进行向下传递
入站是:信息先传给解码器,先解码,然后将信息传入

编解码器添加到服务端/客户端

服务端添加编解码

image.png

客户端添加编解码

image.png

添加了编解码器之后的好处

所有的自定义处理的Handler,就不用进行消息处理了,直接就能打印了。
image.png
image.png

消息出站

先经过自定义handler,再到编码器
image.png

消息入站

先经过解码器,再到自定义handler
image.png

编码解码器Codec

编码解码器: 同时具有编码与解码功能,特点同时实现了ChannelInboundHandler和 ChannelOutboundHandler接口,因此在数据输入和输出时都能进行处理。
image.png

Netty提供提供了一个ChannelDuplexHandler适配器类,编码解码器的抽象基类 ByteToMessageCodec ,MessageToMessageCodec都继承与此类.

代码实现

自定义类要继承MessageToMessageCodec

public class MessageCodec extends MessageToMessageCodec {
    /**
     * 编码
     *
     * @param ctx
     * @param msg
     * @param out
     * @throws Exception
     */
    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
        System.out.println("消息正在进行编码....");
        String str = (String) msg;
        out.add(Unpooled.copiedBuffer(str, CharsetUtil.UTF_8));
    }

    /**
     * 解码
     *
     * @param ctx
     * @param msg
     * @param out
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
        System.out.println("正在进行消息解码....");
        ByteBuf byteBuf = (ByteBuf) msg;
        out.add(byteBuf.toString(CharsetUtil.UTF_8));//传递到下一个handler
    }
}