一、前言
前面已經(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類型),整個過程如下圖所示。
一次從ByteBuf中讀取四個字節(jié)解析成一個int類型,并添加到List中,當(dāng)讀取完成后,將會被傳遞至下個ChannelHandler中,下面是ToIntegerDecoder的源代碼。
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()); } } }
2. ReplayingDecoder抽象類
ReplayingDecoder繼承ByteToMessageDecoder類,并且不再需要調(diào)用readableBytes()方法,其通過自定義的ReplayingDecoderBuffer來實現(xiàn)該功能,其代碼如下所示。
public class ToIntegerDecoder2 extends ReplayingDecoder<Void> { @Override public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { out.add(in.readInt()); } }
其中Void表示不執(zhí)行任何操作,即不管理任何狀態(tài)類型。
3. MessageToMessageDecoder抽象類
可以使用MessageToMessageDecoder在消息格式之間進(jìn)行轉(zhuǎn)換,僅需要實現(xiàn)其decode方法。如IntegerToStringDecoder繼承MessageToMessageDecoder,其將Integer類型轉(zhuǎn)化為對應(yīng)的String表示,轉(zhuǎn)化圖如下圖所示。
下面是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)); } }
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é)議時,此類保護會尤為重要。
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 ... } }
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。
ShortToByteEncoder的代碼如下所示。
public class ShortToByteEncoder extends MessageToByteEncoder<Short> { @Override public void encode(ChannelHandlerContext ctx, Short msg, ByteBuf out) throws Exception { out.writeShort(msg); } }
2. MessageToMessageEncoder抽象類
MessageToMessageEncoder可將一個類型編碼為另一個類型,其只有一個encode方法,下圖展示了IntegerToStringEncoder如何將Integer類型轉(zhuǎn)化為String類型。
IntegerToStringEncoder的代碼如下所示。
public class IntegerToStringEncoder extends MessageToMessageEncoder<Integer> { @Override public void encode(ChannelHandlerContext ctx, Integer msg List<Object> out) throws Exception { out.add(String.valueOf(msg)); } }
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)用程序處理的消息類型。如下代碼展示了具體的使用。
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; } } }
3. CombinedChannelDuplexHandler類
組合解碼器和編碼器可能對可重用性有影響,可以使用CombinedChannelDuplexHandler可通過分別擴展解碼器類和編碼器類的類型來實現(xiàn)編解碼器,而不必直接擴展抽象編解碼器類。
如下代碼中,ByteToCharDecoder將從ByteBuf中讀取字符。
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()); } } }
一次性從ByteBuf中讀取兩個字節(jié)(char由兩個字節(jié)組成),然后裝箱后添加至List中。
如下代碼中,CharToByteEncoder則將Char轉(zhuǎn)化為字節(jié)類型并寫入ByteBuf中。
public class CharToByteEncoder extends MessageToByteEncoder<Character> { @Override public void encode(ChannelHandlerContext ctx, Character msg, ByteBuf out) throws Exception { out.writeChar(msg); } }
我們可以直接使用codec來實現(xiàn)上述代碼示例,假設(shè)已經(jīng)有了ByteToCharDecoder和CharToByteEncoder,其代碼如下所示?!?/p>
public class CombinedByteCharCodec extends CombinedChannelDuplexHandler<ByteToCharDecoder, CharToByteEncoder> { public CombinedByteCharCodec() { super(new ByteToCharDecoder(), new CharToByteEncoder()); } }
可以看到代碼非常簡潔便完成了編碼和解碼操作。
三、總結(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