【转】netty源码分析之LengthFieldBasedFrameDecoder
原文:https://www.jianshu.com/p/a0a51fd79f62
拆包的原理
关于拆包原理的上一篇博文 netty源码分析之拆包器的奥秘 中已详细阐述,这里简单总结下:netty的拆包过程和自己写手工拆包并没有什么不同,都是将字节累加到一个容器里面,判断当前累加的字节数据是否达到了一个包的大小,达到一个包大小就拆开,进而传递到上层业务解码handler
之所以netty的拆包能做到如此强大,就是因为netty将具体如何拆包抽象出一个decode
方法,不同的拆包器实现不同的decode
方法,就能实现不同协议的拆包
这篇文章中要讲的就是通用拆包器LengthFieldBasedFrameDecoder
,如果你还在自己实现人肉拆包,不妨了解一下这个强大的拆包器,因为几乎所有和长度相关的二进制协议都可以通过TA来实现,下面我们先看看他有哪些用法
LengthFieldBasedFrameDecoder 的用法
1.基于长度的拆包

上面这类数据包协议比较常见的,前面几个字节表示数据包的长度(不包括长度域),后面是具体的数据。拆完之后数据包是一个完整的带有长度域的数据包(之后即可传递到应用层解码器进行解码),创建一个如下方式的LengthFieldBasedFrameDecoder
即可实现这类协议
new LengthFieldBasedFrameDecoder(Integer.MAX, 0, 4);
其中
1.第一个参数是 maxFrameLength
表示的是包的最大长度,超出包的最大长度netty将会做一些特殊处理,后面会讲到
2.第二个参数指的是长度域的偏移量lengthFieldOffset
,在这里是0,表示无偏移
3.第三个参数指的是长度域长度lengthFieldLength
,这里是4,表示长度域的长度为4
2.基于长度的截断拆包
如果我们的应用层解码器不需要使用到长度字段,那么我们希望netty拆完包之后,是这个样子

长度域被截掉,我们只需要指定另外一个参数就可以实现,这个参数叫做 initialBytesToStrip
,表示netty拿到一个完整的数据包之后向业务解码器传递之前,应该跳过多少字节
new LengthFieldBasedFrameDecoder(Integer.MAX, 0, 4, 0, 4);
前面三个参数的含义和上文相同,第四个参数我们后面再讲,而这里的第五个参数就是initialBytesToStrip
,这里为4,表示获取完一个完整的数据包之后,忽略前面的四个字节,应用解码器拿到的就是不带长度域的数据包
3.基于偏移长度的拆包
下面这种方式二进制协议是更为普遍的,前面几个固定字节表示协议头,通常包含一些magicNumber,protocol version 之类的meta信息,紧跟着后面的是一个长度域,表示包体有多少字节的数据

只需要基于第一种情况,调整第二个参数既可以实现
new LengthFieldBasedFrameDecoder(Integer.MAX, 4, 4);
lengthFieldOffset
是4,表示跳过4个字节之后的才是长度域
4.基于可调整长度的拆包
有些时候,二进制协议可能会设计成如下方式

即长度域在前,header在后,这种情况又是如何来调整参数达到我们想要的拆包效果呢?
1.长度域在数据包最前面表示无偏移,lengthFieldOffset
为 0
2.长度域的长度为3,即lengthFieldLength
为3
2.长度域表示的包体的长度略过了header,这里有另外一个参数,叫做 lengthAdjustment
,包体长度调整的大小,长度域的数值表示的长度加上这个修正值表示的就是带header的包,这里是 12+2,header和包体一共占14个字节
最后,代码实现为
new LengthFieldBasedFrameDecoder(Integer.MAX, 0, 3, 2, 0);
5.基于偏移可调整长度的截断拆包
更变态一点的二进制协议带有两个header,比如下面这种

拆完之后,HDR1
丢弃,长度域丢弃,只剩下第二个header和有效包体,这种协议中,一般HDR1
可以表示magicNumber,表示应用只接受以该magicNumber开头的二进制数据,rpc里面用的比较多
我们仍然可以通过设置netty的参数实现
1.长度域偏移为1,那么 lengthFieldOffset
为1
2.长度域长度为2,那么lengthFieldLength
为2
3.长度域表示的包体的长度略过了HDR2,但是拆包的时候HDR2也被netty当作是包体的的一部分来拆,HDR2的长度为1,那么 lengthAdjustment
为1
4.拆完之后,截掉了前面三个字节,那么 initialBytesToStrip
为 3
最后,代码实现为
new LengthFieldBasedFrameDecoder(Integer.MAX, 1, 2, 1, 3);
6.基于偏移可调整变异长度的截断拆包
前面的所有的长度域表示的都是不带header的包体的长度,如果让长度域表示的含义包含整个数据包的长度,比如如下这种情况

其中长度域字段的值为16, 其字段长度为2,HDR1的长度为1,HDR2的长度为1,包体的长度为12,1+1+2+12=16,又该如何设置参数呢?
这里除了长度域表示的含义和上一种情况不一样之外,其他都相同,因为netty并不了解业务情况,你需要告诉netty的是,长度域后面,再跟多少字节就可以形成一个完整的数据包,这里显然是13个字节,而长度域的值为16,因此减掉3才是真是的拆包所需要的长度,lengthAdjustment
为-3
这里的六种情况是netty源码里自带的六中典型的二进制协议,相信已经囊括了90%以上的场景,如果你的协议是基于长度的,那么可以考虑不用字节来实现,而是直接拿来用,或者继承他,做些简单的修改即可
【转】netty源码分析之LengthFieldBasedFrameDecoder的更多相关文章
- netty源码分析系列文章
netty源码分析系列文章 nettynetty源码阅读netty源码分析 想在年终之际将对netty研究的笔记记录下来,先看netty3,然后有时间了再写netty4的,希望对大家有所帮助,这个是 ...
- 【Netty源码分析】发送数据过程
前面两篇博客[Netty源码分析]Netty服务端bind端口过程和[Netty源码分析]客户端connect服务端过程中我们分别介绍了服务端绑定端口和客户端连接到服务端的过程,接下来我们分析一下数据 ...
- 【Netty源码分析】客户端connect服务端过程
上一篇博客[Netty源码分析]Netty服务端bind端口过程 我们介绍了服务端绑定端口的过程,这一篇博客我们介绍一下客户端连接服务端的过程. ChannelFuture future = boos ...
- netty源码分析之揭开reactor线程的面纱(二)
如果你对netty的reactor线程不了解,建议先看下上一篇文章netty源码分析之揭开reactor线程的面纱(一),这里再把reactor中的三个步骤的图贴一下 reactor线程 我们已经了解 ...
- netty源码分析之二:accept请求
我在前面说过了server的启动,差不多可以看到netty nio主要的东西包括了:nioEventLoop,nioMessageUnsafe,channelPipeline,channelHandl ...
- Netty源码分析(前言, 概述及目录)
Netty源码分析(完整版) 前言 前段时间公司准备改造redis的客户端, 原生的客户端是阻塞式链接, 并且链接池初始化的链接数并不高, 高并发场景会有获取不到连接的尴尬, 所以考虑了用netty长 ...
- Netty源码分析第1章(Netty启动流程)---->第1节: 服务端初始化
Netty源码分析第一章: Server启动流程 概述: 本章主要讲解server启动的关键步骤, 读者只需要了解server启动的大概逻辑, 知道关键的步骤在哪个类执行即可, 并不需要了解每一步的 ...
- Netty源码分析第1章(Netty启动流程)---->第2节: NioServerSocketChannel的创建
Netty源码分析第一章: Server启动流程 第二节:NioServerSocketChannel的创建 我们如果熟悉Nio, 则对channel的概念则不会陌生, channel在相当于一个通 ...
- Netty源码分析第1章(Netty启动流程)---->第3节: 服务端channel初始化
Netty源码分析第一章:Netty启动流程 第三节:服务端channel初始化 回顾上一小节的initAndRegister()方法: final ChannelFuture initAndRe ...
随机推荐
- es知识点
版权声明:本文为博主原创文章,未经博主允许不得转载.转载请务必加上原作者:铭毅天下,原文地址:blog.csdn.net/laoyang360 https://blog.csdn.net/wojius ...
- 要提高SQL查询效率where语句条件的先后次序应如何写
我们要做到不但会写SQL,还要做到写出性能优良的SQL语句. (1)选择最有效率的表名顺序(只在基于规则的优化器中有效): Oracle的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句 ...
- System.Web.Optimization对脚本和样式表的操作
这个也是本章重点向描述的部分,首先我们可以使用VS2012RC来新建一个MVC4.0项目,版本可以为4.0或4.5.在Global.asax文件代码中,我们发现已经把过滤器,路由器,以及对样式表和脚本 ...
- 初学Linux(一)关闭操作shutdown halt reboot
1.shutdown –h 10 #这个命令告诉大家,计算机将在10分钟后关机,并且会显示在登陆用户的当前屏幕中. 2.Shutdown –h now #立马关机 3.Shutdown –h 11:1 ...
- Z :彻底了解指针数组,数组指针以及函数指针 [复
原创 :彻底了解指针数组,数组指针以及函数指针 [复制链接] 00 roking 白手起家 帖子 60 主题 16 精华 0 可用积分 74 专家积分 0 在线时间 0 小时 注册时间 2003-10 ...
- java StringBuilder案例
实现输出字符串的长度,容量(容量不够则扩容),及内容 import java.util.Arrays; public class MyStringBuilderDemo { //任务:存储字符串并输出 ...
- JS 事件冒泡、捕获。学习记录
作为一个转行刚到公司的新人,任务不多,这一周任务全部消灭,闲暇的一天也别闲着,悄悄的看起了书.今天写一下JS的事件冒泡.捕获. 也是今天看的内容有点多了,有些消化不了,就随手记录一下.纯属自我理解,如 ...
- 使用 Bulk Copy 将大量数据复制到数据库
如果一次要向数据库服务器提交多条记录 , 通常会执行多次Insert命令 , 这样就为要插入的每个记录执行一次与数据库服务器的往返 , 这就给服务器增加了压力 , 效率也大大的降低了... .Net ...
- 使用js插件进行设备检测
一.分析新浪网是怎么做的 如新浪网有两种版本,一种是pc版,存放在www.sina.com.cn这个服务器上:另外一种是手机版,存放在www.sina.cn这个服务器上 原理是当用户输入网址www ...
- C语言利用 void 类型指针实现面向对象类概念与抽象。
不使用C++时,很多C语言新手可能认为C语言缺乏了面向对象和抽象性,事实上,C语言通过某种组合方式,可以间接性的实现面对对象和抽象. 不过多态和继承这种实现,就有点小麻烦,但是依然可以实现. 核心: ...