1. 程式人生 > >使用netty的ReplayingDecoder解決長度變化的複雜資料格式的拆包粘包

使用netty的ReplayingDecoder解決長度變化的複雜資料格式的拆包粘包

ReplayingDecoder

簡介

ReplayingDecoder用於解決通訊時粘包和拆包的問題以及用於分步解析資料的場景。當然,如果是基於分隔符的協議那麼不需要ReplayingDecoder來解決拆包粘包,頭部資料作為長度的資料格式也不需要ReplayingDecoder,netty提供了其他更方便的工具。但是,遇到長度開始時不確定,是通過接收前數個欄位後,才能算出長度的資料格式。或者遇到把長度放中間的資料格式。就需要用到ReplayingDecoder來解決這個問題。

ReplayingDecoder的特點就是可以儲存當前的讀取狀態,這個狀態是自定義的。

​​​​​​​如何使用

我們需要extends ReplayingDecoder<StateClass>

StateClass是自定義的enum (通常) 代表讀取一條資料的進度


@Override

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

ReplayingDecoder這裡的ByteBuf in的型別是ReplayingDecoderByteBuf

他的不同之處是:當呼叫其read方法時,如果接受的資料長度小於期待read的資料長度,會丟擲一個REPLAY異常

ReplayingDecoder會接收到這個異常後會將ByteBuf的讀指標回退到checkpoint的位置

通過checkpoint(State state)方法可以記錄下當前的狀態以及讀指標的位置

通過state()方法可以取得當前的狀態,以助於跳轉到相應程式碼的位置。

示例

下面是示例,示例中的State代表待讀的內容,也可以自己改為代表已經讀取的內容


       @Override

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

              switch(state()){

              case BEGIN:

                     checkpoint(UTC_TIME);

              case UTC_TIME:

                     byte[] utcByte = new byte[4];

                     in.readBytes(utcByte);

                     curUTC = ByteUtils.getDateByUnixTimeStampByte(utcByte);

                     checkpoint(ID);

              case ID:

                     curId = new byte[12];

                     in.readBytes(curId);

                     checkpoint(FrameParseState.DATA_TYPE);

                     …//省略

              default:

                     throw new UnexpectedException("case default不應該到達的位置");

              }

       }