Netty抽象 Codec(編解碼器)類

2018-08-08 10:40 更新

我們?cè)谟懻摻獯a器和編碼器的時(shí)候,都是把它們當(dāng)成不同的實(shí)體的,但是有時(shí)候如果在同一個(gè)類中同時(shí)放入入站和出站的數(shù)據(jù)和信息轉(zhuǎn)換的話,發(fā)現(xiàn)會(huì)更加實(shí)用。而Netty中的抽象Codec(編解碼器)類就能達(dá)到這個(gè)目的,它們成對(duì)地組合解碼器和編碼器,以此提供對(duì)于字節(jié)和消息都相同的操作(這些類實(shí)現(xiàn)了 ChannelInboundHandler 和 ChannelOutboundHandler )。

您可能想知道是否有時(shí)候使用單獨(dú)的解碼器和編碼器會(huì)比使用這些組合類要好,最簡單的答案是,緊密耦合的兩個(gè)函數(shù)減少了他們的可重用性,但是把他們分開實(shí)現(xiàn)就會(huì)更容易擴(kuò)展。當(dāng)我們研究抽象編解碼器類時(shí),我們也會(huì)拿它和對(duì)應(yīng)的獨(dú)立的解碼器和編碼器做對(duì)比。

ByteToMessageCodec

我們需要解碼字節(jié)到消息,也許是一個(gè) POJO,然后轉(zhuǎn)回來。ByteToMessageCodec 將為我們處理這個(gè)問題,因?yàn)樗Y(jié)合了ByteToMessageDecoder 和 MessageToByteEncoder。表7.5中列出的重要方法。

Table 7.5 ByteToMessageCodec API

方法名稱描述
decodeThis method is called as long as bytes are available to be consumed. It converts the inbound ByteBuf to the specified message format and forwards them to the next ChannelInboundHandler in the pipeline.
decodeLastThe default implementation of this method delegates to decode(). It is called only be called once, when the Channel goes inactive. For special handling it can be oerridden.
encodeThis method is called for each message to be written through the ChannelPipeline. The encoded messages are contained in a ByteBuf which

什么會(huì)是一個(gè)好的 ByteToMessageCodec 用例?任何一個(gè)請(qǐng)求/響應(yīng)協(xié)議都可能是,例如 SMTP。編解碼器將讀取入站字節(jié)并解碼到一個(gè)自定義的消息類型 SmtpRequest 。當(dāng)接收到一個(gè) SmtpResponse 會(huì)產(chǎn)生,用于編碼為字節(jié)進(jìn)行傳輸。

MessageToMessageCodec

7.3.2節(jié)中我們看到的一個(gè)例子使用 MessageToMessageEncoder 從一個(gè)消息格式轉(zhuǎn)換到另一個(gè)地方?,F(xiàn)在讓我們看看 MessageToMessageCodec 是如何處理 單個(gè)類 的往返。

在進(jìn)入細(xì)節(jié)之前,讓我們看看表7.6中的重要方法。

Table 7.6 Methods of MessageToMessageCodec

方法名稱描述
decodeThis 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
decodeLastDefault 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.
encodeThe 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

MessageToMessageCodec 是一個(gè)參數(shù)化的類,定義如下:

public abstract class MessageToMessageCodec<INBOUND,OUTBOUND>

上面所示的完整簽名的方法都是這樣的

protected abstract void encode(ChannelHandlerContext ctx,
OUTBOUND msg, List<Object> out)
protected abstract void decode(ChannelHandlerContext ctx,
INBOUND msg, List<Object> out)

encode() 處理出站消息類型 OUTBOUND 到 INBOUND,而 decode() 則相反。我們?cè)谀睦锟赡苁褂眠@樣的編解碼器?

在現(xiàn)實(shí)中,這是一個(gè)相當(dāng)常見的用例,往往涉及兩個(gè)來回轉(zhuǎn)換的數(shù)據(jù)消息傳遞API 。這是常有的事,當(dāng)我們不得不與遺留或?qū)S械南⒏袷竭M(jìn)行互操作。

如清單7.7所示這樣的可能性。在這個(gè)例子中,WebSocketConvertHandler 是一個(gè)靜態(tài)嵌套類,繼承了參數(shù)為 WebSocketFrame(類型為 INBOUND)和 WebSocketFrame(類型為 OUTBOUND)的 MessageToMessageCode

Listing 7.7 MessageToMessageCodec

public class WebSocketConvertHandler extends MessageToMessageCodec<WebSocketFrame, WebSocketConvertHandler.WebSocketFrame> {  //1

    public static final WebSocketConvertHandler INSTANCE = new WebSocketConvertHandler();

    @Override
    protected void encode(ChannelHandlerContext ctx, WebSocketFrame msg, List<Object> out) throws Exception {   
        ByteBuf payload = msg.getData().duplicate().retain();
        switch (msg.getType()) {   //2
            case BINARY:
                out.add(new BinaryWebSocketFrame(payload));
                break;
            case TEXT:
                out.add(new TextWebSocketFrame(payload));
                break;
            case CLOSE:
                out.add(new CloseWebSocketFrame(true, 0, payload));
                break;
            case CONTINUATION:
                out.add(new ContinuationWebSocketFrame(payload));
                break;
            case PONG:
                out.add(new PongWebSocketFrame(payload));
                break;
            case PING:
                out.add(new PingWebSocketFrame(payload));
                break;
            default:
                throw new IllegalStateException("Unsupported websocket msg " + msg);
        }
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, io.netty.handler.codec.http.websocketx.WebSocketFrame msg, List<Object> out) throws Exception {
        if (msg instanceof BinaryWebSocketFrame) {  //3
            out.add(new WebSocketFrame(WebSocketFrame.FrameType.BINARY, msg.content().copy()));
        } else if (msg instanceof CloseWebSocketFrame) {
            out.add(new WebSocketFrame(WebSocketFrame.FrameType.CLOSE, msg.content().copy()));
        } else if (msg instanceof PingWebSocketFrame) {
            out.add(new WebSocketFrame(WebSocketFrame.FrameType.PING, msg.content().copy()));
        } else if (msg instanceof PongWebSocketFrame) {
            out.add(new WebSocketFrame(WebSocketFrame.FrameType.PONG, msg.content().copy()));
        } else if (msg instanceof TextWebSocketFrame) {
            out.add(new WebSocketFrame(WebSocketFrame.FrameType.TEXT, msg.content().copy()));
        } else if (msg instanceof ContinuationWebSocketFrame) {
            out.add(new WebSocketFrame(WebSocketFrame.FrameType.CONTINUATION, msg.content().copy()));
        } else {
            throw new IllegalStateException("Unsupported websocket msg " + msg);
        }
    }

    public static final class WebSocketFrame {  //4
        public enum FrameType {        //5
            BINARY,
            CLOSE,
            PING,
            PONG,
            TEXT,
            CONTINUATION
        }

        private final FrameType type;
        private final ByteBuf data;
        public WebSocketFrame(FrameType type, ByteBuf data) {
            this.type = type;
            this.data = data;
        }

        public FrameType getType() {
            return type;
        }

        public ByteBuf getData() {
            return data;
        }
    }
}
  1. 編碼 WebSocketFrame 消息轉(zhuǎn)為 WebSocketFrame 消息
  2. 檢測(cè) WebSocketFrame 的 FrameType 類型,并且創(chuàng)建一個(gè)新的響應(yīng)的 FrameType 類型的 WebSocketFrame
  3. 通過 instanceof 來檢測(cè)正確的 FrameType
  4. 自定義消息類型 WebSocketFrame
  5. 枚舉類明確了 WebSocketFrame 的類型

CombinedChannelDuplexHandler

如前所述,結(jié)合解碼器和編碼器在一起可能會(huì)犧牲可重用性。為了避免這種方式,并且部署一個(gè)解碼器和編碼器到 ChannelPipeline 作為邏輯單元而不失便利性。

關(guān)鍵是下面的類:

public class CombinedChannelDuplexHandler<I extends ChannelInboundHandler,O extends ChannelOutboundHandler>

這個(gè)類是擴(kuò)展 ChannelInboundHandler 和 ChannelOutboundHandler 參數(shù)化的類型。這提供了一個(gè)容器,單獨(dú)的解碼器和編碼器類合作而無需直接擴(kuò)展抽象的編解碼器類。我們將在下面的例子說明這一點(diǎn)。首先查看 ByteToCharDecoder ,如清單7.8所示。

Listing 7.8 ByteToCharDecoder

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());
        }
    }
}
  1. 繼承 ByteToMessageDecoder
  2. 寫 char 到 MessageBuf

decode() 方法從輸入數(shù)據(jù)中提取兩個(gè)字節(jié),并將它們作為一個(gè) char 寫入 List 。(注意,實(shí)現(xiàn)擴(kuò)展 ByteToMessageDecoder 因?yàn)樗鼜?ByteBuf 讀取字符。)

現(xiàn)在看一下清單7.9中,把字符轉(zhuǎn)換為字節(jié)的編碼器。

Listing 7.9 CharToByteEncoder

public class CharToByteEncoder extends
        MessageToByteEncoder<Character> { //1

    @Override
    public void encode(ChannelHandlerContext ctx, Character msg, ByteBuf out)
            throws Exception {
        out.writeChar(msg);   //2
    }
}
  1. 繼承 MessageToByteEncoder
  2. 寫 char 到 ByteBuf

這個(gè)實(shí)現(xiàn)繼承自 MessageToByteEncoder 因?yàn)樗枰幋a char 消息 到 ByteBuf。這將直接將字符串寫為 ByteBuf。

現(xiàn)在我們有編碼器和解碼器,將他們組成一個(gè)編解碼器。見下面的 CombinedChannelDuplexHandler.

Listing 7.10 CombinedByteCharCodec

public class CombinedByteCharCodec extends CombinedChannelDuplexHandler<ByteToCharDecoder, CharToByteEncoder> {
    public CombinedByteCharCodec() {
        super(new ByteToCharDecoder(), new CharToByteEncoder());
    }
}
  1. CombinedByteCharCodec 的參數(shù)是解碼器和編碼器的實(shí)現(xiàn)用于處理進(jìn)站字節(jié)和出站消息
  2. 傳遞 ByteToCharDecoder 和 CharToByteEncoder 實(shí)例到 super 構(gòu)造函數(shù)來委托調(diào)用使他們結(jié)合起來。

正如你所看到的,它可能是用上述方式來使程序更簡單、更靈活,而不是使用一個(gè)以上的編解碼器類。它也可以歸結(jié)到你個(gè)人喜好或風(fēng)格。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)