日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

netty系列之:自定义编码和解码器要注意的问题

發布時間:2024/2/28 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 netty系列之:自定义编码和解码器要注意的问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 簡介
  • 自定義編碼器和解碼器的實現
  • ReplayingDecoder
  • 總結

簡介

在之前的系列文章中,我們提到了netty中的channel只接受ByteBuf類型的對象,如果不是ByteBuf對象的話,需要用編碼和解碼器對其進行轉換,今天來聊一下netty自定義的編碼和解碼器實現中需要注意的問題。

自定義編碼器和解碼器的實現

在介紹netty自帶的編碼器和解碼器之前,告訴大家怎么實現自定義的編碼器和解碼器。

netty中所有的編碼器和解碼器都是從ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter衍生而來的。

對于ChannelOutboundHandlerAdapter來說,最重要的兩個類是MessageToByteEncoder 和 MessageToMessageEncoder 。

MessageToByteEncoder是將消息編碼成為ByteBuf,這個類也是我們自定義編碼最常用的類,直接繼承這個類并實現encode方法即可。注意到這個類有一個泛型,這個泛型指定的就是消息的對象類型。

例如我們想將Integer轉換成為ByteBuf,可以這樣寫:

public class IntegerEncoder extends MessageToByteEncoder<Integer> {@Overridepublic void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out)throws Exception {out.writeInt(msg);}}

MessageToMessageEncoder是在消息和消息之間進行轉換,因為消息并不能直接寫入到channel中,所以需要和MessageToByteEncoder配合使用。

下面是一個Integer到String的例子:

public class IntegerToStringEncoder extendsMessageToMessageEncoder<Integer> {@Overridepublic void encode(ChannelHandlerContext ctx, Integer message, List<Object> out)throws Exception {out.add(message.toString());}}

對于ChannelInboundHandlerAdapter來說,最重要的兩個類是ByteToMessageDecoder和MessageToMessageDecoder 。

ByteToMessageDecoder是將ByteBuf轉換成對應的消息類型,我們需要繼承這個類,并實現decode方法,下面是一個從ByteBuf中讀取所有可讀的字節,并將結果放到一個新的ByteBuf中,

public class SquareDecoder extends ByteToMessageDecoder {@Overridepublic void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)throws Exception {out.add(in.readBytes(in.readableBytes()));}}

MessageToMessageDecoder是消息和消息之間的轉換,同樣的只需要實現decode方法即可,如下從String轉換到Integer:

public class StringToIntegerDecoder extendsMessageToMessageDecoder<String> {@Overridepublic void decode(ChannelHandlerContext ctx, String message,List<Object> out) throws Exception {out.add(message.length());}}

ReplayingDecoder

上面的代碼看起來很簡單,但是在實現的過程中還有一些問題要注意。

對于Decoder來說,我們從ByteBuf中讀取數據,然后進行轉換。但是在讀取的過程中,并不知道ByteBuf中數據的變動情況,有可能在讀取的過程中ByteBuf還沒有準備好,那么就需要在讀取的時候對ByteBuf中可讀字節的大小進行判斷。

比如我們需要解析一個數據結構,這個數據結構的前4個字節是一個int,表示后面byte數組的長度,我們需要先判斷ByteBuf中是否有4個字節,然后讀取這4個字節作為Byte數組的長度,然后再讀取這個長度的Byte數組,最終得到要讀取的結果,如果其中的某一步出現問題,或者說可讀的字節長度不夠,那么就需要直接返回,等待下一次的讀取。如下所示:

public class IntegerHeaderFrameDecoder extends ByteToMessageDecoder {@Overrideprotected void decode(ChannelHandlerContext ctx,ByteBuf buf, List<Object> out) throws Exception {if (buf.readableBytes() < 4) {return;}buf.markReaderIndex();int length = buf.readInt();if (buf.readableBytes() < length) {buf.resetReaderIndex();return;}out.add(buf.readBytes(length));}}

這種判斷是比較復雜同時也是可能出錯的,為了解決這個問題,netty提供了 ReplayingDecoder用來簡化上面的操作,在ReplayingDecoder中,假設所有的ByteBuf已經處于準備好的狀態,直接從中間讀取即可。

上面的例子用ReplayingDecoder重寫如下:

public class IntegerHeaderFrameDecoderextends ReplayingDecoder<Void> {protected void decode(ChannelHandlerContext ctx,ByteBuf buf, List<Object> out) throws Exception {out.add(buf.readBytes(buf.readInt()));}}

它的實現原理是去嘗試讀取對應的字節信息,如果沒有讀到,則拋出異常,ReplayingDecoder接收到異常之后,會重新調用decode方法。

雖然ReplayingDecoder使用起來非常簡單,但是它有兩個問題。

第一個問題是性能問題,因為會去重復調用decode方法,如果ByteBuf本身并沒有變化,就會導致重復decode同一個ByteBuf,照成性能的浪費。解決這個問題就是在在decode的過程中分階段進行,比如上面的例子中,我們需要先讀取Byte數組的長度,然后再讀取真正的byte數組。所以在讀完byte數組長度之和,可以調用checkpoint()方法做一個保存點,下次再執行decode方法的時候就可以跳過這個保存點,繼續后續的執行過程,如下所示:

public enum MyDecoderState {READ_LENGTH,READ_CONTENT;}public class IntegerHeaderFrameDecoderextends ReplayingDecoder<MyDecoderState> {private int length;public IntegerHeaderFrameDecoder() {// Set the initial state.super(MyDecoderState.READ_LENGTH);}@Overrideprotected void decode(ChannelHandlerContext ctx,ByteBuf buf, List<Object> out) throws Exception {switch (state()) {case READ_LENGTH:length = buf.readInt();checkpoint(MyDecoderState.READ_CONTENT);case READ_CONTENT:ByteBuf frame = buf.readBytes(length);checkpoint(MyDecoderState.READ_LENGTH);out.add(frame);break;default:throw new Error("Shouldn't reach here.");}}}

第二個問題是同一個實例的decode方法可能會被調用多次,如果我們在ReplayingDecoder中有私有變量的話,則需要考慮對這個私有變量的清洗工作,避免多次調用造成的數據污染。

總結

通過繼承上面的幾個類,我們就可以自己實現編碼和解碼的邏輯了。但是好像還有點問題,自定義編碼和解碼器是不是太復雜了?還需要判斷要讀取的byte數組的大小。有沒有更加簡單的方法呢?

有的,敬請期待netty系列的下一篇文章:netty自帶的編碼器和解碼器.

本文的例子可以參考:learn-netty4

本文已收錄于 http://www.flydean.com/14-netty-cust-codec/

最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!

總結

以上是生活随笔為你收集整理的netty系列之:自定义编码和解码器要注意的问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。