一、前言

  前面已經(jīng)學(xué)習(xí)完了Netty框架中的主要組件,接著學(xué)習(xí)codec框架。

二、codec框架

  每個網(wǎng)絡(luò)應(yīng)用程序必須定義如何將在對等體之間傳輸?shù)脑甲止?jié)解析并轉(zhuǎn)換為目標(biāo)程序的數(shù)據(jù)格式,這種轉(zhuǎn)換邏輯有codec處理,其由編碼器和解碼器組成,每個編碼器和解碼器將字節(jié)流從一種格式轉(zhuǎn)換到另一種格式。若將消息視為具有特定意義的結(jié)構(gòu)化字節(jié)序列,那么編碼器將該消息轉(zhuǎn)換成適合于傳輸?shù)母袷剑ê芸赡苁亲止?jié)流),反之,解碼器將網(wǎng)絡(luò)流轉(zhuǎn)換回應(yīng)用程序的消息格式,然后,編碼器處理出站數(shù)據(jù),解碼器處理入站數(shù)據(jù)。

  2.1 解碼器

  解碼器類涵蓋兩個不同的使用用例。

    · 將字節(jié)解碼為消息 - ByteToMessageDecoder和ReplayingDecoder。

    · 將一個消息類型解碼為另一個 - MessageToMessageDecoder。

  解碼器負(fù)責(zé)將入站數(shù)據(jù)從一種格式轉(zhuǎn)換到另一種格式,所以Netty的解碼器實現(xiàn)了ChannelInboundHandler接口。當(dāng)需要在ChannelPipeline中為下一個ChannelInboundHandler轉(zhuǎn)換入站數(shù)據(jù)時需要使用解碼器。由于Netty支持代碼模塊化和重用,因此可以鏈接多個解碼器來實現(xiàn)任意復(fù)雜的轉(zhuǎn)換邏輯。

  1. ByteToMessageDecoder抽象類

  從字節(jié)到消息(或另一個字節(jié)序列)的解碼是一個常見的任務(wù),Netty使用ByteToMessageDecoder抽象類完成該任務(wù),由于無法知道遠(yuǎn)程對等體是否一次發(fā)送完整的消息,因此該類會緩沖入站數(shù)據(jù),直到所有待處理的數(shù)據(jù)已經(jīng)準(zhǔn)備好。

  假設(shè)你收到一個包含簡單int的字節(jié)流,每個int都要單獨處理。 此時將從入站ByteBuf讀取每個int,并將其傳遞給下一個ChannelInboundHandler。而為解碼字節(jié)流,需要擴展ByteToMessageDecoder(當(dāng)int添加到List時,它將自動裝箱到Integer類型),整個過程如下圖所示。

  平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  一次從ByteBuf中讀取四個字節(jié)解析成一個int類型,并添加到List中,當(dāng)讀取完成后,將會被傳遞至下個ChannelHandler中,下面是ToIntegerDecoder的源代碼。 

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

public class ToIntegerDecoder extends ByteToMessageDecoder {
    @Override    public void decode(ChannelHandlerContext ctx, ByteBuf in,
        List<Object> out) throws Exception {        if (in.readableBytes() >= 4) {
            out.add(in.readInt());
        }
    }
}

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  2. ReplayingDecoder抽象類

  ReplayingDecoder繼承ByteToMessageDecoder類,并且不再需要調(diào)用readableBytes()方法,其通過自定義的ReplayingDecoderBuffer來實現(xiàn)該功能,其代碼如下所示。 

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

public class ToIntegerDecoder2 extends ReplayingDecoder<Void> {
    @Override    public void decode(ChannelHandlerContext ctx, ByteBuf in,
        List<Object> out) throws Exception {
        out.add(in.readInt());
    }
}

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  其中Void表示不執(zhí)行任何操作,即不管理任何狀態(tài)類型。

  3. MessageToMessageDecoder抽象類

  可以使用MessageToMessageDecoder在消息格式之間進(jìn)行轉(zhuǎn)換,僅需要實現(xiàn)其decode方法。如IntegerToStringDecoder繼承MessageToMessageDecoder,其將Integer類型轉(zhuǎn)化為對應(yīng)的String表示,轉(zhuǎn)化圖如下圖所示。

  平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  下面是IntegerToStringDecoder的代碼。 

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

public class IntegerToStringDecoder extends
    MessageToMessageDecoder<Integer> {
    @Override    public void decode(ChannelHandlerContext ctx, Integer msg
        List<Object> out) throws Exception {
        out.add(String.valueOf(msg));
    }
}

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  4. TooLongFrameException類

  由于Netty是一個異步框架,所以需要緩沖內(nèi)存中的字節(jié),直到能夠?qū)ζ溥M(jìn)行解碼。不能讓解碼器緩沖太多的數(shù)據(jù)以致耗盡可用內(nèi)存,為解決該問題,Netty提供了TooLongFrameException類異常,如果超過指定的大小限制,則由解碼器拋出該異常。為了避免這種情況,可以設(shè)置最大字節(jié)數(shù)的閾值,如果超出,將拋出TooLongFrameException異常,然后由解碼器的用戶決定如何處理異常。某些協(xié)議(例如HTTP)可能允許返回特殊響應(yīng),而在其他情況下,可能只能關(guān)閉連接。

  下面代碼展示了ByteToMessageDecoder是如何使用TooLongFrameException來通知ChannelPipeline中的其他ChannelHandler關(guān)于幀大小超出運行,特別是當(dāng)使用具有可變框架大小的協(xié)議時,此類保護會尤為重要。 

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

public class SafeByteToMessageDecoder extends ByteToMessageDecoder {    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);            throw new TooLongFrameException("Frame too big!");
        }        // do something        ...
    }
}

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  2.2 編碼器

  編碼器實現(xiàn)了ChannelOutboundHandler,并將出站數(shù)據(jù)從一種格式轉(zhuǎn)換為另一種格式。Netty提供可幫助編寫具有以下功能的編碼器的類:

    · 將消息編碼為字節(jié)。

    · 將消息編碼為消息。

  1. MessageToByteEncoder抽象類

  與ByteToMessageDecoder作用相反,MessageToByteEncoder將消息編碼為字節(jié),其只有encode方法,下圖顯示了ShortToByteEncoder將Shor類型編碼為字節(jié)類型,并寫入ByteBuf,然后轉(zhuǎn)發(fā)給管道中的下一個ChannelOutboundHandler。

  平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  ShortToByteEncoder的代碼如下所示。  

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

public class ShortToByteEncoder extends MessageToByteEncoder<Short> {
    @Override    public void encode(ChannelHandlerContext ctx, Short msg, ByteBuf out)        throws Exception {
        out.writeShort(msg);
    }
}

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  2. MessageToMessageEncoder抽象類

  MessageToMessageEncoder可將一個類型編碼為另一個類型,其只有一個encode方法,下圖展示了IntegerToStringEncoder如何將Integer類型轉(zhuǎn)化為String類型。

  平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  IntegerToStringEncoder的代碼如下所示。  

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

public class IntegerToStringEncoder    extends MessageToMessageEncoder<Integer> {
    @Override    public void encode(ChannelHandlerContext ctx, Integer msg
        List<Object> out) throws Exception {
        out.add(String.valueOf(msg));
    }
}

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  2.3 codec抽象類

  上面單獨討論了解碼器和編碼器,對于出站數(shù)據(jù)使用編碼器,入站數(shù)據(jù)使用解碼器,但可否對于兩者只使用一個類進(jìn)行處理。Netty的codec類可以滿足此需求,每個codec類綁定了編碼器和解碼器,用來處理不同類型的操作,這些類實現(xiàn)了ChannelInboundHandler和ChannelOutboundHandler。

  1. ByteToMessageCodec抽象類

  假如首先需要將字節(jié)類型解碼成消息,然后在編碼成另一種類型,ByteToMessageCodec可以用來處理這種情況,其結(jié)合了ByteToMessageDecoder和MessageToByteEncoder。其包含兩個類的共三個方法,decode、decodeLast、encode。任何請求/響應(yīng)協(xié)議都可以使用ByteToMessageCodec。

  2. MessageToMessageCodec抽象類

  MessageToMessageCodec在消息之間進(jìn)行編碼和解碼操作,其簽名為MessageToMessageCodec<INBOUND_IN,OUTBOUND_IN>,其包含decode和encode兩個方法。decode方法將INBOUND_IN類型轉(zhuǎn)化為OUTBOUND_IN,而encode則相反??蓪NBOUND_IN作為通過線路發(fā)送的消息類型,OUTBOUND_IN作為應(yīng)用程序處理的消息類型。如下代碼展示了具體的使用。 

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

public class WebSocketConvertHandler extends
    MessageToMessageCodec<WebSocketFrame,
    WebSocketConvertHandler.MyWebSocketFrame> {
    @Override    protected void encode(ChannelHandlerContext ctx,
        WebSocketConvertHandler.MyWebSocketFrame msg,
        List<Object> out) throws Exception {
        ByteBuf payload = msg.getData().duplicate().retain();        switch (msg.getType()) {            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, WebSocketFrame msg,
        List<Object> out) throws Exception {
        ByteBuf payload = msg.getData().duplicate().retain();        if (msg instanceof BinaryWebSocketFrame) {
            out.add(new MyWebSocketFrame(
            MyWebSocketFrame.FrameType.BINARY, payload));
        } else if (msg instanceof CloseWebSocketFrame) {
            out.add(new MyWebSocketFrame (
            MyWebSocketFrame.FrameType.CLOSE, payload));
        } else if (msg instanceof PingWebSocketFrame) {
            out.add(new MyWebSocketFrame (
            MyWebSocketFrame.FrameType.PING, payload));
        } else if (msg instanceof PongWebSocketFrame) {
            out.add(new MyWebSocketFrame (
            MyWebSocketFrame.FrameType.PONG, payload));
        } else if (msg instanceof TextWebSocketFrame) {
            out.add(new MyWebSocketFrame (
            MyWebSocketFrame.FrameType.TEXT, payload));
        } else if (msg instanceof ContinuationWebSocketFrame) {
            out.add(new MyWebSocketFrame (
            MyWebSocketFrame.FrameType.CONTINUATION, payload));
        } else {            throw new IllegalStateException(                "Unsupported websocket msg " + msg);
        }
    }    
    public static final class MyWebSocketFrame {        public enum FrameType {
            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;
        }
    }
}

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  3. CombinedChannelDuplexHandler類

  組合解碼器和編碼器可能對可重用性有影響,可以使用CombinedChannelDuplexHandler可通過分別擴展解碼器類和編碼器類的類型來實現(xiàn)編解碼器,而不必直接擴展抽象編解碼器類。

  如下代碼中,ByteToCharDecoder將從ByteBuf中讀取字符。 

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

public class ByteToCharDecoder extends ByteToMessageDecoder {
    @Override    public void decode(ChannelHandlerContext ctx, ByteBuf in,
        List<Object> out) throws Exception {        while (in.readableBytes() >= 2) {
            out.add(in.readChar());
        }
    }
}

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  一次性從ByteBuf中讀取兩個字節(jié)(char由兩個字節(jié)組成),然后裝箱后添加至List中。

  如下代碼中,CharToByteEncoder則將Char轉(zhuǎn)化為字節(jié)類型并寫入ByteBuf中。  

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

public class CharToByteEncoder extends
    MessageToByteEncoder<Character> {
    @Override    public void encode(ChannelHandlerContext ctx, Character msg,
        ByteBuf out) throws Exception {
        out.writeChar(msg);
    }
}

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  我們可以直接使用codec來實現(xiàn)上述代碼示例,假設(shè)已經(jīng)有了ByteToCharDecoder和CharToByteEncoder,其代碼如下所示?!?/p>

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

public class CombinedByteCharCodec extends
    CombinedChannelDuplexHandler<ByteToCharDecoder, CharToByteEncoder> {    public CombinedByteCharCodec() {        super(new ByteToCharDecoder(), new CharToByteEncoder());
    }
}

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

  可以看到代碼非常簡潔便完成了編碼和解碼操作。

三、總結(jié)

  本篇學(xué)習(xí)了Netty中的編碼和解碼操作及其相關(guān)的類型,編解碼器是進(jìn)行數(shù)據(jù)處理的基礎(chǔ)。也謝謝各位園友的觀看~  

PS:如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”,將會是我不竭的動力! 
作者:leesf    掌控之中,才會成功;掌控之外,注定失敗。 
出處:http://www.cnblogs.com/leesf456/ 
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。 

標(biāo)簽: Netty

http://www.cnblogs.com/leesf456/p/6905999.html