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

释放双眼,带上耳机,听听看~!

 

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

 

第三节: 行解码器

 

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

同样, 这个解码器也继承了ByteToMessageDecoder

首先看其参数:


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

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

我们看其decode方法:


1
2
3
4
5
6
7
1protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
2    Object decoded = decode(ctx, in);
3    if (decoded != null) {
4        out.add(decoded);
5    }
6}
7

这里的decode方法和我们上一小节分析的decode方法一样, 调用重载的decode方法, 并将解码后的内容放到out集合中

我们跟到重载的decode方法中:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
1protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
2    //找这行的结尾
3    final int eol = findEndOfLine(buffer);
4    if (!discarding) {
5        if (eol >= 0) {
6            final ByteBuf frame;
7            //计算从换行符到可读字节之间的长度
8            final int length = eol - buffer.readerIndex();
9            //拿到分隔符长度, 如果是\r\n结尾, 分隔符长度为2
10            final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
11
12            //如果长度大于最大长度
13            if (length > maxLength) {
14                //指向换行符之后的可读字节(这段数据完全丢弃)
15                buffer.readerIndex(eol + delimLength);
16                //传播异常事件
17                fail(ctx, length);
18                return null;
19            }
20            //如果这次解析的数据是有效的
21            //分隔符是否算在完整数据包里
22            //true为丢弃分隔符
23            if (stripDelimiter) {
24                //截取有效长度
25                frame = buffer.readRetainedSlice(length);
26                //跳过分隔符的字节
27                buffer.skipBytes(delimLength);
28            } else {
29                //包含分隔符
30                frame = buffer.readRetainedSlice(length + delimLength);
31            }
32
33            return frame;
34        } else {
35            //如果没找到分隔符(非丢弃模式)
36            //可读字节长度
37            final int length = buffer.readableBytes();
38            //如果朝超过能解析的最大长度
39            if (length > maxLength) {
40                //将当前长度标记为可丢弃的
41                discardedBytes = length;
42                //直接将读指针移动到写指针
43                buffer.readerIndex(buffer.writerIndex());
44                //标记为丢弃模式
45                discarding = true;
46                //超过最大长度抛出异常
47                if (failFast) {
48                    fail(ctx, "over " + discardedBytes);
49                }
50            }
51            //没有超过, 则直接返回
52            return null;
53        }
54    } else {
55        //丢弃模式
56        if (eol >= 0) {
57            //找到分隔符
58            //当前丢弃的字节(前面已经丢弃的+现在丢弃的位置-写指针)
59            final int length = discardedBytes + eol - buffer.readerIndex();
60            //当前换行符长度为多少
61            final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
62            //读指针直接移到换行符+换行符的长度
63            buffer.readerIndex(eol + delimLength);
64            //当前丢弃的字节为0
65            discardedBytes = 0;
66            //设置为未丢弃模式
67            discarding = false;
68            //丢弃完字节之后触发异常
69            if (!failFast) {
70                fail(ctx, length);
71            }
72        } else {
73            //累计已丢弃的字节个数+当前可读的长度
74            discardedBytes += buffer.readableBytes();
75            //移动
76            buffer.readerIndex(buffer.writerIndex());
77        }
78        return null;
79    }
80}
81

 
final
int eol = findEndOfLine(buffer) 这里是找当前行的结尾的索引值, 也就是\r\n或者是\n:

 

 

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

6-3-1

图中不难看出, 如果是以\n结尾的, 返回的索引值是\n的索引值, 如果是\r\n结尾的, 返回的索引值是\r的索引值

我们看findEndOfLine(buffer)方法:


1
2
3
4
5
6
7
8
9
10
1private static int findEndOfLine(final ByteBuf buffer) {
2    //找到/n这个字节
3    int i = buffer.forEachByte(ByteProcessor.FIND_LF);
4    //如果找到了, 并且前面的字符是-r, 则指向/r字节
5    if (i > 0 && buffer.getByte(i - 1) == '\r') {
6        i--;
7    }
8    return i;
9}
10

这里通过一个forEachByte方法找\n这个字节, 如果找到了, 并且前面是\r, 则返回\r的索引, 否则返回\n的索引

回到重载的decode方法中:

 
if (!discarding) 判断是否为非丢弃模式, 默认是就是非丢弃模式, 所以进入if中

 
if (eol >= 0) 如果找到了换行符, 我们看非丢弃模式下找到换行符的相关逻辑:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1final ByteBuf frame;
2final int length = eol - buffer.readerIndex();
3final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
4if (length > maxLength) {
5    buffer.readerIndex(eol + delimLength);
6    fail(ctx, length);
7    return null;
8}
9if (stripDelimiter) {
10    frame = buffer.readRetainedSlice(length);
11    buffer.skipBytes(delimLength);
12} else {
13    frame = buffer.readRetainedSlice(length + delimLength);
14}
15
16return frame;
17

首先获得换行符到可读字节之间的长度, 然后拿到换行符的长度, 如果是\n结尾, 那么长度为1, 如果是\r结尾, 长度为2

 
if (length > maxLength) 带表如果长度超过最大长度, 则直接通过 
readerIndex(eol + delimLength) 这种方式, 将读指针指向换行符之后的字节, 说明换行符之前的字节需要完全丢弃

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

6-3-2

丢弃之后通过fail方法传播异常, 并返回null

继续往下看, 走到下一步, 说明解析出来的数据长度没有超过最大长度, 说明是有效数据包

 
if (stripDelimiter) 表示是否要将分隔符放在完整数据包里面, 如果是true, 则说明要丢弃分隔符, 然后截取有效长度, 并跳过分隔符长度

将包含分隔符进行截取

以上就是非丢弃模式下找到换行符的相关逻辑

我们再看非丢弃模式下没有找到换行符的相关逻辑, 也就是非丢弃模式下,  
if (eol >= 0) 中的else块:


1
2
3
4
5
6
7
8
9
10
11
1final int length = buffer.readableBytes();
2if (length > maxLength) {
3    discardedBytes = length;
4    buffer.readerIndex(buffer.writerIndex());
5    discarding = true;
6    if (failFast) {
7        fail(ctx, "over " + discardedBytes);
8    }
9}
10return null;
11

首先通过 
final
int length = buffer.readableBytes() 获取所有的可读字节数

然后判断可读字节数是否超过了最大值, 如果超过最大值, 则属性discardedBytes标记为这个长度, 代表这段内容要进行丢弃

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

6-3-3

 
buffer.readerIndex(buffer.writerIndex()) 这里直接将读指针移动到写指针, 并且将discarding设置为true, 就是丢弃模式

如果可读字节没有超过最大长度, 则返回null, 表示什么都没解析出来, 等着下次解析

我们再看丢弃模式的处理逻辑, 也就是 
if (!discarding) 中的else块:

首先这里也分两种情况, 根据 
if (eol >= 0) 判断是否找到了分隔符, 我们首先看找到分隔符的解码逻辑:


1
2
3
4
5
6
7
8
9
1final int length = discardedBytes + eol - buffer.readerIndex();
2final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
3buffer.readerIndex(eol + delimLength);
4discardedBytes = 0;
5discarding = false;
6if (!failFast) {
7    fail(ctx, length);
8}
9

如果找到换行符, 则需要将换行符之前的数据全部丢弃掉

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

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块:


1
2
3
1discardedBytes += buffer.readableBytes();
2buffer.readerIndex(buffer.writerIndex());
3

这里做的事情非常简单, 就是累计丢弃的字节数, 并将读指针移动到写指针, 也就是将数据全部丢弃

 

最后在丢弃模式下, decode方法返回null, 代表本次没有解析出任何数据

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

 

上一节: 固定长度解码器

下一节: 分隔符解码器

给TA打赏
共{{data.count}}人
人已打赏
安全技术

用node.js做cluster,监听异常的邮件提醒服务

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索