剖析LengthFieldBasedFrameDecoder

剖析LengthFieldBasedFrameDecoder

下面这个测试用例是Netty自带的:

@Test
    public void testDiscardTooLongFrame1() {
        ByteBuf buf = Unpooled.buffer();
        buf.writeInt(32);
        for (int i = 0; i < 32; i++) {
            buf.writeByte(i);
        }
        buf.writeInt(1);
 1--->  buf.writeByte('a');
        EmbeddedChannel channel = new EmbeddedChannel(new LengthFieldBasedFrameDecoder(16, 0, 4));
        try {
            channel.writeInbound(buf);
            Assert.fail();
        } catch (TooLongFrameException e) {
            // expected
        }
        Assert.assertTrue(channel.finish());

        ByteBuf b = channel.readInbound();
        Assert.assertEquals(5, b.readableBytes());
        Assert.assertEquals(1, b.readInt());
        Assert.assertEquals('a', b.readByte());
        b.release();

        Assert.assertNull(channel.readInbound());
        channel.finish();
    }

执行到代码1时,ByteBuf实际数据如下:

图1.png

看上面代码中LengthFieldBasedFrameDecoder参数设定:

maxFrameLength = 16

lengthFieldOffset = 0

lengthFieldLength = 4

查看源码:

    public LengthFieldBasedFrameDecoder(
            int maxFrameLength,
            int lengthFieldOffset, int lengthFieldLength) {
        this(maxFrameLength, lengthFieldOffset, lengthFieldLength, 0, 0);
    }

当只设定这三个参数值时,后面的lengthAdjustment以及initialBytesToStrip默认设置为0

接下进行重要的解码环节,对应LengthFieldBasedFrameDecoder中decode函数

    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);
        }

        frameLength += lengthAdjustment + lengthFieldEndOffset;

        if (frameLength < lengthFieldEndOffset) {
            failOnFrameLengthLessThanLengthFieldEndOffset(in, frameLength, lengthFieldEndOffset);
        }

        if (frameLength > maxFrameLength) {
            exceededFrameLength(in, frameLength);
            System.out.println("frameLength > maxFrameLength");
            return null;
        }

        // never overflows because it's less than maxFrameLength
        int frameLengthInt = (int) frameLength;
        if (in.readableBytes() < frameLengthInt) {
            System.out.println("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;
    }

分析:

     if (in.readableBytes() < lengthFieldEndOffset) {
            return null;
        }

lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
也就是lengthFieldEndOffset = 0+4=4

而in.readableBytes对应到测试用例,为41

所以不满足条件

接着:

        int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;

actualLengthFieldOffset = 0 + 0 = 0

接着:

        long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);

查看下getUnadjustedFrameLength函数:


    protected long getUnadjustedFrameLength(ByteBuf buf, int offset, int length, ByteOrder order) {
        buf = buf.order(order);
        long frameLength;
        switch (length) {
        case 1:
            frameLength = buf.getUnsignedByte(offset);
            break;
        case 2:
            frameLength = buf.getUnsignedShort(offset);
            break;
        case 3:
            frameLength = buf.getUnsignedMedium(offset);
            break;
        case 4:
            frameLength = buf.getUnsignedInt(offset);
            break;
        case 8:
            frameLength = buf.getLong(offset);
            break;
        default:
            throw new DecoderException(
                    "unsupported lengthFieldLength: " + lengthFieldLength + " (expected: 1, 2, 3, 4, or 8)");
        }
        return frameLength;
    }

可以发现:

  • lengthFieldLength的选取只能是1,2,3,4,8

测试用例中lengthFieldLength=4,对应执行语句:

case 4:
            frameLength = buf.getUnsignedInt(offset);

而offset=0,所以:

frameLength = buf.getUnsignedInt(0);

frameLength=32

接下来:

        frameLength += lengthAdjustment + lengthFieldEndOffset;
  • lengthAdjustment = 0

  • lengthFieldEndOffset = 4

所以此时frameLength=36

接下来:

     if (frameLength > maxFrameLength) {
            exceededFrameLength(in, frameLength);
            System.out.println("frameLength > maxFrameLength");
            return null;
        }

此时frameLength=36>maxFrameLength=16,进入条件块:

查看exceededFrameLength函数:

 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);
    }


  • long discard = 36-41 = -5

  • tooLongFrameLength = 36

  • 因为discard<0,所以执行in.skipBytes((int) frameLength);

对应到测试例子中就是把ByteBuf的前36个字节数据给抛弃了

处理思路图:

图2.png

针对文档给出的例子的测试:

public class Msg {

    private byte header;

    private int length;

    private String content;

    private byte hdr1;

    private byte hdr2;

    public byte getHdr1() {
        return hdr1;
    }

    public void setHdr1(byte hdr1) {
        this.hdr1 = hdr1;
    }

    public byte getHdr2() {
        return hdr2;
    }

    public void setHdr2(byte hdr2) {
        this.hdr2 = hdr2;
    }

    public byte getHeader() {
        return header;
    }

    public void setHeader(byte header) {
        this.header = header;
    }

    public int getLength() {
        return this.length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "Msg{" +
                "header=" + header +
                ", length=" + length +
                ", content='" + content + '\'' +
                '}';
    }
}

public class T1 {

    private Msg get() {

        Msg msg = new Msg();
        String s = "hello world";
        msg.setHeader((byte) 0x12);
        msg.setContent(s);
        msg.setLength(s.getBytes().length);
        return msg;
    }

    private Msg get1() {

        Msg msg = new Msg();
        String s = "hello world";
        msg.setContent(s);
        msg.setLength(s.getBytes().length);
        msg.setHdr1((byte) 0xCA);
        msg.setHdr2((byte) 0xFE);
        return msg;
    }


    private ByteBuf getBF() {
        ByteBuf buf = Unpooled.buffer();

        Msg msg = get();

        buf.writeByte(msg.getHeader());

        buf.writeInt(msg.getLength());

        buf.writeBytes(msg.getContent().getBytes());

        EmbeddedChannel channel = new EmbeddedChannel(new LengthFieldBasedFrameDecoder(
                1024,
                1,
                4,
                0,
                0
        ));
        channel.writeInbound(buf);
        ByteBuf b = channel.readInbound();

        return b;

    }

    private ByteBuf getBF1() {
        ByteBuf buf = Unpooled.buffer();

        Msg msg = get();

        buf.writeInt(msg.getLength());

        buf.writeByte(msg.getHeader());

        buf.writeBytes(msg.getContent().getBytes());

        EmbeddedChannel channel = new EmbeddedChannel(new LengthFieldBasedFrameDecoder(
                1024,
                0,
                4,
                1,
                0
        ));
        channel.writeInbound(buf);
        ByteBuf b = channel.readInbound();

        return b;

    }

    private ByteBuf getBF2() {
        ByteBuf buf = Unpooled.buffer();

        Msg msg = get1();

        buf.writeByte(msg.getHdr1());

        buf.writeInt(msg.getLength());

        buf.writeByte(msg.getHdr2());

        buf.writeBytes(msg.getContent().getBytes());

        EmbeddedChannel channel = new EmbeddedChannel(new LengthFieldBasedFrameDecoder(
                1024,
                1,
                4,
                1,
                5
        ));
        channel.writeInbound(buf);
        ByteBuf b = channel.readInbound();

        return b;

    }


    /*
     * <b>lengthFieldOffset</b>   = <b>1</b> (= the length of Header 1)
     * <b>lengthFieldLength</b>   = <b>4</b>
     * lengthAdjustment    = 0
     * initialBytesToStrip = 0
     *
     * BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
     * +----------+----------+----------------+      +----------+----------+----------------+
     * | Header 1 |  Length  | Actual Content |----->| Header 1 |  Length  | Actual Content |
     * |  0xCAFE  | 0x00000C | "HELLO WORLD" |      |  0xCAFE  | 0x00000C | "HELLO WORLD" |
     * +----------+----------+----------------+      +----------+----------+----------------+
     * </pre>

     */
    @Test
    public void test_lengthFieldOffset() {

        ByteBuf buf = getBF();

        assertEquals(16,buf.readableBytes());

        assertEquals(0x12,buf.readByte());

        assertEquals(11,buf.readInt());

        while (buf.isReadable()){
            System.out.print((char)buf.readByte());
        }
    }


    /*
     * lengthFieldOffset   = 0
     * lengthFieldLength   = 4
     * <b>lengthAdjustment</b>    = <b>1</b> (= the length of Header 1)
     * initialBytesToStrip = 0
     *
     * BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
     * +----------+----------+----------------+      +----------+----------+----------------+
     * |  Length  | Header 1 | Actual Content |----->|  Length  | Header 1 | Actual Content |
     * | 0x00000C |  0xCAFE  | "HELLO, WORLD" |      | 0x00000C |  0xCAFE  | "HELLO, WORLD" |
     * +----------+----------+----------------+      +----------+----------+----------------+
     * </pre>
     */
    @Test
    public void test_lengthAdjustment() {


        ByteBuf buf = getBF1();

        assertEquals(16,buf.readableBytes());

        assertEquals(11,buf.readInt());

        assertEquals(0x12,buf.readByte());
        while (buf.isReadable()){
            System.out.print((char)buf.readByte());
        }
    }

    /*
     * lengthFieldOffset   = 1 (= the length of HDR1)
     * lengthFieldLength   = 4
     * <b>lengthAdjustment</b>    = <b>1</b> (= the length of HDR2)
     * <b>initialBytesToStrip</b> = <b>5</b> (= the length of HDR1 + LEN)
     *
     * BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
     * +------+--------+------+----------------+      +------+----------------+
     * | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
     * | 0xCA | 0x000C | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
     * +------+--------+------+----------------+      +------+----------------+
     * </pre>
     *
     */
    @Test
    public void test_4() {
        ByteBuf buf = getBF2();
        assertEquals(12,buf.readableBytes());

        System.out.println(buf.readByte());

        while (buf.isReadable()){
            System.out.print((char)buf.readByte());
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值