抽象 Codec(编解码器)类

    您可能想知道是否有时候使用单独的解码器和编码器会比使用这些组合类要好,最简单的答案是,紧密耦合的两个函数减少了他们的可重用性,但是把他们分开实现就会更容易扩展。当我们研究抽象编解码器类时,我们也会拿它和对应的独立的解码器和编码器做对比。

    我们需要解码字节到消息,也许是一个 POJO,然后转回来。ByteToMessageCodec 将为我们处理这个问题,因为它结合了ByteToMessageDecoder 和 MessageToByteEncoder。表7.5中列出的重要方法。

    Table 7.5 ByteToMessageCodec API

    什么会是一个好的 ByteToMessageCodec 用例?任何一个请求/响应协议都可能是,例如 SMTP。编解码器将读取入站字节并解码到一个自定义的消息类型 SmtpRequest 。当接收到一个 SmtpResponse 会产生,用于编码为字节进行传输。

    7.3.2节中我们看到的一个例子使用 MessageToMessageEncoder 从一个消息格式转换到另一个地方。现在让我们看看 MessageToMessageCodec 是如何处理 单个类 的往返。

    在进入细节之前,让我们看看表7.6中的重要方法。

    Table 7.6 Methods of MessageToMessageCodec

    方法名称 描述
    decode This method is called with the inbound messages of the codec and decodes them to messages. Those messages are forwarded to the next ChannelInboundHandler in the ChannelPipeline
    decodeLast Default implementation delegates to decode().decodeLast will only be called one time, which is when the Channel goes inactive. If you need special handling here you may override decodeLast() to implement it.
    encode The encode method is called for each outbound message to be moved through the ChannelPipeline. The encoded messages are forwarded to the next ChannelOutboundHandler in the pipeline

    上面所示的完整签名的方法都是这样的

    1. OUTBOUND msg, List<Object> out)
    2. protected abstract void decode(ChannelHandlerContext ctx,
    3. INBOUND msg, List<Object> out)

    encode() 处理出站消息类型 OUTBOUND 到 INBOUND,而 decode() 则相反。我们在哪里可能使用这样的编解码器?

    在现实中,这是一个相当常见的用例,往往涉及两个来回转换的数据消息传递API 。这是常有的事,当我们不得不与遗留或专有的消息格式进行互操作。

    如清单7.7所示这样的可能性。在这个例子中,WebSocketConvertHandler 是一个静态嵌套类,继承了参数为 WebSocketFrame(类型为 INBOUND)和 WebSocketFrame(类型为 OUTBOUND)的 MessageToMessageCode

    Listing 7.7 MessageToMessageCodec

    1. 编码 WebSocketFrame 消息转为 WebSocketFrame 消息
    2. 通过 instanceof 来检测正确的 FrameType
    3. 自定义消息类型 WebSocketFrame
    4. 枚举类明确了 WebSocketFrame 的类型

    如前所述,结合解码器和编码器在一起可能会牺牲可重用性。为了避免这种方式,并且部署一个解码器和编码器到 ChannelPipeline 作为逻辑单元而不失便利性。

    关键是下面的类:

    Listing 7.8 ByteToCharDecoder

    1. 继承 ByteToMessageDecoder
    2. 写 char 到 MessageBuf

    decode() 方法从输入数据中提取两个字节,并将它们作为一个 char 写入 List 。(注意,实现扩展 ByteToMessageDecoder 因为它从 ByteBuf 读取字符。)

    现在看一下清单7.9中,把字符转换为字节的编码器。

    Listing 7.9 CharToByteEncoder

    1. public class CharToByteEncoder extends
    2. MessageToByteEncoder<Character> { //1
    3. public void encode(ChannelHandlerContext ctx, Character msg, ByteBuf out)
    4. throws Exception {
    5. out.writeChar(msg); //2
    6. }
    1. 继承 MessageToByteEncoder
    2. 写 char 到 ByteBuf

    这个实现继承自 MessageToByteEncoder 因为他需要编码 char 消息 到 ByteBuf。这将直接将字符串写为 ByteBuf。

    现在我们有编码器和解码器,将他们组成一个编解码器。见下面的 CombinedChannelDuplexHandler.

    Listing 7.10 CombinedByteCharCodec

    1. 传递 ByteToCharDecoder 和 CharToByteEncoder 实例到 super 构造函数来委托调用使他们结合起来。