Netty源码分析第六章: 解码器
第三节: 行解码器
这一小节了解下行解码器LineBasedFrameDecoder, 行解码器的功能是一个字节流, 以\r\n或者直接以\n结尾进行解码, 也就是以换行符为分隔进行解析
同样, 这个解码器也继承了ByteToMessageDecoder
首先看其参数:
//数据包的最大长度, 超过该长度会进行丢弃模式private final int maxLength;//超出最大长度是否要抛出异常private final boolean failFast;//最终解析的数据包是否带有换行符private final boolean stripDelimiter;//为true说明当前解码过程为丢弃模式private boolean discarding;//丢弃了多少字节private int discardedBytes;
其中的丢弃模式, 我们会在源码中看到其中的含义
我们看其decode方法:
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { ???Object decoded = decode(ctx, in); ???if (decoded != null) { ???????out.add(decoded); ???}}
这里的decode方法和我们上一小节分析的decode方法一样, 调用重载的decode方法, 并将解码后的内容放到out集合中
我们跟到重载的decode方法中:
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { ???//找这行的结尾 ???final int eol = findEndOfLine(buffer); ???if (!discarding) { ???????if (eol >= 0) { ???????????final ByteBuf frame; ???????????//计算从换行符到可读字节之间的长度 ???????????final int length = eol - buffer.readerIndex(); ???????????//拿到分隔符长度, 如果是\r\n结尾, 分隔符长度为2 ???????????final int delimLength = buffer.getByte(eol) == ‘\r‘? 2 : 1; ???????????//如果长度大于最大长度 ???????????if (length > maxLength) { ???????????????//指向换行符之后的可读字节(这段数据完全丢弃) ???????????????buffer.readerIndex(eol + delimLength); ???????????????//传播异常事件 ???????????????fail(ctx, length); ???????????????return null; ???????????} ???????????//如果这次解析的数据是有效的 ???????????//分隔符是否算在完整数据包里 ???????????//true为丢弃分隔符 ???????????if (stripDelimiter) { ???????????????//截取有效长度 ???????????????frame = buffer.readRetainedSlice(length); ???????????????//跳过分隔符的字节 ???????????????buffer.skipBytes(delimLength); ???????????} else { ???????????????//包含分隔符 ???????????????frame = buffer.readRetainedSlice(length + delimLength); ???????????} ???????????return frame; ???????} else { ???????????//如果没找到分隔符(非丢弃模式) ???????????//可读字节长度 ???????????final int length = buffer.readableBytes(); ???????????//如果朝超过能解析的最大长度 ???????????if (length > maxLength) { ???????????????//将当前长度标记为可丢弃的 ???????????????discardedBytes = length; ???????????????//直接将读指针移动到写指针 ???????????????buffer.readerIndex(buffer.writerIndex()); ???????????????//标记为丢弃模式 ???????????????discarding = true; ???????????????//超过最大长度抛出异常 ???????????????if (failFast) { ???????????????????fail(ctx, "over " + discardedBytes); ???????????????} ???????????} ???????????//没有超过, 则直接返回 ???????????return null; ???????} ???} else { ???????//丢弃模式 ???????if (eol >= 0) { ???????????//找到分隔符 ???????????//当前丢弃的字节(前面已经丢弃的+现在丢弃的位置-写指针) ???????????final int length = discardedBytes + eol - buffer.readerIndex(); ???????????//当前换行符长度为多少 ???????????final int delimLength = buffer.getByte(eol) == ‘\r‘? 2 : 1; ???????????//读指针直接移到换行符+换行符的长度 ???????????buffer.readerIndex(eol + delimLength); ???????????//当前丢弃的字节为0 ???????????discardedBytes = 0; ???????????//设置为未丢弃模式 ???????????discarding = false; ???????????//丢弃完字节之后触发异常 ???????????if (!failFast) { ???????????????fail(ctx, length); ???????????} ???????} else { ???????????//累计已丢弃的字节个数+当前可读的长度 ???????????discardedBytes += buffer.readableBytes(); ???????????//移动 ???????????buffer.readerIndex(buffer.writerIndex()); ???????} ???????return null; ???}}
final int eol = findEndOfLine(buffer) 这里是找当前行的结尾的索引值, 也就是\r\n或者是\n:
6-3-1
图中不难看出, 如果是以\n结尾的, 返回的索引值是\n的索引值, 如果是\r\n结尾的, 返回的索引值是\r的索引值
我们看findEndOfLine(buffer)方法:
private static int findEndOfLine(final ByteBuf buffer) { ???//找到/n这个字节 ???int i = buffer.forEachByte(ByteProcessor.FIND_LF); ???//如果找到了, 并且前面的字符是-r, 则指向/r字节 ???if (i > 0 && buffer.getByte(i - 1) == ‘\r‘) { ???????i--; ???} ???return i;}
这里通过一个forEachByte方法找\n这个字节, 如果找到了, 并且前面是\r, 则返回\r的索引, 否则返回\n的索引
回到重载的decode方法中:
if (!discarding) 判断是否为非丢弃模式, 默认是就是非丢弃模式, 所以进入if中
if (eol >= 0) 如果找到了换行符, 我们看非丢弃模式下找到换行符的相关逻辑:
final ByteBuf frame; final int length = eol - buffer.readerIndex();final int delimLength = buffer.getByte(eol) == ‘\r‘? 2 : 1; if (length > maxLength) { ???buffer.readerIndex(eol + delimLength); ???fail(ctx, length); ???return null;} if (stripDelimiter) { ????frame = buffer.readRetainedSlice(length); ????buffer.skipBytes(delimLength);} else { ????frame = buffer.readRetainedSlice(length + delimLength);}return frame;
首先获得换行符到可读字节之间的长度, 然后拿到换行符的长度, 如果是\n结尾, 那么长度为1, 如果是\r结尾, 长度为2
if (length > maxLength) 带表如果长度超过最大长度, 则直接通过 readerIndex(eol + delimLength) 这种方式, 将读指针指向换行符之后的字节, 说明换行符之前的字节需要完全丢弃
6-3-2
丢弃之后通过fail方法传播异常, 并返回null
继续往下看, 走到下一步, 说明解析出来的数据长度没有超过最大长度, 说明是有效数据包
if (stripDelimiter) 表示是否要将分隔符放在完整数据包里面, 如果是true, 则说明要丢弃分隔符, 然后截取有效长度, 并跳过分隔符长度
将包含分隔符进行截取
以上就是非丢弃模式下找到换行符的相关逻辑
我们再看非丢弃模式下没有找到换行符的相关逻辑, 也就是非丢弃模式下, if (eol >= 0) 中的else块:
final int length = buffer.readableBytes();if (length > maxLength) { ????discardedBytes = length; ????buffer.readerIndex(buffer.writerIndex()); ???discarding = true; ????if (failFast) { ???????fail(ctx, "over " + discardedBytes); ???}} return null;
首先通过 final int length = buffer.readableBytes() 获取所有的可读字节数
然后判断可读字节数是否超过了最大值, 如果超过最大值, 则属性discardedBytes标记为这个长度, 代表这段内容要进行丢弃
6-3-3
buffer.readerIndex(buffer.writerIndex()) 这里直接将读指针移动到写指针, 并且将discarding设置为true, 就是丢弃模式
如果可读字节没有超过最大长度, 则返回null, 表示什么都没解析出来, 等着下次解析
我们再看丢弃模式的处理逻辑, 也就是 if (!discarding) 中的else块:
首先这里也分两种情况, 根据 if (eol >= 0) 判断是否找到了分隔符, 我们首先看找到分隔符的解码逻辑:
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);}
如果找到换行符, 则需要将换行符之前的数据全部丢弃掉
6-3-4
final int length = discardedBytes + eol - buffer.readerIndex() 这里获得丢弃的字节总数, 也就是之前丢弃的字节数+现在需要丢弃的字节数
然后计算换行符的长度, 如果是\n则是1, \r\n就是2
buffer.readerIndex(eol + delimLength) 这里将读指针移动到换行符之后的位置
然后将discarding设置为false, 表示当前是非丢弃状态
我们再看丢弃模式未找到换行符的情况, 也就是丢弃模式下, if (eol >= 0) 中的else块:
discardedBytes += buffer.readableBytes();buffer.readerIndex(buffer.writerIndex());
这里做的事情非常简单, 就是累计丢弃的字节数, 并将读指针移动到写指针, 也就是将数据全部丢弃
最后在丢弃模式下, decode方法返回null, 代表本次没有解析出任何数据
以上就是行解码器的相关逻辑
Netty源码分析第6章(解码器)---->第3节: 行解码器
原文地址:https://www.cnblogs.com/xiangnan6122/p/10206448.html