Netty源码分析-解码器LengthFieldBasedFrameDecoder

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nimasike/article/details/87809345

解码方式一:

lengthFieldOffset   = 0
lengthFieldLength   = 2
lengthAdjustment    = 0
initialBytesToStrip = 0 (= do not strip header)
 解码前 (14 bytes)                 解码后 (14 bytes)
 +--------+----------------+      +--------+----------------+
 | Length | Actual Content |----->| Length | Actual Content |
 | 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |
 +--------+----------------+      +--------+----------------+

lengthFieldLength=2,用2个字节表示消息长度。HELLO, WORLD长度为12个字节,那么(Length=0x000c)十进制为12。只表示消息(HELLO, WORLD)的长度,不包括长度本身的2个字节。

看一个简单的例子:

假设消息为: 【04abcd06abcdef】 ,那么解析过程如下:

1、因为lengthFieldLength=2,所以首先读取2个字节(04),代表消息长度为4。

2、在读取4个字节就是实际的消息,即abcd。

3、把04abcd发送下一个处理器。

4、以此类推.....在解析出 06abcdef 交给下一个处理器。

解码方式二:

lengthFieldOffset   = 0
lengthFieldLength   = 2
lengthAdjustment    = 0
initialBytesToStrip = 2
 解码前 (14 bytes)                 解码后 (12 bytes)
 +--------+----------------+      +----------------+
 | Length | Actual Content |----->| Actual Content |
 | 0x000C | "HELLO, WORLD" |      | "HELLO, WORLD" |
 +--------+----------------+      +----------------+

lengthFieldLength=2,用2个字节表示消息长度,解析过程与(解码方式一)一致,只是在传递给下一个解码器时会丢弃表示长度的2个字节,将(HELLO, WORLD)进行传递。

解码方式三:

lengthFieldOffset   =  0
lengthFieldLength   =  2
lengthAdjustment    = -2 (= the length of the Length field)
initialBytesToStrip =  0
 解码前 (14 bytes)                 解码后 (14 bytes)
 +--------+----------------+      +--------+----------------+
 | Length | Actual Content |----->| Length | Actual Content |
 | 0x000E | "HELLO, WORLD" |      | 0x000E | "HELLO, WORLD" |
 +--------+----------------+      +--------+----------------+

可以看到(Length 0x000E)=14,与解码方式一,二不同的是,它包含了头部长度本身的2个字节。(2+12=14),所以在解码时需要设置 lengthAdjustment    = -2, 在读取(14-2=12)个字节正好是一个完整消息。

解码方式四:

lengthFieldOffset   = 2 (= the length of Header 1)
lengthFieldLength   = 3
lengthAdjustment    = 0
initialBytesToStrip = 0
 解码前 (17 bytes)                              解码后 (17 bytes)
 +----------+----------+----------------+      +----------+----------+----------------+
 | Header 1 |  Length  | Actual Content |----->| Header 1 |  Length  | Actual Content |
 |  0xCAFE  | 0x00000C | "HELLO, WORLD" |      |  0xCAFE  | 0x00000C | "HELLO, WORLD" |
 +----------+----------+----------------+      +----------+----------+----------------+

Header占2个字节,Length占3个字节,Actual Content占12个字节

lengthFieldOffset=2,Length长度位置的偏移量,Header长度为2,需要读完2个字节才是Length的起始位置。

lengthFieldLength=3,Length长度占用3个字节,解码器读取3个字节拿到(0x00000C=12),那么在读取12个字节就是一个完整的消息,然后将 (Header+Length+Actual Content)整体交给下一个处理器。

解码方式五:

lengthFieldOffset   = 0
lengthFieldLength   = 3
lengthAdjustment    = 2 (= the length of Header 1)
initialBytesToStrip = 0
 解码前 (17 bytes)                              解码后 (17 bytes)
 +----------+----------+----------------+      +----------+----------+----------------+
 |  Length  | Header 1 | Actual Content |----->|  Length  | Header 1 | Actual Content |
 | 0x00000C |  0xCAFE  | "HELLO, WORLD" |      | 0x00000C |  0xCAFE  | "HELLO, WORLD" |
 +----------+----------+----------------+      +----------+----------+----------------+

Length占3个字节,Header占2个字节,Actual Content占12个字节

lengthFieldLength=3,长度占3个字节, 所以先读取3个字节(0x00000C)=12, lengthAdjustment = 2,那么在读取(12+2=14)14个字节就是一个完整的消息。

解码方式六:

 lengthFieldOffset   = 1 (= the length of HDR1)
 lengthFieldLength   = 2
 lengthAdjustment    = 1 (= the length of HDR2)
 initialBytesToStrip = 3 (= the length of HDR1 + LEN)
 解码前 (16 bytes)                               解码后 (13 bytes)
 +------+--------+------+----------------+      +------+----------------+
 | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
 | 0xCA | 0x000C | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
 +------+--------+------+----------------+      +------+----------------+

 HDR1占用1个字节,所以lengthFieldOffset=1,读过1个字节才是Length的起始位置。

 lengthFieldLength=2,Length占用2个字节,读取2个字节拿到0x000C=12,消息长度为12。

 lengthAdjustment=1,HDR2占一个字节,所以在读取(12+1=13)13个字节为一个完整的消息。

 initialBytesToStrip=3,全部读完后丢弃3个字节(HDR1和Length),把剩下的(HDR2和Actual Content)交给下一个处理器。

解码方式七:

lengthFieldOffset   =  1
lengthFieldLength   =  2
lengthAdjustment    = -3 (= the length of HDR1 + LEN, negative)
initialBytesToStrip =  3
  解码前 (16 bytes)                              解码后 (13 bytes)
 +------+--------+------+----------------+      +------+----------------+
 | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
 | 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
 +------+--------+------+----------------+      +------+----------------+

 HDR1占用1个字节,所以lengthFieldOffset=1,读过1个字节才是Length的起始位置。

lengthFieldLength=2,Length占用2个字节,读取2个字节拿到0x0010=16。16代表了整体长度=》 (HDR1*1) +  (Length*2)  + (HDR2*1)  +  (Actual Content*12) = 16。  需要设置 lengthAdjustment    = -3, 即在读取(16-3=13)13个字节 就是一个完整的消息。initialBytesToStrip =  3,读取完毕后丢弃3个字节,把剩下的(HDR2+Actual Content)交个下一个处理器。

源码分析:

package io.netty.handler.codec;

import java.nio.ByteOrder;
import java.util.List;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.serialization.ObjectDecoder;

public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {

    private final ByteOrder byteOrder;
    private final int maxFrameLength;
    private final int lengthFieldOffset;
    private final int lengthFieldLength;
    private final int lengthFieldEndOffset;
    private final int lengthAdjustment;
    private final int initialBytesToStrip;
    private final boolean failFast;
    private boolean discardingTooLongFrame;
    private long tooLongFrameLength;
    private long bytesToDiscard;

    
    public LengthFieldBasedFrameDecoder(
            ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,
            int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
        if (byteOrder == null) {
            throw new NullPointerException("byteOrder");
        }

        if (maxFrameLength <= 0) {
            throw new IllegalArgumentException(
                    "maxFrameLength must be a positive integer: " +
                    maxFrameLength);
        }

        if (lengthFieldOffset < 0) {
            throw new IllegalArgumentException(
                    "lengthFieldOffset must be a non-negative integer: " +
                    lengthFieldOffset);
        }

        if (initialBytesToStrip < 0) {
            throw new IllegalArgumentException(
                    "initialBytesToStrip must be a non-negative integer: " +
                    initialBytesToStrip);
        }

        if (lengthFieldOffset > maxFrameLength - lengthFieldLength) {
            throw new IllegalArgumentException(
                    "maxFrameLength (" + maxFrameLength + ") " +
                    "must be equal to or greater than " +
                    "lengthFieldOffset (" + lengthFieldOffset + ") + " +
                    "lengthFieldLength (" + lengthFieldLength + ").");
        }

        this.byteOrder = byteOrder;
        this.maxFrameLength = maxFrameLength;
        this.lengthFieldOffset = lengthFieldOffset;
        this.lengthFieldLength = lengthFieldLength;
        this.lengthAdjustment = lengthAdjustment;
        lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
        this.initialBytesToStrip = initialBytesToStrip;
        this.failFast = failFast;
    }

    @Override
    protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        Object decoded = decode(ctx, in);
        if (decoded != null) {
            out.add(decoded);
        }
    }

    private void discardingTooLongFrame(ByteBuf in) {
        long bytesToDiscard = this.bytesToDiscard;
        //取最小值
        int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes());
        //调过需要丢弃的字节
        in.skipBytes(localBytesToDiscard);
        //减掉丢弃的字节
        bytesToDiscard -= localBytesToDiscard;
        //重新复制
        this.bytesToDiscard = bytesToDiscard;

        failIfNecessary(false);
    }

    //跳过frameLength个字节,抛出异常
    private static void failOnNegativeLengthField(ByteBuf in, long frameLength, int lengthFieldEndOffset) {
        in.skipBytes(lengthFieldEndOffset);
        throw new CorruptedFrameException(
           "negative pre-adjustment length field: " + frameLength);
    }

    //跳过frameLength个字节,抛出异常
    private static void failOnFrameLengthLessThanLengthFieldEndOffset(ByteBuf in,
                                                                      long frameLength,
                                                                      int lengthFieldEndOffset) {
        in.skipBytes(lengthFieldEndOffset);
        throw new CorruptedFrameException(
           "Adjusted frame length (" + frameLength + ") is less " +
              "than lengthFieldEndOffset: " + lengthFieldEndOffset);
    }

    private void exceededFrameLength(ByteBuf in, long frameLength) {
        long discard = frameLength - in.readableBytes();
        tooLongFrameLength = frameLength;

        if (discard < 0) {
            // buffer contains more bytes then the frameLength so we can discard all now
            in.skipBytes((int) frameLength);
        } else {
            // Enter the discard mode and discard everything received so far.
            discardingTooLongFrame = true;
            bytesToDiscard = discard;
            in.skipBytes(in.readableBytes());
        }
        failIfNecessary(true);
    }

    //跳过frameLength个字节,抛出异常
    private static void failOnFrameLengthLessThanInitialBytesToStrip(ByteBuf in,long frameLength,int initialBytesToStrip) {
        in.skipBytes((int) frameLength);
        throw new CorruptedFrameException(
           "Adjusted frame length (" + frameLength + ") is less " +
              "than initialBytesToStrip: " + initialBytesToStrip);
    }

    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        if (discardingTooLongFrame) {
            discardingTooLongFrame(in);
        }

        //如果可读字节小于长度字段的标识位,则返回空
        //也就是可读字节还没有包含长度字段
        if (in.readableBytes() < lengthFieldEndOffset) {
            return null;
        }

        //长度字段的位置
        int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;
        //获取长度值
        long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);

        //长度值为负数-抛出异常
        if (frameLength < 0) {
            failOnNegativeLengthField(in, frameLength, lengthFieldEndOffset);
        }

        //假设:
        //HDR1 | Length | HDR2 | Actual Content 
        //0xCA | 0x000C | 0xFE | "HELLO, WORLD"
        //lengthFieldOffset   = 1 (= the length of HDR1)
        //lengthFieldLength   = 2
        //lengthAdjustment    = 1 (= the length of HDR2)
        //initialBytesToStrip = 3 (= the length of HDR1 + LEN)
        //那么:frameLength=16,也就是一个完成消息的长度
        //lengthFieldEndOffset=3   ,  lengthAdjustment = 1    , frameLength=  12

        //这里计算一个完成消息的长度
        frameLength += lengthAdjustment + lengthFieldEndOffset;

        //如果消息的长度 小于  (lengthFieldOffset+lengthFieldLength)则抛出异常
        if (frameLength < lengthFieldEndOffset) {
            failOnFrameLengthLessThanLengthFieldEndOffset(in, frameLength, lengthFieldEndOffset);
        }

        //如果消息长度 超过定义的阈值
        if (frameLength > maxFrameLength) {
            exceededFrameLength(in, frameLength);
            return null;
        }

        // never overflows because it's less than maxFrameLength
        //直接类型转换-不会溢出,因为小于int类型的maxFrameLength
        int frameLengthInt = (int) frameLength;

        //如果缓冲区可读字节 小于 一个消息长度则返回null,等待缓冲区继续接受TCP字节
        if (in.readableBytes() < frameLengthInt) {
            return null;
        }

        //如果丢弃字节大于 消息长度 则错误处理
        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;
    }

    //获取长度信息
    //offset=长度标识位的起始位置
    //length=长度标识位占用字节
    protected long getUnadjustedFrameLength(ByteBuf buf, int offset, int length, ByteOrder order) {
        buf = buf.order(order);
        long frameLength;
        //根据offset起始位置,读取Length长度的字节,转换为无符号十进制
        switch (length) {
        case 1:
            //读1个字节
            frameLength = buf.getUnsignedByte(offset);
            break;
        case 2:
            //读2个字节
            frameLength = buf.getUnsignedShort(offset);
            break;
        case 3:
            //读3个字节
            frameLength = buf.getUnsignedMedium(offset);
            break;
        case 4:
            //读4个字节
            frameLength = buf.getUnsignedInt(offset);
            break;
        case 8:
            //读8个字节
            frameLength = buf.getLong(offset);
            break;
        default:
            throw new DecoderException(
                    "unsupported lengthFieldLength: " + lengthFieldLength + " (expected: 1, 2, 3, 4, or 8)");
        }
        return frameLength;
    }

    private void failIfNecessary(boolean firstDetectionOfTooLongFrame) {
        if (bytesToDiscard == 0) {
            // Reset to the initial state and tell the handlers that
            // the frame was too large.
            long tooLongFrameLength = this.tooLongFrameLength;
            this.tooLongFrameLength = 0;
            discardingTooLongFrame = false;
            if (!failFast || firstDetectionOfTooLongFrame) {
                fail(tooLongFrameLength);
            }
        } else {
            // Keep discarding and notify handlers if necessary.
            if (failFast && firstDetectionOfTooLongFrame) {
                fail(tooLongFrameLength);
            }
        }
    }

    
    //返回子缓冲区,包含一个完整消息,并增加引用计数
    protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) {
        return buffer.retainedSlice(index, length);
    }

    private void fail(long frameLength) {
        if (frameLength > 0) {
            throw new TooLongFrameException(
                            "Adjusted frame length exceeds " + maxFrameLength +
                            ": " + frameLength + " - discarded");
        } else {
            throw new TooLongFrameException(
                            "Adjusted frame length exceeds " + maxFrameLength +
                            " - discarding");
        }
    }
}

猜你喜欢

转载自blog.csdn.net/nimasike/article/details/87809345