编程语言
首页 > 编程语言> > Netty源码(十)之LineBasedFrameDecoder和DelimiterBasedFrameDecoder

Netty源码(十)之LineBasedFrameDecoder和DelimiterBasedFrameDecoder

作者:互联网

上篇博客我们说了其中一个解码器,就是基于固定长度的解码器FixedLengthFrameDecoder,这种解码器,不具有通用性,很多时候,都是用不到的,我们来介绍一下新的解码器,就是基于行(\n,\r)的解码器LineBasedFrameDecoder。由于上篇博客我们已经介绍了一个最简单的解码器,这篇博客不会在介绍前置的流程了,具体的前置的流程可以看上篇博客。我们打开对应的LineBasedFrameDecoder的源码,发现和FixedLengthFrameDecoder继承的是同一个父类ByteToMessageDecoder,所以流程也是一样的,都是先调用父类的channelRead方法,唯一不同的就是子类的decode方法。所以我们只需要查看LineBasedFrameDecoder类的decode方法,具体的代码如下:

public class LineBasedFrameDecoder extends ByteToMessageDecoder {
  /** 解码的最大长度.  */
  private final int maxLength;
  /** 是否在超过maxLength时立即抛出异常。 */
  private final boolean failFast;
  /**是否解析换行符(\n,\r\n)*/
  private final boolean stripDelimiter;
  /** 如果因为已经超过maxLength的长度,而丢弃数据,则为True。  */
  private boolean discarding;
  /**已经丢弃多少字节*/
  private int discardedBytes;
  /**最后一次扫描的位置 */
  private int offset;
  
  @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 buffer) throws Exception {
    //返回\r\n的下标位置
    final int eol = findEndOfLine(buffer);
    if (!discarding) {
      if (eol >= 0) {
        final ByteBuf frame;
        //算出本次要截取数据的长度
        final int length = eol - buffer.readerIndex();
        //判断\n前面是否是\r  是返回2  不是返回1
        final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
        //如果读取的长度>指定的最大长度
        if (length > maxLength) {
          //设置此缓冲区的readerIndex。  跳过这段数据
          buffer.readerIndex(eol + delimLength);
          fail(ctx, length);
          return null;
        }
        //判断解析的数据是否要带\r\n
        if (stripDelimiter) {
          //返回从当前readerIndex开始的缓冲区子区域的一个新保留的片
          //返回到\r\n的有效数据 不包括\r\n
          frame = buffer.readRetainedSlice(length);
          //跳过\r\n
          buffer.skipBytes(delimLength);
        } else {
          //截取到\r\n的有效数据 包括\r\n
          frame = buffer.readRetainedSlice(length + delimLength);
        }
        return frame;
      } else {
        //如果没有找到\r\n
        final int length = buffer.readableBytes();
        //如果本次数据的可读长度》最大可读长度
        if (length > maxLength) {
          //设置丢弃的长度为本次buffer的可读取长度
          discardedBytes = length;
          //跳过本次数据
          buffer.readerIndex(buffer.writerIndex());
          //设置为丢弃模式
          discarding = true;
          offset = 0;
          if (failFast) {
            fail(ctx, "over " + discardedBytes);
          }
        }
        return null;
      }
    } else {
      //找到了\r\n
      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 {
        //没找到\r\n
        //丢弃的数据长度+本次可读数据的长度
        discardedBytes += buffer.readableBytes();
        //跳过本次可读取的数据
        buffer.readerIndex(buffer.writerIndex());
        // 我们跳过缓冲区中的所有内容,需要再次将偏移量设置为0。
        offset = 0;
      }
      return null;
    }
  }
}

上面的代码走来是调用findEndOfLine(buffer);方法,我们继续跟进对应的代码,具体如下:

public class LineBasedFrameDecoder extends ByteToMessageDecoder {
  /**
  * 返回找到的行末尾的缓冲区中的索引。
  * 如果缓冲区中没有找到行尾,则返回-1。
  */
  private int findEndOfLine(final ByteBuf buffer) {
    int totalLength = buffer.readableBytes();
    //遍历这个buffer找到\n的位置。ByteProcessor.FIND_LF的值为\n
    int i = buffer.forEachByte(buffer.readerIndex() + offset, totalLength - offset, ByteProcessor.FIND_LF);
    if (i >= 0) {
      offset = 0;
      //如果它的前一个位置是\r,直接返回\r的位置
      if (i > 0 && buffer.getByte(i - 1) == '\r') {
        i--;
      }
    } else {
      offset = totalLength;
    }
    return i;
  }
}

上面的代码找\r\n的位置,如果找到是只有\n,就直接返回\n的位置,如果找到是\r\n的位置,就直接返回\r的位置,如果没有找到,就直接返回-1。我们再回到原来的代码地方,将原来的代码拆成两部分,我们先看非丢弃模式代码,具体如下:

public class LineBasedFrameDecoder extends ByteToMessageDecoder {
  /** 解码的最大长度.  */
  private final int maxLength;
  /** 是否在超过maxLength时立即抛出异常。 */
  private final boolean failFast;
  /**是否解析换行符(\n,\r\n)*/
  private final boolean stripDelimiter;
  /** 如果因为已经超过maxLength的长度,而丢弃数据,则为True。  */
  private boolean discarding;
  /**已经丢弃多少字节*/
  private int discardedBytes;
  /**最后一次扫描的位置 */
  private int offset;
  
  protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    //返回\r\n的下标位置
    final int eol = findEndOfLine(buffer);
    if (!discarding) {
      if (eol >= 0) {
        final ByteBuf frame;
        //算出本次要截取数据的长度
        final int length = eol - buffer.readerIndex();
        //判断\n前面是否是\r  是返回2  不是返回1
        final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
        //如果读取的长度>指定的最大长度
        if (length > maxLength) {
          //设置此缓冲区的readerIndex。  跳过这段数据
          buffer.readerIndex(eol + delimLength);
          fail(ctx, length);
          return null;
        }
        //判断解析的数据是否要带\r\n
        if (stripDelimiter) {
          //返回从当前readerIndex开始的缓冲区子区域的一个新保留的片
          //返回到\r\n的有效数据 不包括\r\n
          frame = buffer.readRetainedSlice(length);
          //跳过\r\n
          buffer.skipBytes(delimLength);
        } else {
          //截取到\r\n的有效数据 包括\r\n
          frame = buffer.readRetainedSlice(length + delimLength);
        }
        return frame;
      } else {
        //如果没有找到\r\n
        final int length = buffer.readableBytes();
        //如果本次数据的可读长度》最大可读长度
        if (length > maxLength) {
          //设置丢弃的长度为本次buffer的可读取长度
          discardedBytes = length;
          //跳过本次数据
          buffer.readerIndex(buffer.writerIndex());
          //设置为丢弃模式
          discarding = true;
          offset = 0;
          if (failFast) {
            fail(ctx, "over " + discardedBytes);
          }
        }
        return null;
      }
    } 
    //省略一部分代码
  }
}

上面的代码我们继续拆分。我们先看找到\r\n情况,具体的代码如下:

public class LineBasedFrameDecoder extends ByteToMessageDecoder {
  /** 解码的最大长度.  */
  private final int maxLength;
  /** 是否在超过maxLength时立即抛出异常。 */
  private final boolean failFast;
  /**是否解析换行符(\n,\r\n)*/
  private final boolean stripDelimiter;
  /** 如果因为已经超过maxLength的长度,而丢弃数据,则为True。  */
  private boolean discarding;
  /**已经丢弃多少字节*/
  private int discardedBytes;
  /**最后一次扫描的位置 */
  private int offset;
  
  protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    //返回\r\n的下标位置
    final int eol = findEndOfLine(buffer);
    if (!discarding) {
      if (eol >= 0) {
        final ByteBuf frame;
        //算出本次要截取数据的长度
        final int length = eol - buffer.readerIndex();
        //判断\n前面是否是\r  是返回2  不是返回1
        final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
        //如果读取的长度>指定的最大长度
        if (length > maxLength) {
          //设置此缓冲区的readerIndex。  跳过这段数据
          buffer.readerIndex(eol + delimLength);
          fail(ctx, length);
          return null;
        }
        //判断解析的数据是否要带\r\n
        if (stripDelimiter) {
          //返回从当前readerIndex开始的缓冲区子区域的一个新保留的片
          //返回到\r\n的有效数据 不包括\r\n
          frame = buffer.readRetainedSlice(length);
          //跳过\r\n
          buffer.skipBytes(delimLength);
        } else {
          //截取到\r\n的有效数据 包括\r\n
          frame = buffer.readRetainedSlice(length + delimLength);
        }
        return frame;
      }
    } 
    //省略一部分代码
  }
}

上面的代码是在非丢弃模式下的找到的\r\n的位置,走来我们拿\r\n的位置减去读指针的位置,得到的就是本次要截取的长度,并且存入到变量length中去。这个时候在判断返回的下标的值是\r还是\n,如果是\r,就需要跳过两个位置,如果是\n,只需要跳过一个位置。这儿通过delimLength变量保存下来,这个时候判断读取到长度是不是大于最大的长度,如果大于最大的长度,直接将读指针设置到\r\n的后面,表示跳过这段数据。然后抛出异常,直接返回null。如果读取的长度没有大于最大的长度,这个时候需要判断解析的时候需不需要带上\r\n,如果不用,直接就读取length长度的数据,并且跳过delimLength的长度。如果要解析的话,直接读取length+delimLength的长度,然后返回出去。

我们再看在非丢弃模式下没有找到\r\n的位置,具体的代码如下:

public class LineBasedFrameDecoder extends ByteToMessageDecoder {
  /** 解码的最大长度.  */
  private final int maxLength;
  /** 是否在超过maxLength时立即抛出异常。 */
  private final boolean failFast;
  /**是否解析换行符(\n,\r\n)*/
  private final boolean stripDelimiter;
  /** 如果因为已经超过maxLength的长度,而丢弃数据,则为True。  */
  private boolean discarding;
  /**已经丢弃多少字节*/
  private int discardedBytes;
  /**最后一次扫描的位置 */
  private int offset;
  
  protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    //返回\r\n的下标位置
    final int eol = findEndOfLine(buffer);
    if (!discarding) {
      //省略一部分代码
      else {
        //如果没有找到\r\n
        final int length = buffer.readableBytes();
        //如果本次数据的可读长度》最大可读长度
        if (length > maxLength) {
          //设置丢弃的长度为本次buffer的可读取长度
          discardedBytes = length;
          //跳过本次数据
          buffer.readerIndex(buffer.writerIndex());
          //设置为丢弃模式
          discarding = true;
          offset = 0;
          if (failFast) {
            fail(ctx, "over " + discardedBytes);
          }
        }
        return null;
      }
    } 
    //省略一部分代码
  }
}

走来直接读取buffer的课读取的字节长度存入到length中去,如果这次到length大于maxLength(最大的长度),这个时候将discardedBytes(丢弃的字节数)设置为length的长度,然后跳过这次的数据,同时将设置成丢弃模式,然后判断failFast是不是true,如果是true直接抛出异常。至此在非丢弃模式下的两种状态已经全部讲完了。

接下来我们看看丢弃模式下,找到\r\n的位置,具体的代码如下:

public class LineBasedFrameDecoder extends ByteToMessageDecoder {
  /** 解码的最大长度.  */
  private final int maxLength;
  /** 是否在超过maxLength时立即抛出异常。 */
  private final boolean failFast;
  /**是否解析换行符(\n,\r\n)*/
  private final boolean stripDelimiter;
  /** 如果因为已经超过maxLength的长度,而丢弃数据,则为True。  */
  private boolean discarding;
  /**已经丢弃多少字节*/
  private int discardedBytes;
  /**最后一次扫描的位置 */
  private int offset;
  
  protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    //返回\r\n的下标位置
    final int eol = findEndOfLine(buffer);
    //省略一部分代码
    else {
      //找到了\r\n
      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);
        }
      } 
      //省略一部分代码
      return null;
    }
  }
}

由于这儿是丢弃的模式,所以肯定有丢弃的数据的长度加上本次可读的数据长度,最后和上面一样,拿到分割符的长度,然后将本次的读指针设置为eol + delimLength,表示丢弃本次的数据,最后设置丢弃的数据长度为0,然后将模式改成非丢弃的模式。failFastfalse,抛出异常。

最后一种情况就是在丢弃模式下,没有找到\r\n的位置,具体的代码如下:

public class LineBasedFrameDecoder extends ByteToMessageDecoder {
  /** 解码的最大长度.  */
  private final int maxLength;
  /** 是否在超过maxLength时立即抛出异常。 */
  private final boolean failFast;
  /**是否解析换行符(\n,\r\n)*/
  private final boolean stripDelimiter;
  /** 如果因为已经超过maxLength的长度,而丢弃数据,则为True。  */
  private boolean discarding;
  /**已经丢弃多少字节*/
  private int discardedBytes;
  /**最后一次扫描的位置 */
  private int offset;
  protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    //返回\r\n的下标位置
    final int eol = findEndOfLine(buffer);
    else {
        //没找到\r\n
        //丢弃的数据长度+本次可读数据的长度
        discardedBytes += buffer.readableBytes();
        //跳过本次可读取的数据
        buffer.readerIndex(buffer.writerIndex());
        // 我们跳过缓冲区中的所有内容,需要再次将偏移量设置为0。
        offset = 0;
      }
      return null;
    }
  }
}

discardedBytes(丢弃的数据)加上这次读取到的数据组成新的丢弃数据,然后跳过本次可读取的数据。最后在丢弃模式下返回的数据都是null。这个基于行的解码器,就讲完了。下面我会讲另外一种解码器,是基于分隔符的解码器DelimiterBasedFrameDecoder,和基于行的解码器有异曲同工之妙。废话不多说,直接上代码。同样的我们只需要看decode方法即可,具体的代码如下:

public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
  //分割的字符
  private final ByteBuf[] delimiters;
  //最大的读取长度
  private final int maxFrameLength;
  //是否带分割符
  private final boolean stripDelimiter;
  //失败是否抛出异常
  private final boolean failFast;
  //是否是丢弃模式
  private boolean discardingTooLongFrame;
  //丢弃的数量
  private int tooLongFrameLength;
  private final LineBasedFrameDecoder lineBasedDecoder;
  
  @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 buffer) throws Exception {
    //首先判断行解码器是否被实例化了   被实例化了就使用行处理器
    if (lineBasedDecoder != null) {
      return lineBasedDecoder.decode(ctx, buffer);
    }
    // 尝试所有分隔符并选择产生最短帧的分隔符。
    int minFrameLength = Integer.MAX_VALUE;
    ByteBuf minDelim = null;
    for (ByteBuf delim: delimiters) {
      //找到最小分隔符的位置
      int frameLength = indexOf(buffer, delim);
      if (frameLength >= 0 && frameLength < minFrameLength) {
        //本次有效数据长度
        minFrameLength = frameLength;
        minDelim = delim;
      }
    }
    //如果找到了
    if (minDelim != null) {
      //分隔符长度
      int minDelimLength = minDelim.capacity();
      ByteBuf frame;
      //如果处于丢弃模式
      if (discardingTooLongFrame) {
        //设置为非丢弃模式
        discardingTooLongFrame = false;
        //跳过本次要截取的数据
        buffer.skipBytes(minFrameLength + minDelimLength);
        //设置丢弃的长度为0
        int tooLongFrameLength = this.tooLongFrameLength;
        this.tooLongFrameLength = 0;
        if (!failFast) {
          fail(tooLongFrameLength);
        }
        return null;
      }
      //本次要截取的数据大于最大能截取的长度
      if (minFrameLength > maxFrameLength) {
        //丢弃本次可读数据加分割符
        buffer.skipBytes(minFrameLength + minDelimLength);
        fail(minFrameLength);
        return null;
      }
      //有效数据是否要截取分隔符
      if (stripDelimiter) {
        frame = buffer.readRetainedSlice(minFrameLength);
        buffer.skipBytes(minDelimLength);
      } else {
        frame = buffer.readRetainedSlice(minFrameLength + minDelimLength);
      }
      return frame;
    } else {
      //如果没有找到分割符
      //判断是否处于非丢弃模式
      if (!discardingTooLongFrame) {
        //如果本次可读取的数据长度大于最大可读取长度
        if (buffer.readableBytes() > maxFrameLength) {
          // 丢弃缓冲区的内容,直到找到分隔符为止。
          //丢弃的长度等于本次可读的长度
          tooLongFrameLength = buffer.readableBytes();
          //丢弃
          buffer.skipBytes(buffer.readableBytes());
          //设置丢弃模式为true
          discardingTooLongFrame = true;
          if (failFast) {
            fail(tooLongFrameLength);
          }
        }
      } else {
        //仍然丢弃缓冲区,因为没有找到分隔符。
        tooLongFrameLength += buffer.readableBytes();
        buffer.skipBytes(buffer.readableBytes());
      }
      return null;
    }
  }
}

我们发现走来判断lineBasedDecoder是否为空,很明显在其他的地方,有初始化过,具体的代码如下:

public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
	public DelimiterBasedFrameDecoder(
    int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters) {
    validateMaxFrameLength(maxFrameLength);
    if (delimiters == null) {
      throw new NullPointerException("delimiters");
    }
    if (delimiters.length == 0) {
      throw new IllegalArgumentException("empty delimiters");
    }
    //调用isLineBased判断是否要初始化基于行的解码器
    if (isLineBased(delimiters) && !isSubclass()) {
      lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast);
      this.delimiters = null;
    } else {
      this.delimiters = new ByteBuf[delimiters.length];
      for (int i = 0; i < delimiters.length; i ++) {
        ByteBuf d = delimiters[i];
        validateDelimiter(d);
        this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes());
      }
      lineBasedDecoder = null;
    }
    this.maxFrameLength = maxFrameLength;
    this.stripDelimiter = stripDelimiter;
    this.failFast = failFast;
  }
}

可以看到上面是我们该类的构造函数,我们发现调用isLineBased(delimiters)来判断是否要初始化基于行的解码器,具体的代码如下:

private static boolean isLineBased(final ByteBuf[] delimiters) {
  if (delimiters.length != 2) {
    return false;
  }
  ByteBuf a = delimiters[0];
  ByteBuf b = delimiters[1];
  if (a.capacity() < b.capacity()) {
    a = delimiters[1];
    b = delimiters[0];
  }
  return a.capacity() == 2 && b.capacity() == 1
    && a.getByte(0) == '\r' && a.getByte(1) == '\n'
    && b.getByte(0) == '\n';
}

上面的代码就是判断分割符是否是\r\n或者是\n,如果是就返回true,然后会初始化基于行的解码器,如果不是就直接返回false。会执行else的代码,将传进来的分割符进行相应的分组。

最后我们再来看的核心的解码方法,具体如下:

public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
  //分割的字符
  private final ByteBuf[] delimiters;
  //最大的读取长度
  private final int maxFrameLength;
  //是否带分割符
  private final boolean stripDelimiter;
  //失败是否抛出异常
  private final boolean failFast;
  //是否是丢弃模式
  private boolean discardingTooLongFrame;
  //丢弃的数量
  private int tooLongFrameLength;
  private final LineBasedFrameDecoder lineBasedDecoder;
  
  protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    //省略一部分代码
    // 尝试所有分隔符并选择产生最短帧的分隔符。
    int minFrameLength = Integer.MAX_VALUE;
    ByteBuf minDelim = null;
    for (ByteBuf delim: delimiters) {
      //找到最小分隔符的位置
      int frameLength = indexOf(buffer, delim);
      if (frameLength >= 0 && frameLength < minFrameLength) {
        //本次有效数据长度
        minFrameLength = frameLength;
        minDelim = delim;
      }
    }
    //省略一部分代码
  }
}

走来会先执行indexOf(buffer, delim);方法,具体的代码如下:

//haystack接收到的buffer
//分割符buffer
private static int indexOf(ByteBuf haystack, ByteBuf needle) {
  for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) {
    int haystackIndex = i;
    int needleIndex;
    for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex ++) {
      if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) {
        break;
      } else {
        haystackIndex ++;
        if (haystackIndex == haystack.writerIndex() &&
            needleIndex != needle.capacity() - 1) {
          return -1;
        }
      }
    }
    if (needleIndex == needle.capacity()) {
      // Found the needle from the haystack!
      return i - haystack.readerIndex();
    }
  }
  return -1;
}

先是遍历接收到的buffer,也就是这儿的haystack,这是外层循环做的事,内层循环遍历分割符,如果相等的话,继续遍历内层循环,如果不想等直接结束内层循环。还有内层循环还要考虑边界的问题,当haystackIndex等于haystack.writerIndex()的时候,表示读到最后一个了这个时候直接返回到就是-1,如果全部匹配上了,就直接返回i - haystack.readerIndex();,也就是第一个匹配的下标。会存入minFrameLength变量中去。这个时候回到我们原来的代码,具体如下:

protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
  //首先判断行解码器是否被实例化了   被实例化了就使用行处理器
  if (lineBasedDecoder != null) {
    return lineBasedDecoder.decode(ctx, buffer);
  }
  // 尝试所有分隔符并选择产生最短帧的分隔符。
  int minFrameLength = Integer.MAX_VALUE;
  ByteBuf minDelim = null;
  for (ByteBuf delim: delimiters) {
    //找到最小分隔符的位置
    int frameLength = indexOf(buffer, delim);
    if (frameLength >= 0 && frameLength < minFrameLength) {
      //本次有效数据长度
      minFrameLength = frameLength;
      minDelim = delim;
    }
  }
  //如果找到了
  if (minDelim != null) {
    //分隔符长度
    int minDelimLength = minDelim.capacity();
    ByteBuf frame;
    //如果处于丢弃模式
    if (discardingTooLongFrame) {
      // We've just finished discarding a very large frame.
      // Go back to the initial state.
      //设置为非丢弃模式
      discardingTooLongFrame = false;
      //跳过本次要截取的数据
      buffer.skipBytes(minFrameLength + minDelimLength);
      //设置丢弃的长度为0
      int tooLongFrameLength = this.tooLongFrameLength;
      this.tooLongFrameLength = 0;
      if (!failFast) {
        fail(tooLongFrameLength);
      }
      return null;
    }
    //本次要截取的数据大于最大能截取的长度
    if (minFrameLength > maxFrameLength) {
      //丢弃本次可读数据加分割符
      buffer.skipBytes(minFrameLength + minDelimLength);
      fail(minFrameLength);
      return null;
    }
    //有效数据是否要截取分隔符
    if (stripDelimiter) {
      frame = buffer.readRetainedSlice(minFrameLength);
      buffer.skipBytes(minDelimLength);
    } else {
      frame = buffer.readRetainedSlice(minFrameLength + minDelimLength);
    }
    return frame;
  } else {
    //如果没有找到分割符
    //判断是否处于非丢弃模式
    if (!discardingTooLongFrame) {
      //如果本次可读取的数据长度大于最大可读取长度
      if (buffer.readableBytes() > maxFrameLength) {
        // 丢弃缓冲区的内容,直到找到分隔符为止。
        //丢弃的长度等于本次可读的长度
        tooLongFrameLength = buffer.readableBytes();
        //丢弃
        buffer.skipBytes(buffer.readableBytes());
        //设置丢弃模式为true
        discardingTooLongFrame = true;
        if (failFast) {
          fail(tooLongFrameLength);
        }
      }
    } else {
      //仍然丢弃缓冲区,因为没有找到分隔符。
      tooLongFrameLength += buffer.readableBytes();
      buffer.skipBytes(buffer.readableBytes());
    }
    return null;
  }
}

我们将上面的代码分成几种情况,第一种情况:非丢弃模式下找到分割符

protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
  //如果找到了
  if (minDelim != null) {
    //分隔符长度
    int minDelimLength = minDelim.capacity();
    ByteBuf frame;
    //省略一部分代码
    //本次要截取的数据大于最大能截取的长度
    if (minFrameLength > maxFrameLength) {
      //丢弃本次可读数据加分割符
      buffer.skipBytes(minFrameLength + minDelimLength);
      fail(minFrameLength);
      return null;
    }
    //有效数据是否要截取分隔符
    if (stripDelimiter) {
      frame = buffer.readRetainedSlice(minFrameLength);
      buffer.skipBytes(minDelimLength);
    } else {
      frame = buffer.readRetainedSlice(minFrameLength + minDelimLength);
    }
    return frame;
  }
  //省略一部分代码
}

走来先获取分割符的长度存入到变量minDelimLength中,如果本次读取到长度大于最大的能截取的长度,我们会直接丢弃本次的数据加上分割符。同时抛出异常,并返回null。如果没有超过最大的能截取的长度,我们先判断是否需要保留分割符,如果不需要直接将此次读取到的长度赋值给frame,同时改变读指针,跳过对应的分割符。如果要保留,直接读取数据加上分割符,同时赋值给frame,然后返回farme

下面我们看第二种情况:丢弃模式下找到分割符

protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
  //省略一部分代码
  //如果找到了
  if (minDelim != null) {
    //分隔符长度
    int minDelimLength = minDelim.capacity();
    ByteBuf frame;
    //如果处于丢弃模式
    if (discardingTooLongFrame) {
      // We've just finished discarding a very large frame.
      // Go back to the initial state.
      //设置为非丢弃模式
      discardingTooLongFrame = false;
      //跳过本次要截取的数据
      buffer.skipBytes(minFrameLength + minDelimLength);
      //设置丢弃的长度为0
      int tooLongFrameLength = this.tooLongFrameLength;
      this.tooLongFrameLength = 0;
      if (!failFast) {
        fail(tooLongFrameLength);
      }
      return null;
    }
  }
}

上面的代码直接将模式设置成非丢弃模式,然后跳过本次要截取的数据。同时将丢弃的长度设置为0。然后如果failFastfalse,就直接抛出异常

第三种情况:非丢弃模式下没有找到对应的分割符

protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
  //省略一部分代码
  else {
    //如果没有找到分割符
    //判断是否处于非丢弃模式
    if (!discardingTooLongFrame) {
      //如果本次可读取的数据长度大于最大可读取长度
      if (buffer.readableBytes() > maxFrameLength) {
        // 丢弃缓冲区的内容,直到找到分隔符为止。
        //丢弃的长度等于本次可读的长度
        tooLongFrameLength = buffer.readableBytes();
        //丢弃
        buffer.skipBytes(buffer.readableBytes());
        //设置丢弃模式为true
        discardingTooLongFrame = true;
        if (failFast) {
          fail(tooLongFrameLength);
        }
      }
    }
    //省略一部分代码
    return null;
  }
}

判断本次可读取的数据长度是否大于最大长度,如果大于最大的长度,直接将丢弃所有的数据,同时将本次丢弃的数据存入tooLongFrameLength变量中,如果failFast变量为true的话,直接抛出异常

第四种情况:丢弃模式下没有找到对应的分割符

protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
  //省略一部分代码
  else {
    //省略一部分代码
     else {
      //仍然丢弃缓冲区,因为没有找到分隔符。
      tooLongFrameLength += buffer.readableBytes();
      buffer.skipBytes(buffer.readableBytes());
    }
    return null;
  }
}

直接记录下丢弃的字节数,同时丢弃所有的字节数。

至此两个解码器就讲完。下篇博客会讲最后一个解码器:基于长度的解码器。

标签:Netty,丢弃,buffer,private,int,源码,长度,LineBasedFrameDecoder,final
来源: https://blog.csdn.net/qq_33762302/article/details/113748960