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
Netty源码分析 (十)----- 拆包器之LineBasedFrameDecoder的更多相关文章
- netty源码分析(十八)Netty底层架构系统总结与应用实践
一个EventLoopGroup当中会包含一个或多个EventLoop. 一个EventLoop在它的整个生命周期当中都只会与唯一一个Thread进行绑定. 所有由EventLoop所处理的各种I/O ...
- Netty源码分析(前言, 概述及目录)
Netty源码分析(完整版) 前言 前段时间公司准备改造redis的客户端, 原生的客户端是阻塞式链接, 并且链接池初始化的链接数并不高, 高并发场景会有获取不到连接的尴尬, 所以考虑了用netty长 ...
- 【转】netty源码分析之LengthFieldBasedFrameDecoder
原文:https://www.jianshu.com/p/a0a51fd79f62 拆包的原理 关于拆包原理的上一篇博文 netty源码分析之拆包器的奥秘 中已详细阐述,这里简单总结下:netty的拆 ...
- Netty源码分析第5章(ByteBuf)---->第10节: SocketChannel读取数据过程
Netty源码分析第五章: ByteBuf 第十节: SocketChannel读取数据过程 我们第三章分析过客户端接入的流程, 这一小节带大家剖析客户端发送数据, Server读取数据的流程: 首先 ...
- Netty源码分析第6章(解码器)---->第3节: 行解码器
Netty源码分析第六章: 解码器 第三节: 行解码器 这一小节了解下行解码器LineBasedFrameDecoder, 行解码器的功能是一个字节流, 以\r\n或者直接以\n结尾进行解码, 也就是 ...
- Netty源码分析第6章(解码器)---->第4节: 分隔符解码器
Netty源码分析第六章: 解码器 第四节: 分隔符解码器 基于分隔符解码器DelimiterBasedFrameDecoder, 是按照指定分隔符进行解码的解码器, 通过分隔符, 可以将二进制流拆分 ...
- C# DateTime的11种构造函数 [Abp 源码分析]十五、自动审计记录 .Net 登陆的时候添加验证码 使用Topshelf开发Windows服务、记录日志 日常杂记——C#验证码 c#_生成图片式验证码 C# 利用SharpZipLib生成压缩包 Sql2012如何将远程服务器数据库及表、表结构、表数据导入本地数据库
C# DateTime的11种构造函数 别的也不多说没直接贴代码 using System; using System.Collections.Generic; using System.Glob ...
- 【Netty源码分析】发送数据过程
前面两篇博客[Netty源码分析]Netty服务端bind端口过程和[Netty源码分析]客户端connect服务端过程中我们分别介绍了服务端绑定端口和客户端连接到服务端的过程,接下来我们分析一下数据 ...
- 【Netty源码分析】客户端connect服务端过程
上一篇博客[Netty源码分析]Netty服务端bind端口过程 我们介绍了服务端绑定端口的过程,这一篇博客我们介绍一下客户端连接服务端的过程. ChannelFuture future = boos ...
- netty源码分析之揭开reactor线程的面纱(二)
如果你对netty的reactor线程不了解,建议先看下上一篇文章netty源码分析之揭开reactor线程的面纱(一),这里再把reactor中的三个步骤的图贴一下 reactor线程 我们已经了解 ...
随机推荐
- 《机器学习基石》---Linear Models for Classification
1 用回归来做分类 到目前为止,我们学习了线性分类,线性回归,逻辑回归这三种模型.以下是它们的pointwise损失函数对比(为了更容易对比,都把它们写作s和y的函数,s是wTx,表示线性打分的分数) ...
- Sqlserver 游标的写法记录
---游标更新删除当前数据 ---1.声明游标 declare orderNum_03_cursor cursor scroll for select OrderId ,userId from big ...
- Discuz! ML远程代码执行(CVE-2019-13956)
Discuz! ML远程代码执行(CVE-2019-13956) 一.漏洞描述 该漏洞存在discuz ml(多国语言版)中,cookie中的language可控并且没有严格过滤,导致可以远程代码执行 ...
- C# 中的数据库操作~存储过程篇Mysql SqlServer
Mysql 存储过程查询方式 SQL server 普通数据库操作 EF 调用SQL SERVER存储过程 Mysql 存储过程查询方式: public NetPort GetNetdevicePor ...
- nanopi NEO2 学习笔记 2:安装 pip 和 pip 安装第三方模块
我现在越来越喜欢用python做开发了,特别是知道了python还能用rpi.gpio库操作 NEO2 的 io 口之后,更是激动 在进行一下的操作之前,要先更换国内的 apt arm64 源,并更新 ...
- 关于line-height 行高的一些理解和技巧
大家都知道,如何设置文字垂直居中,也就是:设置line-height 和 外围盒子的高度height一致: 其实这里有个地方,是多余的,也就是height,设不设置都居中: 那么,行高是生产高度的? ...
- python爬取豆瓣首页热门栏目详细流程
记录一下爬取豆瓣热门专栏的经过,通过这篇文章,你能学会requests,HTMLParser,json的基本使用,以及爬取网页内容的基本思路. 使用模块 1,获取豆瓣首页代码:首先我们需要访问豆瓣页面 ...
- MSIL实用指南-比较运算
数值的比较就是大于.小于.等于.大于等于.小于等于.不等于,它们的运算结果都是布尔值.大于.小于.等于有直接对应的指令,分别是Cgt.Clt.Ceq.大于等于.小于等于.不等于没有直接对应的指令,它的 ...
- ThreadLocal可以解决并发问题吗?
前言 到底什么是线程的不安全?为什么会存在线程的不安全?线程的不安全其实就是多个线程并发的去操作同一共享变量没用做同步所产生意料之外的结果.那是如何体现出来的呢?我们看下面的一个非常经典的例子:两个操 ...
- OpenCvSharp 通过特征点匹配图片
现在的手游基本都是重复操作,一个动作要等好久,结束之后继续另一个动作.很麻烦,所以动起了自己写一个游戏辅助的心思. 这个辅助本身没什么难度,就是通过不断的截图,然后从这个截图中找出预先截好的能代表相应 ...