剖析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实际数据如下:
看上面代码中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个字节数据给抛弃了
处理思路图:
针对文档给出的例子的测试:
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;
}
/*
* lengthFieldOffset = 1 (= the length of Header 1)
* lengthFieldLength = 4
* 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" |
* +----------+----------+----------------+ +----------+----------+----------------+
*
*/
@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
* lengthAdjustment = 1 (= 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" |
* +----------+----------+----------------+ +----------+----------+----------------+
*
*/
@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
* lengthAdjustment = 1 (= the length of HDR2)
* initialBytesToStrip = 5 (= 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" |
* +------+--------+------+----------------+ +------+----------------+
*
*
*/
@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());
}
}
}