【转】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 ...
随机推荐
- git工具的使用
Git工具的出现降低了软件版本维护的成本,极大的提高了工作效率,在这里列出了一些使用方法,方便以后查看. 1.Git的初始化->创建一个Git仓库:git init 2.创建信息:git con ...
- 2017年秋季个人阅读计划 ---《掌握需求过程》第二版 pdf
这学期我们学习是软件需求分析,为了扩展视野,我们老师要求精读一本书,我根据老师推荐的书籍中找到了一本,名字叫做<掌握需求过程>,我大概浏览了一下这本书,这本书论述了软件开发中的重要课题—如 ...
- Angular-学习。
今天刚学了点关于Angular的知识,就迫不及待的想跟大家来分享. 1.angular.extend ( )方法可以把一个或多个对象中的方法和属性扩展到一个目的对象中. <script typ ...
- Elasticsearch安装记录
一 安装部分 1.新建用户 elasticsearch不能使用root身份执行 adduser esuser passwd esuser 2.赋予权限 切换到root chown -R esuser ...
- [翻译] PNChart
PNChart https://github.com/kevinzhow/PNChart You can also find swift version at here https://github. ...
- 设计多选按钮ListChooseView
设计多选按钮ListChooseView 答应某位女屌丝而写的控件,效果还不错,开源给大家^_^! 效果图: 源码: // // ListChooseView.h // ScrollChooseBut ...
- spring-springmvc-hibernate项目小结
1. web.xml中别忘记加入spring监听器 <listener> <listener-class>org.springframework.web.context.Con ...
- 各个系统Docker安装
Ubuntu 1.Ubuntu 14.04及以上版本 Ubuntu 14.04版本官方软件源已经自带了Docker包,可以直接安装: $ sudo apt-get update $ sudo apt- ...
- Undefined function or method 'deploywhich' for input arguments of type 'char'
在进行matlab和java混合编程的时候.由matlab打包,把m文件转换为jar文件.供java调用.有时在Tomcat中调用此类jar类会出现如题或者以下的错误: ??? Error using ...
- 1925: [Sdoi2010]地精部落
1925: [Sdoi2010]地精部落 Time Limit: 10 Sec Memory Limit: 64 MB Submit: 1929 Solved: 1227 [Submit][Statu ...