15 Netty編解碼框架

1 Netty編解碼框架

在上一節(jié)中皱坛,我們提到TCP的粘包、拆包問(wèn)題靶剑,可以通過(guò)自定義通信協(xié)議的方式來(lái)解決。通信協(xié)議就是通信雙方約定好的數(shù)據(jù)格式池充,發(fā)送方按照這個(gè)數(shù)據(jù)格式來(lái)發(fā)送桩引,接受方按照這個(gè)格式來(lái)解析。典型的協(xié)議包括:定長(zhǎng)協(xié)議收夸、特殊字符分隔符協(xié)議坑匠、報(bào)文頭指定Length等。在確定了使用什么通信協(xié)議的情況下卧惜,發(fā)送方和接收方要完成的工作不同:

編碼:發(fā)送方要將發(fā)送的二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成協(xié)議規(guī)定的格式的二進(jìn)制數(shù)據(jù)流厘灼,稱之為編碼(encode),編碼功能由編碼器(encoder)完成咽瓷。

解碼:接收方需要根據(jù)協(xié)議的格式设凹,對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行解析,稱之為解碼(decode)忱详,解碼功能由解碼器(decoder)完成围来。

編解碼:如果有一種組件,既能編碼匈睁,又能解碼监透,則稱之為編碼解碼器(codec)。這種組件在發(fā)送方和接收方都可以使用航唆。

因此對(duì)于開(kāi)發(fā)人員而言胀蛮,我們要做的工作主要就是2點(diǎn):確定協(xié)議、編寫協(xié)議對(duì)應(yīng)的編碼/解碼器糯钙。

協(xié)議分為公有協(xié)議和私有協(xié)議粪狼。所謂公有協(xié)議,指的是業(yè)界普遍遵循的通信協(xié)議任岸,Netty提供了大量公有協(xié)議數(shù)據(jù)格式的編碼解碼器再榄,從而簡(jiǎn)化開(kāi)發(fā)者的使用。例如:

  • 你想開(kāi)發(fā)一個(gè)基于Netty的郵件服務(wù)器享潜,你將會(huì)發(fā)現(xiàn)Netty針對(duì)POP3困鸥、IMAP、SMTP協(xié)議的數(shù)據(jù)格式都提供了相應(yīng)的編碼解碼器剑按。

  • 如果你想自己開(kāi)發(fā)一個(gè)web服務(wù)器疾就,你會(huì)發(fā)現(xiàn)Netty提供好了HTTP協(xié)議澜术、Websocket協(xié)議相應(yīng)的編解碼器。

  • 甚至猬腰,對(duì)于一些業(yè)界流行的組件鸟废,如redis、memcached這兩個(gè)緩存服務(wù)器姑荷,netty都提供了相應(yīng)的解碼器盒延,因此如果你有意愿的話,可以自己編寫訪問(wèn)redis厢拭、memcached服務(wù)器的client兰英,甚至是開(kāi)源出去給其他人使用。

另外一方面供鸠,可能有的時(shí)候畦贸,我們希望定義一些私有協(xié)議,例如你們的公司需要編寫一個(gè)RPC框架楞捂,這個(gè)框架僅限于公司內(nèi)部使用薄坏。這個(gè)時(shí)候,因?yàn)閰f(xié)議本身還沒(méi)有寨闹,對(duì)應(yīng)的編解碼器也沒(méi)有胶坠,所以我們就要自己實(shí)現(xiàn)。

Netty提供了一套完善的編解碼框架繁堡,不論是公有協(xié)議/私有協(xié)議沈善,我們都可以在這個(gè)框架的基礎(chǔ)上,非常容易的實(shí)現(xiàn)相應(yīng)的編碼/解碼器椭蹄。輸入的數(shù)據(jù)是在ChannelInboundHandler中處理的闻牡,數(shù)據(jù)輸出是在ChannelOutboundHandler中處理的。因此編碼器/解碼器實(shí)際上是這兩個(gè)接口的特殊實(shí)現(xiàn)類绳矩,不過(guò)它們的作用僅僅是編碼/解碼罩润。

2 解碼器

對(duì)于解碼器,Netty中主要提供了抽象基類ByteToMessageDecoderMessageToMessageDecoder

1B171BD0-9287-4212-8A4D-112ACC0DEDF6.png

2.1 抽象類ByteToMessageDecoder

用于將接收到的二進(jìn)制數(shù)據(jù)(Byte)解碼翼馆,得到完整的請(qǐng)求報(bào)文(Message)割以。

通常,ByteToMessageDecoder解碼后內(nèi)容會(huì)得到一個(gè)ByteBuf實(shí)例列表应媚,每個(gè)ByteBuf實(shí)例都包含了一個(gè)完整的報(bào)文信息严沥。你可以直接把這些ByteBuf實(shí)例直接交給之后的ChannelInboundHandler處理,或者將這些包含了完整報(bào)文信息的ByteBuf實(shí)例解析封裝到不同的Java對(duì)象實(shí)例后中姜,再交其處理消玄。不管哪一種情況,之后的ChannelInboundHandler在處理時(shí)不需要在考慮粘包、拆包問(wèn)題莱找。

不過(guò),ByteToMessageDecoder提供的一些常見(jiàn)的實(shí)現(xiàn)類:

  • FixedLengthFrameDecoder:定長(zhǎng)協(xié)議解碼器嗜桌,我們可以指定固定的字節(jié)數(shù)算一個(gè)完整的報(bào)文

  • LineBasedFrameDecoder:行分隔符解碼器奥溺,遇到\n或者\(yùn)r\n,則認(rèn)為是一個(gè)完整的報(bào)文

  • DelimiterBasedFrameDecoder:分隔符解碼器骨宠,與LineBasedFrameDecoder類似浮定,只不過(guò)分隔符可以自己指定

  • LengthFieldBasedFrameDecoder:長(zhǎng)度編碼解碼器,將報(bào)文劃分為報(bào)文頭/報(bào)文體层亿,根據(jù)報(bào)文頭中的Length字段確定報(bào)文體的長(zhǎng)度桦卒,因此報(bào)文提的長(zhǎng)度是可變的

  • JsonObjectDecoder:json格式解碼器,當(dāng)檢測(cè)到匹配數(shù)量的"{" 匿又、”}”或”[””]”時(shí)方灾,則認(rèn)為是一個(gè)完整的json對(duì)象或者json數(shù)組。

這些實(shí)現(xiàn)類碌更,都只是將接收到的二進(jìn)制數(shù)據(jù)裕偿,解碼成包含完整報(bào)文信息的ByteBuf實(shí)例后,就直接交給了之后的ChannelInboundHandler處理痛单。之所以不將ByteBuf中的信息封裝到Java對(duì)象中嘿棘,道理很簡(jiǎn)單,Netty根本不知道開(kāi)發(fā)者想封裝到什么對(duì)象中旭绒,甚至不知道報(bào)文中的具體內(nèi)容是什么鸟妙,因此不如直接把包含了完整報(bào)文信息的ByteBuf實(shí)例,交給開(kāi)發(fā)人員來(lái)自己解析封裝挥吵。

當(dāng)然也有例外重父,例如Netty提供的XmlDecoder,直接將二進(jìn)制數(shù)據(jù)流解析成Aalto XML parser類庫(kù)中定義的xml對(duì)象蔫劣。

我們也可以自定義ByteToMessageDecoder坪郭,此時(shí)需要覆蓋ByteToMessageDecoder的decode方法:

protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;

參數(shù)的作用如下:

  • in:需要解碼的二進(jìn)制數(shù)據(jù)。

  • List<Object> out:解碼后的有效報(bào)文列表脉幢,我們需要將解碼后的報(bào)文添加到這個(gè)List中歪沃。之所以使用一個(gè)List表示,是因?yàn)榭紤]到粘包問(wèn)題嫌松,因此入?yún)⒌膇n中可能包含多個(gè)有效報(bào)文沪曙。當(dāng)然,也有可能發(fā)生了拆包萎羔,in中包含的數(shù)據(jù)還不足以構(gòu)成一個(gè)有效報(bào)文液走,此時(shí)不往List中添加元素即可。

另外特別要注意的是,在解碼時(shí)缘眶,不需要直接調(diào)用ByteBuf的readXXX方法來(lái)讀取數(shù)據(jù)嘱根,而是應(yīng)該首先要判斷能否構(gòu)成一個(gè)有效的報(bào)文。例如對(duì)于以下的案例巷懈,假設(shè)協(xié)議規(guī)定傳輸?shù)臄?shù)據(jù)都是int類型的整數(shù):

WX20180909-193812@2x.png

上圖中顯式輸入的ByteBuf中包含4個(gè)字節(jié)该抒,每個(gè)字節(jié)的值分別為:1,2顶燕,3凑保,4。我們自定義一個(gè)ToIntegerDecoder進(jìn)行解碼涌攻,盡管這里我看到了4個(gè)字節(jié)剛好可以構(gòu)成一個(gè)int類型整數(shù)欧引,但是在真正解碼之前,我們并不知道ByteBuf包含的字節(jié)數(shù)能否構(gòu)成一個(gè)或者多個(gè)完成的有效報(bào)文恳谎,因此需要首先判斷ByteBuf中剩余可讀的字節(jié)芝此,是否大于等于4,如下:

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());
    } }
}

只有在可讀字節(jié)數(shù)>=4的情況下惠爽,我們才進(jìn)行解碼癌蓖,即讀取一個(gè)int,并添加到List中婚肆。

在可讀字節(jié)數(shù)小于4的情況下租副,我們并沒(méi)有做任何處理,假設(shè)剩余可讀字節(jié)數(shù)為3较性,不足以構(gòu)成1個(gè)int用僧。那么父類ByteToMessageDecoder發(fā)現(xiàn)這次解碼List中的元素沒(méi)有變化,則會(huì)對(duì)in中的剩余3個(gè)字節(jié)進(jìn)行緩存赞咙,等待下1個(gè)字節(jié)的到來(lái)责循,之后再回到調(diào)用ToIntegerDecoder的decode方法。

另外攀操,細(xì)心的讀者可能注意到了院仿,在ToIntegerDecoder的decode方法中,每次最多只讀取一個(gè)1個(gè)int速和。如果ByteBuf中的字節(jié)數(shù)很多歹垫,例如為16,那么可以構(gòu)成4個(gè)int颠放,而這里只讀取了1個(gè)int排惨,那么剩余12字節(jié)怎么辦?這個(gè)其實(shí)不用擔(dān)心碰凶,ByteToMessageDecoder再每次回調(diào)子類的decode方法之后暮芭,都會(huì)判斷輸入的ByteBuf中是否還有剩余字節(jié)可讀鹿驼,如果還有,會(huì)再次回調(diào)子類的decode方法辕宏,直到某個(gè)回調(diào)decode方法List中的元素個(gè)數(shù)沒(méi)有變化時(shí)才停止畜晰,元素個(gè)數(shù)沒(méi)有變化,實(shí)際上意味著子類已經(jīng)沒(méi)有辦法從剩余的字節(jié)中讀取一個(gè)有效報(bào)文瑞筐。

由于存在剩余可讀字節(jié)時(shí)舷蟀,ByteToMessageDecoder會(huì)自動(dòng)再次回調(diào)子類decode方法,因此筆者建議在實(shí)現(xiàn)ByteToMessageDecoder時(shí)面哼,decode方法每次只解析一個(gè)有效報(bào)文即可,沒(méi)有必要一次全部解析出來(lái)扫步。

2.2 抽象類MessageToMessageDecoder

ByteToMessageDecoder是將二進(jìn)制流進(jìn)行解碼后魔策,得到有效報(bào)文。而MessageToMessageDecoder則是將一個(gè)本身就包含完整報(bào)文信息的對(duì)象轉(zhuǎn)換成另一個(gè)Java對(duì)象河胎。

舉例來(lái)說(shuō)闯袒,前面介紹了ByteToMessageDecoder的部分子類解碼后,會(huì)直接將包含了報(bào)文完整信息的ByteBuf實(shí)例交由之后的ChannelInboundHandler處理游岳,此時(shí)政敢,你可以在ChannelPipeline中,再添加一個(gè)MessageToMessageDecoder胚迫,將ByteBuf中的信息解析后封裝到Java對(duì)象中喷户,簡(jiǎn)化之后的ChannelInboundHandler的操作。

另外访锻,一些場(chǎng)景下褪尝,有可能你的報(bào)文信息已經(jīng)封裝到了Java對(duì)象中,但是還要繼續(xù)轉(zhuǎn)成另外的Java對(duì)象期犬,因此一個(gè)MessageToMessageDecoder后面可能還跟著另一個(gè)MessageToMessageDecoder河哑。一個(gè)比較容易的理解的類比案例是Java Web編程,通彻昊ⅲ客戶端瀏覽器發(fā)送過(guò)來(lái)的二進(jìn)制數(shù)據(jù)璃谨,已經(jīng)被web容器(如tomcat)解析成了一個(gè)HttpServletRequest對(duì)象,但是我們還是需要將HttpServletRequest中的數(shù)據(jù)提取出來(lái)鲤妥,封裝成我們自己的POJO類佳吞,也就是從一個(gè)Java對(duì)象(HttpServletRequest)轉(zhuǎn)換成另一個(gè)Java對(duì)象(我們的POJO類)。

除了一些公有協(xié)議的解碼器外旭斥,Netty提供的MessageToMessageDecoder實(shí)現(xiàn)類較少容达,主要是:

StringDecoder:用于將包含完整的報(bào)文信息的ByteBuf轉(zhuǎn)換成字符串。我們可以將其與ByteToMessageDecoder的一些實(shí)現(xiàn)類聯(lián)合使用垂券,以LineBasedFrameDecoder為例花盐,其將二進(jìn)制數(shù)據(jù)流按行分割后封裝到ByteBuf中羡滑。我們可以在其之后再添加一個(gè)StringDecoder,將ByteBuf中的數(shù)據(jù)轉(zhuǎn)換成字符串算芯。

Base64Decoder:用于Base64編碼柒昏。例如,前面我們提到LineBasedFrameDecoder熙揍、DelimiterBasedFrameDecoder等ByteToMessageDecoder實(shí)現(xiàn)類职祷,是使用特殊字符作為分隔符作為解碼的條件。但是如果報(bào)文內(nèi)容中如果本身就包含了分隔符届囚,那么解碼就會(huì)出錯(cuò)有梆。此時(shí),對(duì)于發(fā)送方意系,可以先使用Base64Encoder對(duì)報(bào)文內(nèi)容進(jìn)行Base64編碼泥耀,然后我們選擇Base64編碼包含的64種字符之外的其他特殊字符作為分隔符。在解碼時(shí)蛔添,首先特殊字符進(jìn)行分割痰催,然后通過(guò)Base64Decoder解碼得到原始的二進(jìn)制字節(jié)流。

MessageToMessageDecoder的類聲明如下:

public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter

其中泛型參數(shù)I表示我們要解碼的消息類型迎瞧。例前面夸溶,我們?cè)赥oIntegerDecoder中,把二進(jìn)制字節(jié)流轉(zhuǎn)換成了一個(gè)int類型的整數(shù)凶硅。

類似的缝裁,MessageToMessageDecoder也有一個(gè)decode方法需要覆蓋 ,如下:

/**
* 參數(shù)msg足绅,需要進(jìn)行解碼的參數(shù)压语。例如ByteToMessageDecoder解碼后的得到的包含完整報(bào)文信息ByteBuf
* List<Object> out參數(shù):將msg經(jīng)過(guò)解析后得到的java對(duì)象,添加到放到List<Object> out中
*/
protected abstract void decode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;

例如编检,現(xiàn)在我們想編寫一個(gè)IntegerToStringDecoder胎食,把前面編寫的ToIntegerDecoder輸出的int參數(shù)轉(zhuǎn)換成字符串,此時(shí)泛型I就應(yīng)該是Integer類型允懂。

WX20180909-193852@2x.png

IntegerToStringDecoder源碼如下所示:

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í)我們應(yīng)該按照如下順序組織ChannelPipieline中ToIntegerDecoder和IntegerToStringDecoder 的關(guān)系:

ChannelPipieline ch=....
ch.addLast(new ToIntegerDecoder());
ch.addLast(new IntegerToStringDecoder());

也就是說(shuō)厕怜,前一個(gè)ChannelInboudHandler輸出的參數(shù)類型,就是后一個(gè)ChannelInboudHandler的輸入類型蕾总。

特別需要注意的一點(diǎn)是粥航,如果我們指定MessageToMessageDecoder的泛型參數(shù)為ByteBuf,表示其可以直接針對(duì)ByteBuf進(jìn)行解碼生百,那么其是否能替代ByteToMessageDecoder呢递雀?

答案是不可以的。因?yàn)锽yteToMessageDecoder除了進(jìn)行解碼蚀浆,還要會(huì)對(duì)不足以構(gòu)成一個(gè)完整數(shù)據(jù)的報(bào)文拆包數(shù)據(jù)(拆包)進(jìn)行緩存缀程。而MessageToMessageDecoder則沒(méi)有這樣的邏輯搜吧。

因此通常的使用建議是,使用一個(gè)ByteToMessageDecoder進(jìn)行粘包杨凑、拆包處理滤奈,得到完整的有效報(bào)文的ByteBuf實(shí)例,然后交由之后的一個(gè)或者多個(gè)MessageToMessageDecoder對(duì)ByteBuf實(shí)例中的數(shù)據(jù)進(jìn)行解析撩满,轉(zhuǎn)換成POJO類蜒程。

3 編碼器

與ByteToMessageDecoder和MessageToMessageDecoder相對(duì)應(yīng),Netty提供了對(duì)應(yīng)的編碼器實(shí)現(xiàn)MessageToByteEncoderMessageToMessageEncoder伺帘,二者都實(shí)現(xiàn)ChannelOutboundHandler接口昭躺。

C62F858C-5306-4456-B20B-D07648BE4594.png

相對(duì)來(lái)說(shuō),編碼器比解碼器的實(shí)現(xiàn)要更加簡(jiǎn)單伪嫁,原因在于解碼器除了要按照協(xié)議解析數(shù)據(jù)窍仰,還要要處理粘包、拆包問(wèn)題礼殊;而編碼器只要將數(shù)據(jù)轉(zhuǎn)換成協(xié)議規(guī)定的二進(jìn)制格式發(fā)送即可。

3.1 抽象類MessageToByteEncoder

MessageToByteEncoder也是一個(gè)泛型類针史,泛型參數(shù)I表示將需要編碼的對(duì)象的類型晶伦,編碼的結(jié)果是將信息轉(zhuǎn)換成二進(jìn)制流放入ByteBuf中。子類通過(guò)覆寫其抽象方法encode啄枕,來(lái)實(shí)現(xiàn)編碼婚陪,如下所示:

public abstract class MessageToByteEncoder<I> extends ChannelOutboundHandlerAdapter {
....
     protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
}

可以看到,MessageToByteEncoder的輸出對(duì)象out是一個(gè)ByteBuf實(shí)例频祝,我們應(yīng)該將泛型參數(shù)msg包含的信息寫入到這個(gè)out對(duì)象中泌参。

MessageToByteEncoder使用案例:

public class IntegerToByteEncoder extends MessageToByteEncoder<Integer> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) throws Exception {
        out.writeInt(msg);//將Integer轉(zhuǎn)成二進(jìn)制字節(jié)流寫入ByteBuf中
    }
}

3.2 抽象類MessageToMessageEncoder

MessageToMessageEncoder同樣是一個(gè)泛型類,泛型參數(shù)I表示將需要編碼的對(duì)象的類型常空,編碼的結(jié)果是將信息放到一個(gè)List中沽一。子類通過(guò)覆寫其抽象方法encode,來(lái)實(shí)現(xiàn)編碼漓糙,如下所示:

public abstract class MessageToMessageEncoder<I> extends ChannelOutboundHandlerAdapter {
   ...
   protected abstract void encode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;
   ...
}

與MessageToByteEncoder不同的铣缠,MessageToMessageEncoder編碼后的結(jié)果放到的out參數(shù)類型是一個(gè)List中。例如昆禽,你一次發(fā)送2個(gè)報(bào)文蝗蛙,因此msg參數(shù)中實(shí)際上包含了2個(gè)報(bào)文,因此應(yīng)該解碼出兩個(gè)報(bào)文對(duì)象放到List中醉鳖。

MessageToMessageEncoder提供的常見(jiàn)子類包括:

  • LineEncoder:按行編碼捡硅,給定一個(gè)CharSequence(如String),在其之后添加換行符\n或者\(yùn)r\n盗棵,并封裝到ByteBuf進(jìn)行輸出壮韭,與LineBasedFrameDecoder相對(duì)應(yīng)北发。

  • Base64Encoder:給定一個(gè)ByteBuf,得到對(duì)其包含的二進(jìn)制數(shù)據(jù)進(jìn)行Base64編碼后的新的ByteBuf進(jìn)行輸出泰涂,與Base64Decoder相對(duì)應(yīng)鲫竞。

  • LengthFieldPrepender:給定一個(gè)ByteBuf,為其添加報(bào)文頭Length字段逼蒙,得到一個(gè)新的ByteBuf進(jìn)行輸出从绘。Length字段表示報(bào)文長(zhǎng)度,與LengthFieldBasedFrameDecoder相對(duì)應(yīng)是牢。

  • StringEncoder:給定一個(gè)CharSequence(如:StringBuilder僵井、StringBuffer、String等)驳棱,將其轉(zhuǎn)換成ByteBuf進(jìn)行輸出批什,與StringDecoder對(duì)應(yīng)。
    細(xì)心的讀者注意到了社搅,這些MessageToMessageEncoder實(shí)現(xiàn)類最終輸出的都是ByteBuf驻债,因?yàn)樽罱K在網(wǎng)絡(luò)上傳輸?shù)亩家嵌M(jìn)制數(shù)據(jù)。

在后面的章節(jié)中形葬,我們將會(huì)對(duì)上述提到的編碼/解碼器如何使用進(jìn)行詳細(xì)的介紹合呐。

4 編碼解碼器Codec

編碼解碼器同時(shí)具有編碼與解碼功能,特點(diǎn)同時(shí)實(shí)現(xiàn)了ChannelInboundHandler和ChannelOutboundHandler接口笙以,因此在數(shù)據(jù)輸入和輸出時(shí)都能進(jìn)行處理淌实。Netty提供提供了一個(gè)ChannelDuplexHandler適配器類,編碼解碼器的抽象基類 ByteToMessageCodec 猖腕、MessageToMessageCodec都繼承與此類拆祈,如下:

0BF45DAD-EA36-44E1-8946-A844F2BBCC6D.png

ByteToMessageCodec內(nèi)部維護(hù)了一個(gè)ByteToMessageDecoder和一個(gè)MessageToByteEncoder實(shí)例,可以認(rèn)為是二者的功集合倘感,泛型參數(shù)I是接受的編碼類型:

public abstract class ByteToMessageCodec<I> extends ChannelDuplexHandler {
    private final TypeParameterMatcher outboundMsgMatcher;
    private final MessageToByteEncoder<I> encoder;
    private final ByteToMessageDecoder decoder = new ByteToMessageDecoder(){…}
  
    ...
    protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
    protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
    ...
}

MessageToMessageCodec內(nèi)部維護(hù)了一個(gè)MessageToMessageDecoder和一個(gè)MessageToMessageEncoder實(shí)例放坏,可以認(rèn)為是二者的功集合,泛型參數(shù)INBOUND_INOUTBOUND_IN分別表示需要解碼和編碼的數(shù)據(jù)類型老玛。

public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> extends ChannelDuplexHandler {
   private final MessageToMessageEncoder<Object> encoder= ...
   private final MessageToMessageDecoder<Object> decoder =…
   ...
   protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, List<Object> out) throws Exception;
   protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, List<Object> out) throws Exception;
}

由于前面已經(jīng)分析了編碼器和解碼器轻姿,這里對(duì)于編碼解碼器不再進(jìn)行過(guò)多說(shuō)明。在后面章節(jié)中逻炊,我們將詳細(xì)介紹不同編碼互亮、解碼器的詳細(xì)使用方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末余素,一起剝皮案震驚了整個(gè)濱河市豹休,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌桨吊,老刑警劉巖威根,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凤巨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡洛搀,警方通過(guò)查閱死者的電腦和手機(jī)敢茁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)留美,“玉大人彰檬,你說(shuō)我怎么就攤上這事』牙” “怎么了逢倍?”我有些...
    開(kāi)封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)景图。 經(jīng)常有香客問(wèn)我较雕,道長(zhǎng),這世上最難降的妖魔是什么挚币? 我笑而不...
    開(kāi)封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任亮蒋,我火速辦了婚禮,結(jié)果婚禮上妆毕,老公的妹妹穿的比我還像新娘慎玖。我一直安慰自己,他們只是感情好设塔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著远舅,像睡著了一般闰蛔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上图柏,一...
    開(kāi)封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天序六,我揣著相機(jī)與錄音,去河邊找鬼蚤吹。 笑死例诀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的裁着。 我是一名探鬼主播繁涂,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼二驰!你這毒婦竟也來(lái)了扔罪?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤桶雀,失蹤者是張志新(化名)和其女友劉穎矿酵,沒(méi)想到半個(gè)月后唬复,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡全肮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年敞咧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辜腺。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡休建,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出哪自,到底是詐尸還是另有隱情丰包,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布壤巷,位于F島的核電站邑彪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏胧华。R本人自食惡果不足惜寄症,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望矩动。 院中可真熱鬧有巧,春花似錦、人聲如沸悲没。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)示姿。三九已至甜橱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間栈戳,已是汗流浹背岂傲。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留子檀,地道東北人镊掖。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像褂痰,于是被迫代替她去往敵國(guó)和親亩进。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容