LineBasedFrameDecoder源碼分析
package io.netty.handler.codec;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ByteProcessor;
import java.util.List;
//\r\n分割器,編碼採用ASCII或者UTF-8,因爲\r\n的ASCII,或UTF-8編碼都是一致的。
//都是0D,0A,再0x7F[0111 1111]範圍內,超過這個範圍的碼點採用DelimiterBasedFrameDecoder解碼
public class LineBasedFrameDecoder extends ByteToMessageDecoder {
//每段消息的最大長度
private final int maxLength;
//超過最大長度是否拋出異常
/** Whether or not to throw an exception as soon as we exceed maxLength. */
private final boolean failFast = false;
//是否跳過分隔符
private final boolean stripDelimiter = true;
//true的話需要丟棄一部分字節,因爲超過了每段消息的最大長度
/** True if we're discarding input because we're already over maxLength. */
private boolean discarding;
//丟棄字節的長度
private int discardedBytes;
//偏移量
/** Last scan position. */
private int offset;
@Override
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
//如果解析出decoded就放入out集合,父類會fire給下一個handler
Object decoded = decode(ctx, in);
if (decoded != null) {
out.add(decoded);
}
}
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
//查詢\r\n的位置 假設消息爲 "abcedf\r\n" 整體length=8 那麼eol=6
final int eol = findEndOfLine(buffer);
//discarding=false 說明正常情況
if (!discarding) {
//查詢到\r\n的位置
if (eol >= 0) {
final ByteBuf frame;
//6-0 = 6 是消息的長度
final int length = eol - buffer.readerIndex();
//eol的位置 如果是\r則分隔符2個字節, 否則一個字節。
final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
//如果消息長度 大於 每段消息的最大長度限制
if (length > maxLength) {
//把緩衝區readerIndex設置爲0,相當於丟棄了前面的字節數據。
buffer.readerIndex(eol + delimLength);
//fire異常其它handler
fail(ctx, length);
return null;
}
//正常情況
//去掉分隔符
if (stripDelimiter) {
//返回子緩衝區,原始緩衝區的readerIndex會往後遞增length個位置
//frame.refCnt=1 原始緩衝區的refCnt=2
//frame=[abcdef]
frame = buffer.readRetainedSlice(length);
//原始緩衝區丟棄delimLength長度的字節(丟棄分隔符)
buffer.skipBytes(delimLength);
} else {
//不丟棄分隔符,那麼length需要在加上分隔符的長度 6+2=8
frame = buffer.readRetainedSlice(length + delimLength);
}
//返回子緩衝區
return frame;
} else {
//沒找到分隔符位置
//可讀字節長度
final int length = buffer.readableBytes();
//大於每段信息最大長度範圍
if (length > maxLength) {
//記錄需要丟棄的字節數
discardedBytes = length;
//跳過length個字節
buffer.readerIndex(buffer.writerIndex());
//設置標記
discarding = true;
offset = 0;
//拋異常
if (failFast) {
fail(ctx, "over " + discardedBytes);
}
}
return null;
}
} else { //進到這裏,說明之前發生過特殊情況,需要丟棄一個消息
//找到了分隔符
if (eol >= 0) {
//discardedBytes是之前記錄需要丟棄的字節數 [eol - buffer.readerIndex()]是分隔符前面的字節數
final int length = discardedBytes + eol - buffer.readerIndex();
//分隔符長度
final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
//丟棄分隔符之前的字節(包括分隔符)
buffer.readerIndex(eol + delimLength);
//重置標記變量
discardedBytes = 0;
discarding = false;
//拋異常
if (!failFast) {
fail(ctx, length);
}
} else {
//沒找到分隔符
//增加discardedBytes的值 然後繼續丟棄字節
discardedBytes += buffer.readableBytes();
buffer.readerIndex(buffer.writerIndex());
// We skip everything in the buffer, we need to set the offset to 0 again.
offset = 0;
}
return null;
}
}
//fire異常信息
private void fail(final ChannelHandlerContext ctx, int length) {
fail(ctx, String.valueOf(length));
}
private void fail(final ChannelHandlerContext ctx, String length) {
ctx.fireExceptionCaught(
new TooLongFrameException(
"frame length (" + length + ") exceeds the allowed maximum (" + maxLength + ')'));
}
private int findEndOfLine(final ByteBuf buffer) {
//buffer緩衝區中可讀字節數
int totalLength = buffer.readableBytes();
//從readerIndex+offset開始,到totalLength - offset截止,默認offset=0,查找\n的位置
int i = buffer.forEachByte(buffer.readerIndex() + offset, totalLength - offset, ByteProcessor.FIND_LF);
//找到
if (i >= 0) {
offset = 0;
//看前一個字節是否是\r
if (i > 0 && buffer.getByte(i - 1) == '\r') {
//位置往前挪一位
i--;
}
} else {
//沒找到- offset等於當前可讀字節數
offset = totalLength;
}
return i;
}
}