开发者

Netty解码器LengthFieldBasedFrameDecoder详解

目录
  • Netty解码器LengthFieldBasedFrameDecoder
    • 接下来看它是如何根据规则解码的
    • 就要说下这个规则是什么了
    • 源码解读如下,下面可以不看
  • 总结

    Netty解码器LengthFieldBasedFrameDecoder

    解码器LengthFieldBasedFrameDecoder,从名字上可以猜测出来,它是基于长度的解码器.

    Netty从TCP缓冲区中读取字节,把这些字节交给LengthFieldBasedFrameDecoder进行解码,解码的操作是根据设定的规则,根据规则,从字节中解码出来有意义的数据,然后把数据再交给后续的Handler处理.

    接下来看它是如何根据规则解码的

    Netty解码器LengthFieldBasedFrameDecoder详解

    如上图,从网络中读取到的数据是基于流的,而且是有方向的.

    然而数据是没有边界的,不知道从哪儿到哪儿是一个完整的数据,下一个数据又是从哪个到哪个.

    因此应用层需要设定规则,根据规则就可以知道数据的边界在哪儿.

    Netty解码器LengthFieldBasedFrameDecoder详解

    如上图,便是根据设定的规则,就可以’筛选’出来真正有意义的数据(data)在哪个. 而且允许每个data的长度是不一样大小.

    就要说下这个规则是什么了

    规则是由4个主要的属性构成,lengthFieldOffset,lengthFieldLength,lengthAdjustment,initialBytesToStrip.

    通过一个数据块为例介绍这4个属性.

    Netty解码器LengthFieldBasedFrameDecoder详解

    python上图,从红色箭头指向的位置开始读取数据.

    lengthFieldOffset表示长度字段的偏移量,经过lengthFieldOffset之后,箭头指向了下一个位置.

    如果lengthFieldOffset=3,那么箭头需要向右边走3个字节.

    Netty解码器LengthFieldBasedFrameDecoder详解

    接下来,lengthFieldLength表示长度字段的长度(好绕口).

    如果lengthFieldLength=4,那么就会从上图红色位置向后读取4个字节,把4个字节里面的内容作为真正data的长度.

    而且lengthFieldLength的取值不是任意的,它只能取值1,2,3,4,8. 具体原因后面的源码会说明.

    Netty解码器LengthFieldBasedFrameDecoder详解

    如上图,假如lengthFieldLength=4,读取4个字节的内容是0x00000010(十六进制表示),十进制就是16,也就是说,数据data的长度是16个字节. 但是这里稍等下,需要介绍下一个关键属性.

    lengthAdjustment表示长度调整. 调整什么呢? 还是要说下lengthFieldLength. lengthFieldLength里面的内容是16,虽然这个16表示长度,但是它是表示真正数据data的长度,还是表示整个的长度呢,或者其他呢. 因此要想真正表示真正数据data的长度,必须用lengthFieldLength的内容值+lengthAdjustment的值.

    如果lengthAwww.devze.comdjustment=-5,也就是用16+(-5)=11,即从上图红色位置继续向后读取11个字节才能真正的把数据读取完整,读取少了或多了都不行.

    到这里,已经把一个完整的数据块读取完成了. 但是呢,真正表示业务数据的内容是data部分.我们不想要前面的lengthFieldOffset和lengthFieldLength部分,这里就需要使用initialBytesToStrip. 它表示跳过多少字节.

    如果initialBytesToStrip=7,那么就是说要跳过7个字节,把剩余部分传给下游的Handler继续处理.

    Netty解码器LengthFieldBasedFrameDecoder详解

    以上就是4个主要属性的解释,从源码中拿一个具体的’案例’再温习下.

    Netty解码器LengthFieldBasedFrameDecoder详解

    从最左边开始读取数据,lengthFieldOffset=1,那么向后读取1个字节,lengthFieldLength=2,向后读取2个字节,读取到的内容是0x0010(十六进制),十进制就是16,由于lengthAdjustment=-3,因此16+(-3)=13,于是继续向后读取13个字节.

    就会把0xFE和"HELLO,WORLD"这13个字节读取到. 到目前为止,读取到的内容是0xCA0010FE和"HELLO,WORLD"共16个字节. 又initialBytesToStrip=3,因此从16个字节的开头跳过3个字节,跳过了0xCA0010这3个字节,最后剩下0xFE和"HELLO,WORLD"传给了下游的Handler.

    源码解读如下,下面可以不看

    Netty的源码位置 io.netty.handler.codec.LengthFieldBasedFrameDecoder

    Netty解码器LengthFieldBasedFrameDecoder详解

    LengthFieldBasedFrameDecoder继承了ChannelInboundHandlerAdapter,因此当Netty读取到网络数据之后,再向下传播数据的过程中,会调用到ByteToMessageDecoder的channelRead方法,channelRead方法内部会调用decode方法.

    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
     
            // 由于整个帧的长度 frameLength 大于 设定的maxFrameLength, 是需要跳过这个无效帧的.
     
            // 之前已经跳过了一部分数据, 由于之前不够跳过, 现在又读取到了数据, 那么需要继续跳过剩下'欠'的数据
     
            if (discardingTooLongFrame) {
     
                discardingTooLongFrame(in);
     
            }
     
     
     
            // 在构造函数中定义了  lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength
     
            //http://www.devze.com 它的意思是目前可以从网络中读取的实际字节数(in.readableBytes()) 小于 lengthFieldEndOffset , 无法处理 直接返回, 需要等待更多的数据.
     
            if (in.readableBytes() < lengthFieldEndOffset) {
     
                return null;
     
            }
     
     
     
            // in.readerIndex() 表http://www.devze.com示读取上一个完整数据的最后下标
     
            int actualLengtwww.devze.comhFieldOffset = in.readerIndex() + lengthFieldOffset;
     
            // frameLength 表示获取没有调整前的数据帧长度 .  后面的逻辑要使用lengthAdjustment属性调整成真实的数据帧长度
     
            long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);
     
     
     
     
     
            // 如果读取到的frameLength 小于 0, 说明此数据有问题, 抛出异常
     
            if (frameLength < 0) {
     
                failOnNegativeLengthField(in, frameLength, lengthFieldEndOffset);
     
            }
     
     
     
            // 调整frameLength长度  frameLength = frameLength + lengthAdjustment + lengthFieldEndOffset
     
            // frameLength + lengthAdjustment 表示真实数据的长度
     
            // 即这里的frameLength 就是整个数据的长度(包括真实数据).
     
            frameLength += lengthAdjustment + lengthFieldEndOffset;
     
     
     
            // 如果frameLength < lengthFieldEndOffset  那只能说明在上面的计算过程中, frameLength + lengthAdjustment < 0 了.
     
            // frameLength + lengthAdjustment 表示真实数据的长度, 数据的长度怎么会小于0呢   因此抛异常.
     
            if (frameLength < lengthFieldEndOffset) {
     
                failOnFrameLengthLessThanLengthFieldEndOffset(in, frameLength, lengthFieldEndOffset);
     
            }
     
     
     
            // 如果整个数据的长度大于设定的最大值. 那么认为这是无效数据, 需要跳过这个无效数据
     
            if (frameLength > maxFrameLength) {
     
                // 跳过一个frameLength长度的数据
     
                exceededFrameLength(in, frameLength);
     
                return null;
     
            }
     
     
     
            // never overflows because it's less than maxFrameLength
     
            int frameLengthInt = (int) frameLength;
     
            // 表示目前可读的数据还不够一个帧, 那么直接返回
     
            if (in.readableBytes() < frameLengthInt) {
     
                return null;
     
            }
     
     
     
            // 比如一个即将要读取的帧长度=10,  可是initialBytesToStrip = 12, 跳过的字节比要读取的字节还大,  读取的字节还不够跳过的, 有问题   直接抛异常
     
            if (initialBytesToStrip > frameLengthInt) {
     
                failOnFrameLengthLessThanInitialBytesToStrip(in, frameLength, initialBytesToStrip);
     
            }
     
            in.skipBytes(initialBytesToStrip);
     
     
     
            // extract frame
     
            // 读取实际有意义的业务数据
     
            int readerIndex = in.readerIndex();
     
            int actualFrameLength = frameLengthInt - initialBytesToStrip;
     
            ByteBuf frame = extractFrame(ctx, in, readerIndex, actualFrameLength);
     
            in.readerIndex(readerIndex + actualFrameLength);
     
            return frame;
     
        }

    Netty解码器LengthFieldBasedFrameDecoder详解

    Netty解码器LengthFieldBasedFrameDecoder详解

    Netty解码器LengthFieldBasedFrameDecoder详解

    总结

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜