Netty源码分析 (十)----- 拆包器之LineBasedFr…
2019-09-23 09:06:26来源:博客园 阅读 ()
Netty源码分析 (十)----- 拆包器之LineBasedFrameDecoder
Netty 自带多个粘包拆包解码器。今天介绍 LineBasedFrameDecoder,换行符解码器。
行拆包器
下面,以一个具体的例子来看看业netty自带的拆包器是如何来拆包的
这个类叫做 LineBasedFrameDecoder
,基于行分隔符的拆包器,TA可以同时处理 \n
以及\r\n
两种类型的行分隔符,核心方法都在继承的 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); } }
netty 中自带的拆包器都是如上这种模板,我们来看看decode(ctx, in);
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { int eol = findEndOfLine(buffer); int length; int length; if (!this.discarding) { if (eol >= 0) { length = eol - buffer.readerIndex(); int delimLength = buffer.getByte(eol) == '\r' ? 2 : 1; if (length > this.maxLength) { buffer.readerIndex(eol + delimLength); this.fail(ctx, length); return null; } else { ByteBuf frame; if (this.stripDelimiter) { frame = buffer.readRetainedSlice(length); buffer.skipBytes(delimLength); } else { frame = buffer.readRetainedSlice(length + delimLength); } return frame; } } else { length = buffer.readableBytes(); if (length > this.maxLength) { this.discardedBytes = length; buffer.readerIndex(buffer.writerIndex()); this.discarding = true; if (this.failFast) { this.fail(ctx, "over " + this.discardedBytes); } } return null; } } else { if (eol >= 0) { length = this.discardedBytes + eol - buffer.readerIndex(); length = buffer.getByte(eol) == '\r' ? 2 : 1; buffer.readerIndex(eol + length); this.discardedBytes = 0; this.discarding = false; if (!this.failFast) { this.fail(ctx, length); } } else { this.discardedBytes += buffer.readableBytes(); buffer.readerIndex(buffer.writerIndex()); } return null; } } ByteProcessor FIND_LF = new IndexOfProcessor((byte) '\n'); private static int findEndOfLine(ByteBuf buffer) { int i = buffer.forEachByte(ByteProcessor.FIND_LF); if (i > 0 && buffer.getByte(i - 1) == '\r') { --i; } return i; }
找到换行符位置
final int eol = findEndOfLine(buffer); private static int findEndOfLine(final ByteBuf buffer) { int i = buffer.forEachByte(ByteProcessor.FIND_LF); if (i > 0 && buffer.getByte(i - 1) == '\r') { i--; } return i; } ByteProcessor FIND_LF = new IndexOfProcessor((byte) '\n');
for循环遍历,找到第一个 \n
的位置,如果\n
前面的字符为\r
,那就返回\r
的位置
非discarding模式的处理
接下来,netty会判断,当前拆包是否属于丢弃模式,用一个成员变量来标识
private boolean discarding;
第一次拆包不在discarding模式
非discarding模式下找到行分隔符的处理
// 1.计算分隔符和包长度 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;
1.首先,新建一个帧,计算一下当前包的长度和分隔符的长度(因为有两种分隔符)
2.然后判断一下需要拆包的长度是否大于该拆包器允许的最大长度(maxLength
),这个参数在构造函数中被传递进来,如超出允许的最大长度,就将这段数据抛弃,返回null
3.最后,将一个完整的数据包取出,如果构造本解包器的时候指定 stripDelimiter
为false,即解析出来的包包含分隔符,默认为不包含分隔符
非discarding模式下未找到分隔符的处理
没有找到对应的行分隔符,说明字节容器没有足够的数据拼接成一个完整的业务数据包,进入如下流程处理
final int length = buffer.readableBytes(); if (length > maxLength) { discardedBytes = length; buffer.readerIndex(buffer.writerIndex()); discarding = true; if (failFast) { fail(ctx, "over " + discardedBytes); } } return null;
首先取得当前字节容器的可读字节个数,接着,判断一下是否已经超过可允许的最大长度,如果没有超过,直接返回null,字节容器中的数据没有任何改变,否则,就需要进入丢弃模式
使用一个成员变量 discardedBytes
来表示已经丢弃了多少数据,然后将字节容器的读指针移到写指针,意味着丢弃这一部分数据,设置成员变量discarding
为true表示当前处于丢弃模式。如果设置了failFast
,那么直接抛出异常,默认情况下failFast
为false,即安静得丢弃数据
discarding模式
如果解包的时候处在discarding模式,也会有两种情况发生
discarding模式下找到行分隔符
在discarding模式下,如果找到分隔符,那可以将分隔符之前的都丢弃掉
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); }
计算出分隔符的长度之后,直接把分隔符之前的数据全部丢弃,当然丢弃的字符也包括分隔符,经过这么一次丢弃,后面就有可能是正常的数据包,下一次解包的时候就会进入正常的解包流程
discarding模式下未找到行分隔符
这种情况比较简单,因为当前还在丢弃模式,没有找到行分隔符意味着当前一个完整的数据包还没丢弃完,当前读取的数据是丢弃的一部分,所以直接丢弃
discardedBytes += buffer.readableBytes();
buffer.readerIndex(buffer.writerIndex());
特定分隔符拆包
这个类叫做 DelimiterBasedFrameDecoder
,可以传递给TA一个分隔符列表,数据包会按照分隔符列表进行拆分,读者可以完全根据行拆包器的思路去分析这个DelimiterBasedFrameDecoder
原文链接:https://www.cnblogs.com/java-chen-hao/p/11445297.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
下一篇:第二周Java课堂作业
- 你说研究过Spring里面的源码,循环依赖你会么? 2020-06-09
- 这可能是目前最透彻的Netty讲解了... 2020-06-08
- 通俗理解spring源码(六)—— 默认标签(import、alias、be 2020-06-07
- 数据结构:用实例分析ArrayList与LinkedList的读写性能 2020-06-04
- 学习源码的第八个月,我成了Spring的开源贡献者 2020-06-02
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash