基于长度域解码器LengthFieldBasedFrameDecoder我们主要分析以下三点:
1、计算需要抽取的数据包的长度
2、跳过字节逻辑处理
3、丢弃模式下的处理
首先源码还是LengthFieldBasedFrameDecoder的decode方法:
/**
* Create a frame out of the {@link ByteBuf} and return it.
*
* @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to
* @param in the {@link ByteBuf} from which to read data
* @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could
* be created.
*/
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
if (discardingTooLongFrame) {
long bytesToDiscard = this.bytesToDiscard;
int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes());
in.skipBytes(localBytesToDiscard);
bytesToDiscard -= localBytesToDiscard;
this.bytesToDiscard = bytesToDiscard;
failIfNecessary(false);
}
if (in.readableBytes() < lengthFieldEndOffset) {
return null;
}
int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;
long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);
if (frameLength < 0) {
in.skipBytes(lengthFieldEndOffset);
throw new CorruptedFrameException(
"negative pre-adjustment length field: " + frameLength);
}
frameLength += lengthAdjustment + lengthFieldEndOffset;
if (frameLength < lengthFieldEndOffset) {
in.skipBytes(lengthFieldEndOffset);
throw new CorruptedFrameException(
"Adjusted frame length (" + frameLength + ") is less " +
"than lengthFieldEndOffset: " + lengthFieldEndOffset);
}
if (frameLength > maxFrameLength) {
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);
return null;
}
// never overflows because it's less than maxFrameLength
int frameLengthInt = (int) frameLength;
if (in.readableBytes() < frameLengthInt) {
return null;
}
if (initialBytesToStrip > frameLengthInt) {
in.skipBytes(frameLengthInt);
throw new CorruptedFrameException(
"Adjusted frame length (" + frameLength + ") is less " +
"than initialBytesToStrip: " + 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;
}
lengthFiledEndOffset上一篇文章我们分析过,在LengthFieldBasedFrameDecoder的构造器里面初始化,
lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
代表长度域偏移加上长度域长度。
那么in.readableBytes()<lengthFieldEndOffset的话,说明数据包不完整,连lengthFiledOffset的长度都达不到,数据包肯定不完整,所以返回空,然后累加器就会继续累加,知道数据包完整为止。
接下去看这一段:
int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;
lengthFiledOffset是我们的相对偏移量,加上readerIndex就是在这个byteBuf里面的绝对偏移量了,通过这个绝对偏移量,我们后续的操作就可以变得更加简单。
接下去看:
long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);
通过in这个byteBuf,actualLengthFieldOffset实际偏移量和lengthFieldLength就能找出我们信息的长度。点击去看怎么实现的:
/**
* Decodes the specified region of the buffer into an unadjusted frame length. The default implementation is
* capable of decoding the specified region into an unsigned 8/16/24/32/64 bit integer. Override this method to
* decode the length field encoded differently. Note that this method must not modify the state of the specified
* buffer (e.g. {@code readerIndex}, {@code writerIndex}, and the content of the buffer.)
*
* @throws DecoderException if failed to decode the specified region
*/
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;
}
简单,就是在buf里面找到长度域的位置,然后取出长度域里面的值,作为frameLength返回。
然后:
if (frameLength < 0) {
in.skipBytes(lengthFieldEndOffset);
throw new CorruptedFrameException(
"negative pre-adjustment length field: " + frameLength);
}
如果取出的frameLength<0,那就是不符合条件的,抛出异常。
然后:
frameLength += lengthAdjustment + lengthFieldEndOffset;
我们上一篇讲过一个调整值,长度域里面内容不一定表示的就是实际内容的长度,它可能加上长度域的长度或者head或者两者都有,所以我们通过这个调整值来调整。
所以最终frameLength就是frameLength加上调整值,再加上lengthEndOffset。
我们以以下这个图作为例子:
由于长度域表示的值是8,这个8包括了长度域2和长度域前面的两个内容2,当然还有最终的信息4,所以是8,所以需要调整 -4,也就是减去长度域2和长度域之前的内容2。最终上面那个表达式的值就是8,也就是最终读取到的整段信息的内容。
然后:
if (frameLength < lengthFieldEndOffset) {
in.skipBytes(lengthFieldEndOffset);
throw new CorruptedFrameException(
"Adjusted frame length (" + frameLength + ") is less " +
"than lengthFieldEndOffset: " + lengthFieldEndOffset);
}
这个frameLength是经过调整之后的frameLength,经过调整之后的frameLength是整段信息内容也就是包括lengthFieldEndOffset的,但是现在小于lengthFieldEndOffset明显不对,跳过lengthFieldEndOffset这段内容,并且抛出异常。
接下去一段的内容是丢弃模式的处理,我们放最后。
二、跳过字节逻辑处理
看这一段:
// never overflows because it's less than maxFrameLength
int frameLengthInt = (int) frameLength;
if (in.readableBytes() < frameLengthInt) {
return null;
}
到了这里就说明我们的数据是正确的,没有超过maxFrameLength。先转换为10进制。
如果当前可读的内容小于我们需要读取的内容,那么就说明数据包不完整,继续回去读。
接下去:
if (initialBytesToStrip > frameLengthInt) {
in.skipBytes(frameLengthInt);
throw new CorruptedFrameException(
"Adjusted frame length (" + frameLength + ") is less " +
"than initialBytesToStrip: " + initialBytesToStrip);
}
如果需要跳过的直接大于当前已有的字节,当然也是不对的,继续抛出异常。
最后才是真正跳过字节逻辑的处理:
in.skipBytes(initialBytesToStrip);
取出readerIndex,并且求出真正的跳过之后信息的长度:
// extract frame
int readerIndex = in.readerIndex();
int actualFrameLength = frameLengthInt - initialBytesToStrip;
最后把需要的信息提取出来,并且移动指针,然后把取到的信息返回:
ByteBuf frame = extractFrame(ctx, in, readerIndex, actualFrameLength);
in.readerIndex(readerIndex + actualFrameLength);
return frame;
三、丢弃模式下的处理
我们先分析代码是如何进入丢弃模式的,也就是这一段:
if (frameLength > maxFrameLength) {
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);
return null;
}
当解析到的frameLength大于maxFrameLength了,那就是可能要进入丢弃模式了。
discard=frameLength-in.readableBytes
frameLength本来这一段都是要丢弃的,但是有可能当前可读的数据还小于frameLength的长度,那就还有discard这么多的字节,后续需要丢弃。
如果discard<0,说明当前可读的大于frameLength长度了,frameLength这么长的数据就直接丢弃:
if (discard < 0) {
// buffer contains more bytes then the frameLength so we can discard all now
in.skipBytes((int) frameLength);
}
否则,就要进入丢弃模式:
进入丢弃模式,并且需要记录剩下的还没有丢弃的数据,然后跳过当前可读的数据。
if (discard < 0) {
...
} else {
// Enter the discard mode and discard everything received so far.
discardingTooLongFrame = true;
bytesToDiscard = discard;
in.skipBytes(in.readableBytes());
}
最后,调用failIfNecessary,因为是第一次,所以我们firstDetectionOfTooLongFrame为true。
我们看failIfNecessary:
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 ||
failFast && firstDetectionOfTooLongFrame) {
fail(tooLongFrameLength);
}
} else {
// Keep discarding and notify handlers if necessary.
if (failFast && firstDetectionOfTooLongFrame) {
fail(tooLongFrameLength);
}
}
}
如果还需要丢弃的数据为0,那就可以结束丢弃模式了,并且tooLongFrameLength的值赋给别的变量,方便做后续异常处理的参数。如果是非快速失败模式,或者是快速失败模式,并且是第一次进来(因为我们上一步判断discard的时候只用了>0,没有考虑到=0的情况,所以也有可能进入到这个判断,这种情况是第一次进入丢弃模式,但是马上在这里又还原了,所以也可以调用fail方法),那就调用fail抛出异常。
否则,符合failFast模式并且是第一次进来也是抛出异常。
然后我们看函数一开始进来的代码:
if (discardingTooLongFrame) {
long bytesToDiscard = this.bytesToDiscard;
int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes());
in.skipBytes(localBytesToDiscard);
bytesToDiscard -= localBytesToDiscard;
this.bytesToDiscard = bytesToDiscard;
failIfNecessary(false);
}
这里就是在丢弃模式下需要处理的事情了。
比较需要丢弃的数据的长度和可读的长度,取最小值,就是是我们当前可以丢弃的长度:
int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes());
然后跳过当前可以跳过的长度:
in.skipBytes(localBytesToDiscard);
最后把之前需要丢弃的长度,减去当前丢弃了的长度,就得到还需要丢弃的长度,这个还需要丢弃的长度>=0,大于0说明可读长度还小于bytesToDiscard,还有内容需要丢弃,等于0说明都丢弃完了;然后记录还需要丢弃的长度。
bytesToDiscard -= localBytesToDiscard;
this.bytesToDiscard = bytesToDiscard;
最后还是调用failIfNecessary,但是这次不再是第一次了,所以firstDetectionOfTooLongFrame传入false。和之前第一次的failIfNecessary比较一下,会有更加深入的理解。