LengthFieldBasedFrameDecoder解析
LengthFieldBasedFrameDecoder 是netty中功能最强大,应用最广泛的一个解码器,可以方便的用其解析用户的自定义协议。
1、参数说明
- lengthFieldOffset 长度域的偏移量,简单而言就是偏移几个字节是长度域
- lengthFieldLength : 长度域的所占的字节数
- lengthAdjustment : 长度值的调整值
- initialBytesToStrip : 需要跳过的字节数
如图所示:上面是原报文,下面是目标报文
因为长度域的起始位置在在报文开头偏移量为2的地方,所以LengthFiledOffSet的值为2。
因为长度域占用两个字节,所以LengthFiledLength的值为2。
因为长度域的值为4 ,Netty默认认为长度字段后面的都是报文内容,但是在实际的业务场景中,并不一定是这样,比如上图中的这个情况,目标报文最终需要解析出来5个字节,因此lengthAdjustment的值为1.
因为最终解析出来的报文只包含有效的数据报文,没有了长度值及其前面的报文,因此需要将前面的报文跳过,也就是跳过之前的4个字节,所以initialBytesToStrip的值为4.
2、解码过程
2.1、计算要抽取的数据包的长度![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/4398bbdae4055f0ba61d535e3753a325.png)
获取实际的长度域的绝对位置的偏移量,然后从这个偏移量开始,读取lengthFieldLength 个大小的字节,得到报文长度的值。
从代码中可以看出,这个lengthFieldLength只支持1,2,3,4,8这几种,其它会抛出异常。
如果长度值小于0,抛出异常。然后计算报文的真实长度,需要加上
lengthAdjustment和lengthFieldEndOffset,因为报文的长度是包含长度域之前的长度所占的字节和长度域的偏移量所占的字节,
2.2、 跳过字节的逻辑处理
跳过的比长度还长,抛出异常,否则的话,就跳过initialBytesToStrip这么些字节。然后移动读索引,然后读取指定长度的字节流,就完成了解码的逻辑。
2.3、丢弃模式的处理
当长度值大于最大长度的时候,就说明这段字节流有问题,需要进行丢弃。
需要完整丢弃FrameLength长度的字节,如果当前没有这么多,下一轮循环的时候继续丢弃。并且会抛出一个异常出来。
3、总结
解析过程就是先获取lengthFieldLength的绝对位置,该值等于当前读索引+lengthFieldOffset,这样便获取到了长度值的位置,然后跳过lengthFieldOffset,读取lengthFieldLength的值,这样便拿到了长度的值,但是这个值只是lengthFieldLength代表的值,还需要根据lengthAdjustment进行调整,因此给长度值再加上 lengthAdjustment+lengthFieldOffset+lengthFieldLength,这样获取到了最终的长度,再向后传播的时候,会跳过一些字节,所以最终再跳过一段字节也就是
int actualFrameLength = frameLengthInt - initialBytesToStrip;
这样便获取到了最终的字节流的长度,然后读取出来即拿到了一个完整的报文。
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;
// 如果bytesToDiscard = 0 了则说明已经丢弃完了,如果没有等于0 说明还需要进行丢弃
failIfNecessary(false);
}
// 可读的字节数小于长度域的偏移量的长度,如果lengthFieldEndOffset设置为2
// 说明当前可读的字节数还不到两个
if (in.readableBytes() < lengthFieldEndOffset) {
return null;
}
// 长度域的偏移量所在的绝对位置,因为之前可能有已经解析过的数据
int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;
// 读取出lengthFieldLength所占字节对应的数,也就是帧长度的大小
long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);
// 长度小于0
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 {
// 不能丢弃完整个frameLength ,先丢弃当前所有的,等接收到了字节流再进行丢弃
// 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);
// 设置readIndex
in.readerIndex(readerIndex + actualFrameLength);
return frame;
}