Netty源码分析第6章(解码器)---->第3节: 行解码器

 

Netty源码分析第六章: 解码器html

 

第三节: 行解码器源码分析

 

这一小节了解下行解码器LineBasedFrameDecoder, 行解码器的功能是一个字节流, 以\r\n或者直接以\n结尾进行解码, 也就是以换行符为分隔进行解析spa

一样, 这个解码器也继承了ByteToMessageDecoder指针

首先看其参数:code

//数据包的最大长度, 超过该长度会进行丢弃模式
private final int maxLength; //超出最大长度是否要抛出异常
private final boolean failFast; //最终解析的数据包是否带有换行符
private final boolean stripDelimiter; //为true说明当前解码过程为丢弃模式
private boolean discarding; //丢弃了多少字节
private int discardedBytes;

其中的丢弃模式, 咱们会在源码中看到其中的含义htm

咱们看其decode方法:blog

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, 表明本次没有解析出任何数据

以上就是行解码器的相关逻辑

 

上一节: 固定长度解码器

下一节: 分隔符解码器

相关文章
相关标签/搜索