上一節(jié)中提到了對象反序列化解析器ObjectDecoder,ObjectDecoder是繼承自LengthFieldBasedFrameDecoder构捡,LengthFieldBasedFrameDecoder是Netty通用的拆包器
LengthFieldBasedFrameDecoder用法
LengthFieldBasedFrameDecoder源碼解釋中是這么說的:
通過長度域lengthFieldLength對ByteBuf進行動態(tài)拆分的decoder,特別適合當(dāng)有一個int做了消息包體頭部進行反序列化
LengthFieldBasedFrameDecoder有很多配置參數(shù)所以可以對任何指定長度域lengthFieldLength的消息進行反序列化她我,所以經(jīng)常用戶客戶端和服務(wù)端的通信協(xié)議。
LengthFieldBasedFrameDecoder的幾個重要參數(shù):
lengthFiledLength: 長度域大小
maxFrameLength:最大幀長度迫横,即單個包最大的長度
lengthFieldOffset:表示數(shù)據(jù)長度字段開始的偏移量
lengthAdjustment:長度調(diào)整值
initialBytesToStrip:表示從整個包第一個字節(jié)開始鸦难,向后忽略的字節(jié)數(shù)
1、基于長度的拆包
長度域2個字節(jié)员淫,偏移量0合蔽,initialBytesToStrip為0
2、過濾包頭Header介返,只獲取包體數(shù)據(jù)
lengthFieldLength = 2 //數(shù)據(jù)長度為2個字節(jié)
initialBytesToStrip=2 //等于數(shù)據(jù)的長度拴事,意思就是跳過數(shù)據(jù)長度字節(jié)
lengthFieldOffset=0 //偏移量為0
3沃斤、lengthFieldLength代表數(shù)據(jù)Header+Body的長度,
4刃宵、基于可調(diào)整長度的拆包
總之衡瓶,反序列首先會跳過initialBytesToStrip個字節(jié)讀取buyBuf,lengthAdjustment表示長度域后還有多少個字節(jié)才是真正的數(shù)據(jù)長度。lengthFieldOffset表示還有多少偏移才是真正的長度域
源碼分析
構(gòu)造函數(shù)
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;
}
lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;表示長度域后第一個字節(jié)在整個包的偏移量
具體的拆包協(xié)議:
@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);
}
}
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
//是否丟棄超出長度的數(shù)據(jù)幀 默認為false
if (discardingTooLongFrame) {
long bytesToDiscard = this.bytesToDiscard;
int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes());
in.skipBytes(localBytesToDiscard);
bytesToDiscard -= localBytesToDiscard;
this.bytesToDiscard = bytesToDiscard;
failIfNecessary(false);
}
//如果buf中可讀字節(jié)數(shù)小于長度域最小偏移量 說明肯定讀不到長度域 直接返回
if (in.readableBytes() < lengthFieldEndOffset) {
return null;
}
//bytebuf讀位置索引+長度域偏移量=長度域在整個buf中實際偏移量
int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;
//獲取實際未調(diào)整的包長度
long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);
//如果包長度小于0 拋異常
if (frameLength < 0) {
in.skipBytes(lengthFieldEndOffset);
throw new CorruptedFrameException(
"negative pre-adjustment length field: " + frameLength);
}
//整個包的長度 = 未調(diào)整的包長度+長度域調(diào)整值+長度域偏移量
frameLength += lengthAdjustment + lengthFieldEndOffset;
//如果整個包長度小于長度域偏移量 異常
if (frameLength < lengthFieldEndOffset) {
in.skipBytes(lengthFieldEndOffset);
throw new CorruptedFrameException(
"Adjusted frame length (" + frameLength + ") is less " +
"than lengthFieldEndOffset: " + lengthFieldEndOffset);
}
//整個包長度大于最大定義的數(shù)據(jù)包長度 丟棄模式
if (frameLength > maxFrameLength) {
//多余的字節(jié)數(shù) = 數(shù)據(jù)包長度-buf可讀字節(jié)數(shù)
long discard = frameLength - in.readableBytes();
tooLongFrameLength = frameLength;
// 多余的字節(jié)數(shù)小于0牲证,表示當(dāng)前可讀字節(jié)已達到frameLength哮针,直接跳過frameLength個字節(jié),丟棄之后坦袍,后面有可能就是一個合法的數(shù)據(jù)包
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;
// 當(dāng)前可讀字節(jié)未達到frameLength十厢,說明后面未讀到的字節(jié)也需要丟棄,進入丟棄模式捂齐,先把當(dāng)前累積的字節(jié)全部丟棄
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;
}
//如果需要跳過的字節(jié)數(shù)大于數(shù)據(jù)包長度 直接拋異常
if (initialBytesToStrip > frameLengthInt) {
in.skipBytes(frameLengthInt);
throw new CorruptedFrameException(
"Adjusted frame length (" + frameLength + ") is less " +
"than initialBytesToStrip: " + initialBytesToStrip);
}
//跳過字節(jié)數(shù)
in.skipBytes(initialBytesToStrip);
// extract frame
//拿到當(dāng)前buf的讀偏移量
int readerIndex = in.readerIndex();
//實際數(shù)據(jù)包長度 = 整個包長度減去忽略的字節(jié)數(shù)
int actualFrameLength = frameLengthInt - initialBytesToStrip;
//拿到待抽取的數(shù)據(jù)幀
ByteBuf frame = extractFrame(ctx, in, readerIndex, actualFrameLength);
//移動讀指針
in.readerIndex(readerIndex + actualFrameLength);
return frame;
}
getUnadjustedFrameLength獲取未調(diào)整數(shù)據(jù)長度的源碼:
protected long getUnadjustedFrameLength(ByteBuf buf, int offset, int length, ByteOrder order) {
//入?yún)ffsset是in.readerIndex() + lengthFieldOffset蛮放,即buf讀指針+長度偏移量,
//那么獲取長度字段所在的位置奠宜,然后讀取消息長度字段所占字節(jié)數(shù)的字節(jié)(消息長度字段占用的字節(jié)數(shù)是協(xié)議約定好的)
//讀取出來的數(shù)值就是消息長度的值了包颁。
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;
}