《Netty深入剖析》之七:Netty解码

7 Netty解码

将二进制数据流解析成自定义协议数据包ByteBuf

两个部分:

1.解码器基类

2.常见的解码器分析

7.1 解码器基类ByteToMessageDecoder解码步骤

  • 流程

1.使用一个累加器cumulation(类型为ByteBuf)来累加字节流

2.调用子类的decode方法进行解析(callDecode(),去累加器读取数据)

3.如果解析到了ByteBuf,则将解析到的ByteBuf向下传播(fireChannelRead())

  • 解析

ByteToMessageDecoder#channelRead方法里,如果msg不是ByteBuf,则向下传播,说明解码是基于ByteBuf的

如果累加器为null,则直接将msg赋值给累加器,进行第二步;如果不为null,则追加,可能需要扩容

子类将解析到的ByteBuf对象放到CodecOutputList这个ArrayList里面,然后向下传播给后面的handler,这样就有可能传播到业务的解码器里面进行业务的解码处理

子类decode方法框架:

@Override
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
   
    Object decoded = decode(ctx, in);//decode由子类自己实现
    if (decoded != null) {
   
        out.add(decoded);
    }
}

注意:自定义解码器如果没有读取到数据,则要返回null,ByteToMessageDecoder才会break结束循环,等待下一次数据的到来

7.2 基于固定长度解码器FixedLengthFrameDecoder的分析

以固定长度frameLength 来划分数据包

7.3 基于行解码器LineBasedFrameDecoder的分析

以换行符来划分数据包(字节流以\r\n,\n结尾时可以使用)

使用findEndOfLine来找到eol(endOfLine)

非丢弃模式处理:支持\n和\r\n两种分隔符(具体算法:找到\n,然后加个if判断前一个字符是否为\r,是的话i–,就得到\r的位置了)

1.正常情况下:readIndex到第一个eol的数据封装成数据包后,将readIndex指针移到第一个eol下一个位置,重复此过程直到结束即可
在这里插入图片描述
当然,如果readIndex到第一个eol的数据的长度大于max,则丢弃,不会封装成数据包
2.而如果readIndex到writeIndex中间没有eol,则会判断其长度是否大于maxLength:
如果小于则直接返回null,代表什么都没有解析到;
如果大于,则需要丢弃(即将readIndex移到writeIndex的位置上),然后进入丢弃模式(discarding=true)
在这里插入图片描述
丢弃模式:
3.正常情况下,将readIndex移到第一个eol的下一个位置,即丢弃第一个eol前的数据,然后进入非丢弃模式;
在这里插入图片描述
4.而如果readIndex到writeIndex中间没有eol,则将writeIndex-readIndex这段丢弃(将readIndex移到writeIndex的位置上)
在这里插入图片描述

7.4 基于分隔符解码器DelimiterBasedFrameDecoder的分析

可以传进去多个分隔符来划分数据包;还要传一个maxFrameLength

  • 流程

1.如果分隔符是基于行的分隔符,则使用行解码器LineBasedFrameDecoder来处理

2.找到最小分隔符,首先以最小分隔符来拆分数据包

3.解码

  • 解析

1 LineBasedFrameDecoder的初始化:构造DelimiterBasedFrameDecoder的过程中,发现使用“\ n”和“\r\n”作为分隔符进行解码时进行

2 最小分隔符:不是指分隔符本身大小,而是指分隔出来的数据包大小;
每个分隔符分隔出来的数据包中,取最小的数据,这个分隔符才是最小分隔符

3

找到最小分隔符:

  • 非丢弃模式:

如果发现分隔出来的数据包大小大于maxFrameLength,则将这段数据包+分隔符全部丢弃,然后fail方法抛出异常,返回null;
如果小于,即正常情况,封装成ByteBuf(同样有“是否包含分隔符一起封装”的逻辑),返回此ByteBuf

  • 丢弃模式:

丢弃找到的这段数据包+分隔符,然后进入非丢弃模式,返回null

没有找到最小分隔符:

  • 丢弃模式:

丢弃可读字节,返回null

  • 非丢弃模式:

如果可读字节大于max,则丢弃这段可读字节,进入丢弃模式,返回null;
如果可读字节小于max,则什么都不做,留着这段字节留待下次使用,返回null

行分隔解码器就是按照这个逻辑编写的,代码跟DelimiterBasedFrameDecoder不一样而已:DelimiterBasedFrameDecoder先判断分隔符,而行分隔解码器则知道了分隔符,所以直接按照丢弃非丢弃来编写

7.5 基于长度域解码器LengthFieldBasedFrameDecoder的分析

7.5.1 重要参数分析

https://blog.csdn.net/fst438060684/article/details/82912122

lengthFieldOffset:长度域的偏移量,也就是长度域要从什么地方开始,跳过lengthFieldOffset,就开始Length的运算了

lengthFieldLength:长度域的长度,也就是长度域(Length域)占多少个字节

lengthAdjustment:长度域的值的调整,也就是长度域里面的还要做多少调整才是符合要求的,这个值可以是正值,可以是负值,正值表示还要加多少,负值表示还要减去多少,lengthFieldLength里的值和lengthAdjustment运算过后得到的值就是真正的内容

initialBytesToStrip:原始需要跳过多少才返回给用户

源码例子1:没有偏移,长度域的长度为2字节,也就是为0x000C(12)

 * <b>lengthFieldOffset</b>   = <b>0</b>
 * <b>lengthFieldLength</b>   = <b>2</b>
 * lengthAdjustment    = 0
 * initialBytesToStrip = 0 (= do not strip header)
 *
 * BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
 * +--------+----------------+      +--------+----------------+
 * | Length | Actual Content |
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值