Netty分布式行解码器
向南是个万人迷 人气:0前文传送门:Netty分布式固定长度解码器实现原理剖析
这一小节了解下行解码器LineBasedFrameDecoder, 行解码器的功能是一个字节流, 以\r\n或者直接以\n结尾进行解码, 也就是以换行符为分隔进行解析
同样, 这个解码器也继承了ByteToMessageDecoder
行解码器LineBasedFrameDecoder
首先看其参数
//数据包的最大长度, 超过该长度会进行丢弃模式 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, 则说明要丢弃分隔符, 然后截取有效长度, 并跳过分隔符长度
将包含分隔符进行截取
加载全部内容