使用netty的ReplayingDecoder解決長度變化的複雜資料格式的拆包粘包
阿新 • • 發佈:2018-12-13
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不應該到達的位置"); } }