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不应该到达的位置");
}
}