Netty-源码分析LineBasedFrameDecoder
Posted 征服.刘华强
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty-源码分析LineBasedFrameDecoder相关的知识,希望对你有一定的参考价值。
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;
以上是关于Netty-源码分析LineBasedFrameDecoder的主要内容,如果未能解决你的问题,请参考以下文章