Netty(5):核心部件:Codec 编解码器

版权声明:转载随意,附上转载信息即可 :) https://blog.csdn.net/Al_assad/article/details/79451979

Netty Codec 编解码器


Netty 对于解决数据从一种特定协议格式到另一种格式的转换(如二进制字节和POJO之间地转换),提供了 codecs (编解码器) 组件来处理的。使用 Netty 的 codecs ,可以很方便地为各种不同地协议编写编解码器;

  • 解码器:负责处理入站数据,将消息从字节或其他序列形式转成指定的消息对象;
  • 编码器:负责处理出站数据,将消息对象转化为字节或其他序列形式;

消息被编码后解码后会自动通过 ReferenceCountUtil.release(message) 释放,如果不想释放消息可以使用 ReferenceCountUtil.retain(message),这将会使引用数量增加而没有消息发布,大多数时候不需要这么做。

Decoder 解码器

Decoder 主要用于入站处理,Netty 中的提供的 Decoder 分为以下 2 类:
  • 字节到消息解码(byte-to-message):ByteToMessageDecoder 、ReplayingDecoder 等;
  • 消息到消息解码(message-to-message):MessageToMessageDecoder 等;
Decoder 是 ChannelInboundHandler 的抽象实现,所以 Decoder 的使用方法类似于 ChannelInboundHandler ,可以在 ChannelPipeline 中直接加入 Decoder ,就是可实现入站信息的编码;

ByteToMessageDecoder

用于将字节转为消息(或其他字节序列),其中有 2 个需要覆盖的比较重要的方法如下:
decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) 解码方法,将一个 BytedBuf 解码为一个 List<Object> 对象,并传递给下一个 ChannelHandler;
decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) 当 Channel 处于 inactive 状态时,会触发该方法一次;
以下示例中,每次从入站的 ByteBuf 中读取 4 个字节,编码为 int,并添加到一个 List,当decode方法执行结束后,out 包含的数据就会转发到下一个 ChannelInboundHandler 中;
 
public class ToIntegerDecoder extends ByteToMessageDecoder {  //1
    @Override
    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
            throws Exception {
        if (in.readableBytes() >= 4) {  //当可读字节达到 4 个时,读取这 4 个字节,并编码为一个 Integer,并添加到 out 传递到下一个 ChannelInboundHandler
            out.add(in.readInt());  
        }
    }
}

ReplayingDecoder

ReplayingDecoder 是 byte-to-message 解码的一种特殊的抽象基类,读取缓冲区的数据之前需要检查缓冲区是否有足够的字节,使用 ReplayingDecoder 就无需自己检查;若 ByteBuf 中有足够的字节,则会正常读取;若没有足够的字节则会停止解码;
以下示例中使用 ReplayingDecoder 改写以上的示例:
 
public class ToIntegerDecoder2 extends ReplayingDecoder<Void> {   
    @Override
    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
            throws Exception {
        out.add(in.readInt());  
    }
}

※此外 Decoder 还有其他丰富的 Decoder,如:io.netty.handler.codec.LineBasedFrameDecoder 通过结束控制符("\n" 或 "\r\n").解析入站数据io.netty.handler.codec.http.HttpObjectDecoder 用于 HTTP 数据解码;

MessageToMessageDecoder

用于从一种消息解码为另外一种消息(例如,POJO 到 POJO),同样最简单实现 MessageToMessageDecoder 只需覆盖 decode 方法即可;
以下示例中实现 IntegerToStringDecoder,用于将 Integer 转化为 String 格式;
 
public class IntegerToStringDecoder extends MessageToMessageDecoder<Integer> { 
    @Override
    public void decode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {
        out.add(String.valueOf(msg)); 
    }
}

解码时处理长帧

为了避免过长帧导致内存溢出,可以在解码器里设置一个最大字节数阈值,如果超出,将抛出 TooLongFrameException (并由 ChannelHandler.exceptionCaught() 捕获)。然后由译码器的用户决定如何处理它。虽然一些协议,比如 HTTP、允许这种情况下有一个特殊的响应,有些可能没有,此时唯一的选择可能就是关闭连接;
如以下 SafeByteToMessageDecoder,当帧长度大于 1024 字节时,会抛出 TooLongFrameException 通知其他 ChannelHandler 对其进行处理:
 
public class SafeByteToMessageDecoder extends ByteToMessageDecoder {  //1
    private static final int MAX_FRAME_SIZE = 1024;
    @Override
    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int readable = in.readableBytes();
        if (readable > MAX_FRAME_SIZE) {
            in.skipBytes(readable);        //当帧过长时,跳过该帧,并通知其他 ChannelHandler
            throw new TooLongFrameException("Frame too big!");
        }
    }
}




Encoder 编码器

Encoder 主要用于出站处理,Netty 中的提供的 Encoder 分为以下 2 类:
  • 消息到字节编码(message-to-byte):MessageToByteDecoder 等;
  • 消息到消息编码(message-to-message):MessageToMessageDecoder 等;
Encoder 是 ChannelOutboundHandler 的抽象实现,所以 Decoder 的使用方法类似于 ChannelOutboundHandler ,可以在 ChannelPipeline 中直接加入 Encoder ,就是可实现出站信息的解码;

MessageToByteEncoder

以下示例实现一个 IntegerToByteEncoder,用于将 Integer 类型消息转化为 bytes,并传递给下一个 ChannelOutboundHandler;
 
public class IntegerToByteEncoder extends MessageToByteEncoder<Integer> {  
    @Override
    public void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) throws Exception {
        out.writeInteger(msg); 
    }
}

MessageToMessageEncoder

以下实现一个 StringToIntegerEncoder ,用于将 String 编码,并传递给下一个 ChannelOutboundHandler;
 
public class StringToIntegerEncoder extends MessageToMessageEncoder<Integer> { 
    @Override
    public void encode(ChannelHandlerContext ctx, String msg, List<Object> out) throws Exception {
        out.add(Integer.parseInt(msg)); 
    }
}




Codec 编解码器

以上的 Decoder 和 Encoder 是分开的,Netty 也提供了 Codec 编解码类,同时可以支持编码、解码过程;

Netty 的编解码器也可以分为 byte-to-message,message-to-message 两类,典型的就是 ByteToMessageCodec、MessageToMessageCodec,实现一个 Codec 最简单的方式只需要覆盖其 decode,encode 方法即可;

但是结合编码器解码器在一起可能会增加耦合度,降低代码的重用性,Netty 提供了 CombinedChannelDuplexHandler 用于组装一个 Decoder 和 Encoder 为一个编解码器;
如下示例:
 
//入站解码器
public class ByteToCharDecoder extends ByteToMessageDecoder { //1
    @Override
    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
            throws Exception {
        if (in.readableBytes() >= 2) {  //2
            out.add(in.readChar());
        }
    }
}
 
//出站编码器
public class CharToByteEncoder extends MessageToByteEncoder<Character> { //1
    @Override
    public void encode(ChannelHandlerContext ctx, Character msg, ByteBuf out)
            throws Exception {
        out.writeChar(msg);   //2
    }
}
 
//组合以上编码器、解码器
public class CombinedByteCharCodec extends CombinedChannelDuplexHandler<ByteToCharDecoder, CharToByteEncoder> {
    public CombinedByteCharCodec() {
        super(new ByteToCharDecoder(), new CharToByteEncoder());
    }
}



猜你喜欢

转载自blog.csdn.net/Al_assad/article/details/79451979