W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
在本節(jié)內(nèi)容中,我們會提供幾個類用于 decoder 的實現(xiàn),并且通過一些具體的例子來告訴大家什么情況下可以使用它們以及它們的使用方法。
Netty 提供了豐富的解碼器抽象基類,我們可以很容易的實現(xiàn)這些基類來自定義解碼器。主要分兩類:
decoder 負責(zé)將“入站”數(shù)據(jù)從一種格式轉(zhuǎn)換到另一種格式,Netty的解碼器是一種 ChannelInboundHandler 的抽象實現(xiàn)。實踐中使用解碼器很簡單,就是將入站數(shù)據(jù)轉(zhuǎn)換格式后傳遞到 ChannelPipeline 中的下一個ChannelInboundHandler 進行處理;這樣的處理是很靈活的,我們可以將解碼器放在 ChannelPipeline 中,重用邏輯。
ByteToMessageDecoder 是用于將字節(jié)轉(zhuǎn)為消息(或其他字節(jié)序列)。
你不能確定遠端是否會一次發(fā)送完一個完整的“信息”,因此這個類會緩存入站的數(shù)據(jù),直到準備好了用于處理。表7.1說明了它的兩個最重要的方法。
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 |
handling
假設(shè)我們接收一個包含簡單整數(shù)的字節(jié)流,每個都單獨處理。在本例中,我們將從入站 ByteBuf 讀取每個整數(shù)并將其傳遞給 pipeline 中的下一個ChannelInboundHandler?!敖獯a”字節(jié)流成整數(shù)我們將擴展ByteToMessageDecoder,實現(xiàn)類為“ToIntegerDecoder”,如圖7.1所示。
Figure 7.1 ToIntegerDecoder
每次從入站的 ByteBuf 讀取四個字節(jié),解碼成整形,并添加到一個 List (本例是指 Integer),當不能再添加數(shù)據(jù)到 list 時,它所包含的內(nèi)容就會被發(fā)送到下個 ChannelInboundHandler
Listing 7.1 ByteToMessageDecoder that decodes to Integer
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
}
}
}
盡管 ByteToMessageDecoder 簡化了這個模式,你會發(fā)現(xiàn)它還是有點煩人,在實際的讀操作(這里 readInt())之前,必須要驗證輸入的 ByteBuf 要有足夠的數(shù)據(jù)。在下一節(jié)中,我們將看看 ReplayingDecoder,一個特殊的解碼器。
章節(jié)5和6中提到,應(yīng)該特別注意引用計數(shù)。對于編碼器和解碼器來說,這個過程非常簡單。一旦一個消息被編碼或解碼它自動被調(diào)用ReferenceCountUtil.release(message) 。如果你稍后還需要用到這個引用而不是馬上釋放,你可以調(diào)用 ReferenceCountUtil.retain(message)。這將增加引用計數(shù),防止消息被釋放。
ReplayingDecoder 是 byte-to-message 解碼的一種特殊的抽象基類,讀取緩沖區(qū)的數(shù)據(jù)之前需要檢查緩沖區(qū)是否有足夠的字節(jié),使用ReplayingDecoder就無需自己檢查;若ByteBuf中有足夠的字節(jié),則會正常讀?。蝗魶]有足夠的字節(jié)則會停止解碼。
ByteToMessageDecoder 和 ReplayingDecoder
注意到 ReplayingDecoder 繼承自 ByteToMessageDecoder ,所以API 跟表 7.1 是相同的
也正因為這樣的包裝使得 ReplayingDecoder 帶有一定的局限性:
如果這些限制是可以接受你可能更喜歡使用 ReplayingDecoder。下面是一個簡單的準則:
如果不引入過多的復(fù)雜性 使用 ByteToMessageDecoder 。否則,使用ReplayingDecoder。
Listing 7.2 ReplayingDecoder
public class ToIntegerDecoder2 extends ReplayingDecoder<Void> { //1
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
throws Exception {
out.add(in.readInt()); //2
}
}
如果我們比較清單7.1和7.2很明顯,實現(xiàn)使用 ReplayingDecoder 更簡單。
更多 Decoder
下面是更加復(fù)雜的使用場景: io.netty.handler.codec.LineBasedFrameDecoder 通過結(jié)束控制符("\n" 或 "\r\n").解析入站數(shù)據(jù)。 io.netty.handler.codec.http.HttpObjectDecoder 用于 HTTP 數(shù)據(jù)解碼
用于從一種消息解碼為另外一種消息(例如,POJO 到 POJO),下表展示了方法:
Table 7.2 MessageToMessageDecoder API
方法名稱 | 描述 |
---|---|
decode | decode is the only abstract method you need to implement. It is called for each inbound message to be decoded to another format . The decoded messages are then passed to the next ChannelInboundHandler 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 |
handling
將 Integer 轉(zhuǎn)為 String,我們提供了 IntegerToStringDecoder,繼承自 MessageToMessageDecoder。
因為這是一個參數(shù)化的類,實現(xiàn)的簽名是:
public class IntegerToStringDecoder extends MessageToMessageDecoder<Integer>
decode() 方法的簽名是
protected void decode( ChannelHandlerContext ctx,
Integer msg, List<Object> out ) throws Exception
也就是說,入站消息是按照在類定義中聲明的參數(shù)類型(這里是 Integer) 而不是 ByteBuf來解析的。在之前的例子,解碼消息(這里是String)將被添加到List
Figure 7.2 IntegerToStringDecoder
實現(xiàn)如下:
Listing 7.3 MessageToMessageDecoder - Integer to String
public class IntegerToStringDecoder extends
MessageToMessageDecoder<Integer> { //1
@Override
public void decode(ChannelHandlerContext ctx, Integer msg, List<Object> out)
throws Exception {
out.add(String.valueOf(msg)); //2
}
}
正如我們上面指出的,decode()方法的消息參數(shù)的類型是由給這個類指定的泛型的類型(這里是Integer)確定的。
HttpObjectAggregator
更多復(fù)雜的示例,請查看類 io.netty.handler.codec.http.HttpObjectAggregator,繼承自MessageToMessageDecoder
Netty 是異步框架需要緩沖區(qū)字節(jié)在內(nèi)存中,直到你能夠解碼它們。因此,你不能讓你的解碼器緩存太多的數(shù)據(jù)以免耗盡可用內(nèi)存。為了解決這個共同關(guān)心的問題, Netty 提供了一個 TooLongFrameException ,通常由解碼器在幀太長時拋出。
為了避免這個問題,你可以在你的解碼器里設(shè)置一個最大字節(jié)數(shù)閾值,如果超出,將導(dǎo)致 TooLongFrameException 拋出(并由 ChannelHandler.exceptionCaught() 捕獲)。然后由譯碼器的用戶決定如何處理它。雖然一些協(xié)議,比如 HTTP、允許這種情況下有一個特殊的響應(yīng),有些可能沒有,事件唯一的選擇可能就是關(guān)閉連接。
如清單7.4所示 ByteToMessageDecoder 可以利用 TooLongFrameException 通知其他 ChannelPipeline 中的 ChannelHandler。
Listing 7.4 SafeByteToMessageDecoder encodes shorts into a ByteBuf
public class SafeByteToMessageDecoder extends ByteToMessageDecoder { //1
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) { //2
in.skipBytes(readable); //3
throw new TooLongFrameException("Frame too big!");
}
// do something
}
}
這種保護是很重要的,尤其是當你解碼一個有可變幀大小的協(xié)議的時候。
好了,解碼器常見用例的解釋以及 Netty 提供的用于構(gòu)建它們它們的抽象基類就講到這了。但是理解完解碼器只是一方面而已,另一方面,我們還需要完成 Codec API,所以在下一章中我們將介紹編碼器,它是用來轉(zhuǎn)換消息到出站數(shù)據(jù)的。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: