Netty入門教程3——Decoder和Encoder

? Netty強(qiáng)大的地方,是他能方便的實(shí)現(xiàn)自定義協(xié)議的網(wǎng)絡(luò)傳輸。在上一篇文章中,通過使用Netty封裝好的工具類,實(shí)現(xiàn)了簡單的http服務(wù)器。在接下來的文章中,我們看看怎么使用他來搭建自定義協(xié)議的服務(wù)器。要做到這點(diǎn),第一步要做的,就是要自定義編碼器和解碼器,這就是我們這一章主要講的內(nèi)容。

什么是Decoder和Encoder

? 在學(xué)習(xí)Decoder和Encoder之前,首先要了解他們在具體是個什么東西。在Netty里面,有四個核心概念,這個在第一篇文章提到的,他們的分別是:

  • Channel,一個客戶端與服務(wù)器通信的通道

  • ChannelHandler,業(yè)務(wù)邏輯處理器,分為ChannelInboundHandler和ChannelOutboundHandler

    • ChannelInboundHandler,輸入數(shù)據(jù)處理器
    • ChannelOutboundHandler,輸出業(yè)務(wù)處理器

    通常情況下,業(yè)務(wù)邏輯都是存在于ChannelHandler之中

  • ChannelPipeline,用于存放ChannelHandler的容器

  • ChannelContext,通信管道的上下文

    他們之間的交流流程如下圖:


    Channel關(guān)系圖

他們的交互流程是:

  1. 事件傳遞給 ChannelPipeline 的第一個 ChannelHandler
  2. ChannelHandler 通過關(guān)聯(lián)的 ChannelHandlerContext 傳遞事件給 ChannelPipeline 中的 下一個
  3. ChannelHandler 通過關(guān)聯(lián)的 ChannelHandlerContext 傳遞事件給 ChannelPipeline 中的 下一個

而我們本文所需要詳細(xì)講的Decoder和Encoder,他們分別就是ChannelInboundHandler和ChannelOutboundHandler,分別用于在數(shù)據(jù)流進(jìn)來的時候?qū)⒆止?jié)碼轉(zhuǎn)換為消息對象和數(shù)據(jù)流出去的時候?qū)⑾ο筠D(zhuǎn)換為字節(jié)碼。

Encoder

? Encoder最重要的實(shí)現(xiàn)類是MessageToByteEncoder<T>,這個類的作用就是將消息實(shí)體T從對象轉(zhuǎn)換成byte,寫入到ByteBuf,然后再丟給剩下的ChannelOutboundHandler傳給客戶端,流程圖如下:


Encoder流程圖

Table 7.3 MessageToByteEncoder API

方法名稱 描述
encode The encode method is the only abstract method you need to implement. It is called with the outbound message, which this class will encodes to a ByteBuf. The ByteBuf is then forwarded to the next ChannelOutboundHandler in the ChannelPipeline.

encode方法是繼承MessageToByteEncoder唯一需要重寫的方法,可見其簡單程度。也是因?yàn)镋ncoder相比于Decoder更為簡單,在這里也不多做贅述,直接上代碼:

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

Decoder

? 和Encoder一樣,decoder就是在服務(wù)端收到數(shù)據(jù)的時候,將字節(jié)流轉(zhuǎn)換為實(shí)體對象Message。但是和Encoder的處理邏輯不一樣,數(shù)據(jù)傳到服務(wù)端有可能不是一次請求就能完成的,中間可能需要經(jīng)過幾次數(shù)據(jù)傳輸,并且每一次傳輸傳多少數(shù)據(jù)也是不確定的,所以它有兩個重要方法:

Table 7.1 ByteToMessageDecoder API

方法名稱 描述
decode This is the only abstract method you need to implement. It is called with a ByteBuf having the incoming bytes and a List into which decoded messages are added. decode() is called repeatedly until the List is empty on return. The contents of the List are then passed to the next handler in the pipeline.
decodeLast The default implementation provided simply calls decode(). This method is called once, when the Channel goes inactive. Override to provide special

? decode和decodeLast的不同之處,在于他們的調(diào)用時機(jī)不同,正如描述所說,decodeLast只有在Channel的生命周期結(jié)束之前會調(diào)用一次,默認(rèn)是調(diào)用decode方法。

? 同樣是ToInteger的解碼器,他的代碼如下:

public class ToIntegerDecoder extends ByteToMessageDecoder {  //1

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

? 從這段代碼可以看出,因?yàn)椴恢肋@次請求發(fā)過來多少數(shù)據(jù),所以每次都要判斷byte長度夠不夠4,如果你的數(shù)據(jù)長度更長,且不固定的話,這里的邏輯會變得非常復(fù)雜。所以在這里介紹另一個我們常用的解碼器 :ReplayingDecoder。

ReplayingDecoder

? ReplayingDecoder 是 byte-to-message 解碼的一種特殊的抽象基類,讀取緩沖區(qū)的數(shù)據(jù)之前需要檢查緩沖區(qū)是否有足夠的字節(jié),使用ReplayingDecoder就無需自己檢查;若ByteBuf中有足夠的字節(jié),則會正常讀??;若沒有足夠的字節(jié)則會停止解碼。

? RelayingDecoder在使用的時候需要搞清楚的兩個方法是checkpoint(S s)和state(),其中checkpoint的參數(shù)S,代表的是ReplayingDecoder所處的狀態(tài),一般是枚舉類型。RelayingDecoder是一個有狀態(tài)的Handler,狀態(tài)表示的是它目前讀取到了哪一步,checkpoint(S s)是設(shè)置當(dāng)前的狀態(tài),state()是獲取當(dāng)前的狀態(tài)。

? 在這里我們模擬一個簡單的Decoder,假設(shè)每個包包含length:int和content:String兩個數(shù)據(jù),其中l(wèi)ength可以為0,代表一個空包,大于0的時候代表content的長度。代碼如下:

public class LiveDecoder extends ReplayingDecoder<LiveDecoder.LiveState> { //1

    public enum LiveState { //2
        LENGTH,
        CONTENT
    }

    private LiveMessage message = new LiveMessage();

    public LiveDecoder() {
        super(LiveState.LENGTH); // 3
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        switch (state()) { // 4
            case LENGTH:
                int length = byteBuf.readInt();
                if (length > 0) {
                    checkpoint(LiveState.CONTENT); // 5
                } else {
                    list.add(message); // 6
                }
                break;
            case CONTENT:
                byte[] bytes = new byte[message.getLength()];
                byteBuf.readBytes(bytes);
                String content = new String(bytes);
                message.setContent(content);
                list.add(message);
                break;
            default:
                throw new IllegalStateException("invalid state:" + state());
        }
    }
}
  1. 繼承ReplayingDecoder,泛型LiveState,用來表示當(dāng)前讀取的狀態(tài)
  2. 描述LiveState,有讀取長度和讀取內(nèi)容兩個狀態(tài)
  3. 初始化的時候設(shè)置為讀取長度的狀態(tài)
  4. 讀取的時候通過state()方法來確定當(dāng)前處于什么狀態(tài)
  5. 如果讀取出來的長度大于0,則設(shè)置為讀取內(nèi)容狀態(tài),下一次讀取的時候則從這個位置開始
  6. 讀取完成,往結(jié)果里面放解析好的數(shù)據(jù)

? 以上就是ReplayingDecoder的使用方法,他比ByteToMessageDecoder更加靈活,能夠通過巧妙的方式來處理復(fù)雜的業(yè)務(wù)邏輯,但是也是因?yàn)檫@個原因,使得ReplayingDecoder帶有一定的局限性:

  • 不是所有的標(biāo)準(zhǔn) ByteBuf 操作都被支持,如果調(diào)用一個不支持的操作會拋出 UnreplayableOperationException

  • ReplayingDecoder 略慢于 ByteToMessageDecoder

    所以,如果不引入過多的復(fù)雜性 使用 ByteToMessageDecoder 。否則,使用ReplayingDecoder。

MessageToMessage

? Encoder和Decoder除了能完成Byte和Message的相互轉(zhuǎn)換之外,為了處理復(fù)雜的業(yè)務(wù)邏輯,還能幫助使用者完成Message和Message的相互轉(zhuǎn)換,我們熟悉的Http協(xié)議的處理,其中就用到了很多MessageToMessage的派生類。

? 因?yàn)槭褂梅椒ê鸵陨系腄ecoder/Encoder類似,在這里就不多做贅述了。

?

? 以上是我在學(xué)習(xí)Netty過程中的一些筆記,其中部分內(nèi)容源自Netty實(shí)戰(zhàn)精髓,如有理解不當(dāng)之處,歡迎指出,一起討論。

Netty入門教程4——如何實(shí)現(xiàn)長連接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容