解码方式一:
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");
}
}
}