看懂通信协议:自定义通信协议设计之TLV编码应用

为什么80%的码农都做不了架构师?>>>   hot3.png

因为之前从事过电信信令类工作,接触较多的则是ASN.1中的BER、PER编码,其中BER是基于TLV方式进行编码,本文主要介绍一下TLV在自定义协议中的应用。

通过该文章,你可以肉眼看懂一些类似二进制通信协议,并可以尝试封装自己的通信协议

1. 通信协议

协议可以使双方不需要了解对方的实现细节的情况下进行通信,因此双方可以是异构的,server可以是c++,client可以是java,基于相同的协议,我们可以用自己熟识的语言工具来实现。

协议一般由一个或多个消息组成,简单的来说,消息就像是一个Table,由表头(消息的字段定义,包括名称与数据类型)与行(字段值)组成。

2. 自定义通信协议

约定好双方交换数据的编解码方式,包括一致的基本数据类型,业务类型,字节序、消息内容等。

3. 编码方式

可以跟据业务需要进行定制,如对编解码速度、网络带宽、用户量等进行考量

3.1. 基于字符串编码

报头(4字节描述数据体长度)+数据(字符串+分隔符或直接使用JSON),该方式实现简单,在编解码阶段成本低、但在数据类型转时成本较高,同时可能会较占用带宽。

3.2. 基于二进制编码

将协议以特定格式编码为字节数组,该种方式相较字符串编码方式实现要求要高一些,但带宽占用相对小一些,本文主要介绍其中一种较常用的编码方式TLV,即Tag\Length\Value。

4. TLV编码介绍( 其中一种实现介绍 )

TLV:TLV是指由数据的类型Tag,数据的长度Length,数据的值Value组成的结构体,几乎可以描任意数据类型,TLV的Value也可以是一个TLV结构,正因为这种嵌套的特性,可以让我们用来包装协议的实现。

以下将分别针对Tag、Length、Value进行解说:

4.1. Tag 描述Value的数据类型,TLV嵌套时可以用于描述消息的类型

Tag由一个或多个字节组成,上图描述首字节0~7位的具体含义

1) Tag首节字说明
  • 第6~7位:表示TLV的类型,00表示TLV描述的是基本数据类型(Primitive Frame, int,string,long...),01表示用户自定义类型(Private Frame,常用于描述协议中的消息)。
  • 第5位:表示Value的编码方式,分别支持Primitive及Constructed两种编码方式, Primitive指以原始数据类型进行编码,Constructed指以TLV方式进行编码,0表示以Primitive方式编码,1表示以Constructed方式编码。
  • 第0~4位:当Tag Value小于0x1F(31)时,首字节0~4位用来描述Tag Value,否则0~4位全部置1,作为存在后续字节的标志,Tag Value将采用后续字节进行描述。

2) Tag后续字节说明

后续字节采用每个字节的0~6位(即7bit)来存储Tag Value, 第7位用来标识是否还有后续字节。

  • 第7位:描述是否还有后续字节,1表示有后续字节,0表示没有后续字节,即结束字节。
  • 第0~6位:填充Tag Value的对应bit(从低位到高位开始填充),如:Tag Value为:0000001 11111111 11111111 (10进制:131071), 填充后实际字节内容为:10000111 11111111 01111111。

以下提供Tag编码的JAVA实现

    /*** 生成 Tag ByteArray** @param tagValue  Tag 值,即协议中定义的交易类型 或 基本数据类型* @param frameType TLV类型,Tag首字节最左两bit为00:基本类型,01:私有类型(自定义类型)* @param dataType  数据类型,Tag首字节第5位为0:基本数据类型,1:结构类型(TLV类型,即TLV的V为一个TLV结构)* @return Tag ByteArray*/public byte[] parseTag(int tagValue, int frameType, int dataType) {int size = 1;rawTag = frameType | dataType | tagValue;if (tagValue < 0x1F) {// 1 byte tagrawTag = frameType | dataType | tagValue;} else {// mutli byte tagrawTag = frameType | dataType | 0x1F;if (tagValue < 0x80) {rawTag <<= 8;rawTag |= tagValue & 0x7F;} else if (tagValue < 0x3FFF) {rawTag <<= 16;rawTag |= (((tagValue & 0x3FFF) >> 7 & 0x7F) | 0x80) << 8;rawTag |= ((tagValue & 0x3FFF) & 0x7F);} else if (tagValue < 0x3FFFF) {rawTag <<= 24;rawTag |= (((tagValue & 0x3FFFF) >> 14 & 0x7F) | 0x80) << 16;rawTag |= (((tagValue & 0x3FFFF) >> 7 & 0x7F) | 0x80) << 8;rawTag |= ((tagValue & 0x3FFFF) & 0x7F);}}return intToByteArray(rawTag);}

4.2. Length 描述Value的长度

描述Value部分所占字节的个数,编码格式分两类:定长方式(DefiniteForm)和不定长方式(IndefiniteForm),其中定长方式又包括短形式与长形式。

1) 定长方式

定长方式中,按长度是否超过一个八位,又分为短、长两种形式,编码方式如下:

  • 短形式: 字节第7位为0,表示Length使用1个字节即可满足Value类型长度的描述,范围在0~127之间的。

  • 长形式:
    即Value类型的长度大于127时,Length需要多个字节来描述,这时第一个字节的第7位置为1,0~6位用来描述Length值占用的字节数,然后直将Length值转为byte后附在其后,如: Value大小占234个字节(11101010),由于大于127,这时Length需要使用两个字节来描述,10000001 11101010

以下提供Length定长方式的JAVA实现

    public byte[] parseLength(int length) {if (length < 0) {throw new IllegalArgumentException();} else// 短形式if (length < 128) {byte[] actual = new byte[1];actual[0] = (byte) length;return actual;} else// 长形式if (length < 256) {byte[] actual = new byte[2];actual[0] = (byte) 0x81;actual[1] = (byte) length;return actual;} else if (length < 65536) {byte[] actual = new byte[3];actual[0] = (byte) 0x82;actual[1] = (byte) (length >> 8);actual[2] = (byte) length;return actual;} else if (length < 16777126) {byte[] actual = new byte[4];actual[0] = (byte) 0x83;actual[1] = (byte) (length >> 16);actual[2] = (byte) (length >> 8);actual[3] = (byte) length;return actual;} else {byte[] actual = new byte[5];actual[0] = (byte) 0x84;actual[1] = (byte) (length >> 24);actual[2] = (byte) (length >> 16);actual[3] = (byte) (length >> 8);actual[4] = (byte) length;return actual;}}
2) 不定长方式

Length所在八位组固定编码为0x80,但在Value编码结束后以两个0x00结尾。这种方式使得可以在编码没有完全结束的情况下,可以先发送部分数据给对方。

4.3. Value 描述数据的值

由一个或多个值组成 ,值可以是一个原始数据类型(Primitive Data),也可以是一个TLV结构(Constructed Data)

1) Primitive Data 编码

2) Constructed Data 编码

5. TLV编码应用

如果各位看官充分消化了第4点TLV的描述,自然可以很容易将其应用到自定义协议之中,其实我们只要定制各种TLV自定义类型(Private Frame)与协议中的消息一一对应更行了

下面将以一个简单的协议来描述TLV的应用,假设该协议消息定义如下:

消息名称设备故障码(DEVICE_FAULT_1)Tag值1
公共字段定义
名称字段Tag值长度类型
设备编号DeviceNo14Integer
设备版本号DeviceVersion212String
请求定义
名称字段Tag值长度类型
错误码FaultCode34Integer
响应定义
名称字段Tag值长度类型
响应码ResponseCode34Integer
响应信息ResponseMsg4-1String

5.1 基本数据类型约定

这时需要对基本数据类型(Primitive Data)进行约定,以便通信双方以一致的方式进行数据转换,这也作为协议制定的一部分

基本数据类型约定

名称类型标记:Tag长度:Length值范围:Value
布尔Boolean10进制:1, 2进制:0000000111:true .. 0:false
小整型Tiny10进制:2, 2进制:000000101-127 .. 127
无符号小整型UTiny10进制:3, 2进制:0000001110 .. 255
短整型Short10进制:4, 2进制:000001002-32768 .. 32767
无符号短整型UShort10进制:5, 2进制:0000010120 .. 65535
整型Integer10进制:6, 2进制:000001104-2147483648 .. 2147483648
无符号整型UInteger10进制:7, 2进制:0000011140 .. 4294967295
长整型Long10进制:8, 2进制:000010008-2^64 .. 2^64
无符号长整型ULong10进制:9, 2进制:0000100180 .. 2^128-1
单精浮点类型Float10进制:10, 2进制:000010104-2^128 .. 2^128
双精浮点类型Double10进制:11, 2进制:000010118-2^1024 .. 2^1024
字符类型Char10进制:12, 2进制:000011001ASCII
字符串类型String10进制:13, 2进制:00001101可变由一个或多个Char组成
组合类型Complex10进制:14, 2进制:00001110可变由一个或多个基本类型1~9组成,由协议两端双方进行约定编解码
空类型Null10进制:15, 2进制:000011110

上表需要关注的是数据类型对应的Tag值与Length值

5.2 协议消息约定

名称消息标记:Tag
设备故障码DEVICE_FAULT_11

5.3 示例

通过三层TLV嵌套,完成协议消息的封包

  • 第一层:与协义消息对应
  • 第二层:与消息字段对应
  • 第三层:与字段值对应,包括其值的类型信息

Tips:每层嵌套都有2个或以上的字节增加(Tag和Length),一般通信双方可以按照协议对数据类型进行推定,所以大家可以根据实际需要,决定是否省略第三层的Tag和Length,即可通过配置文件或其它方式让程序了解字段的类型,从而降低数据包的大小,节省流量。

6 总结

从上面可以看出,TLV是一种与业务无关的编码方式,可以较容易用来实现自定义协议

转载于:https://my.oschina.net/maxid/blog/206546

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/300077.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

统信软件用一年时间跨越式发展说明了一件事

近年来&#xff0c;随着国际局势风云变幻和国内政策利好&#xff0c;国产操作系统迎来了发展机遇期&#xff0c;多家厂商合并重组&#xff0c;或引入投资人实现跨越式发展&#xff0c;在研发人员规模上也从原本的几百人规模快速增加了上千人&#xff0c;其中&#xff0c;统信UO…

C#中as和is关键字

一. as 运算符用于在兼容的引用类型之间执行某些类型的转换。例如&#xff1a; staticvoidMain(string[] args) { object[] obj newobject[3]; obj[0] newclass1(); obj[1] "hello"; obj[2] 10; for(inti 0; i <obj.Length; i) { …

c#小技巧

1.将一个Form显示在另一个Form里面添加一个Panel控件到FormA中&#xff0c;设置FormB的TopLevel属性为false FormB.TopLevel false;FormA.Panel1.Controls.Add(FormB);FormB.Show();2.求一串混合字符的长度&#xff08;中英文相间的&#xff09; stringstr "hello world 时…

SQL Server默认1433端口修改方法

SQL Server默认端口1433端口并不是十分的安全&#xff0c;需要将SQL Server默认端口进行更改&#xff0c;在更改之前&#xff0c;让我们先了解一下什么是1433端口。 什么是1433端口 1433端口&#xff0c;是SQL Server默认端口&#xff0c;SQL Server服务使用两个端口&#xff1…

相亲有风险,且行且珍惜!| 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅

netcore一键nssm发布为windows服务

AntDeploy是我开发一款开源一键部署工具包功能一览&#xff1a;docker容器一键部署docker镜像一键发布支持iis一键部署windows服务一键部署linux服务一键部署支持增量发布支持一键回滚支持点火支持选择特定文件发布支持查看发布记录一个issue前两天有一个使用者给我反馈了一个i…

冒名者还是重名?疑惑中!

虽然已经算是离开存储行业了&#xff0c;但是还是偶尔去存储行业的专业中文网站瞄上几眼&#xff0c;还是很关注存储行业的发展。今天早上无意中打开Dostor([url]http://www.dostor.com[/url])&#xff0c;看到前段时间讨论的关于IBM存储价值的问题又重新归来&#xff0c;如下所…

过了双十一之后的你。。| 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源 阿粪青&#xff0c;侵权删&#xff09;

失业、创业。。。

在这个经济危机的时候&#xff0c;突然遭遇失业。呵呵&#xff0c;&#xff0c;暂且不说失业的原因了。当然这样的事情可能每天多发生。在一个企业里面待的时间长了对自己的发展未必是好事。在目标我待了两年&#xff0c;突然出来以后发现自己多难以适应这外面的世界。就像井底…

Exchange server 2007 出现“0x8004010F”错误的解决办法

环境&#xff1a; 服务器&#xff1a;Exchange server 2007 客户端&#xff1a;outlook2003或outlook2007 在outlook中通过MAPI方式访问Exchange server&#xff0c;在点击"发送接收"后出现0x8004010F的错误。 -------------------------------------------------- 1…

Hello Blazor:(7)WebAssembly、Server模式你选哪种?小孩子才做选择!我用混合模式...

在上次的文章中&#xff0c;我们介绍了“Blazor WebAssembly和Blazor Server的区别”。它们各有优缺点。Blazor WebAssembly的优势在于运行期间可以和服务器无交互&#xff0c;但是需要长时间下载资源&#xff0c;而Blazor Server则首次加载速度非常快&#xff0c;但是必须和服…

一生只有43年,喜欢泡妹打架,却凭借一篇文章震惊世界,跻身一流数学家

全世界只有3.14 %的人关注了青少年数学之旅不会做就别乱套我洛必达的公式!——节选自《数学之旅 闪耀人类的54个数学家》众所周知&#xff0c;求极限是高等数学中最重要的内容之一&#xff0c;而洛必达法则将对原式的求导转化成了导函数形式的问题&#xff0c;这就大大简化了一…

计算机三级网络技术分值占比,大一能考计算机三级吗?计算机三级考试题型分析...

原标题&#xff1a;大一能考计算机三级吗&#xff1f;计算机三级考试题型分析计算机等级考试&#xff0c;很多学生在刚刚进入大学时&#xff0c;就想把计算机三级证书拿到手。那么&#xff0c;大一能考计算机三级吗&#xff1f;计算机三级考试题型有哪些&#xff1f;下面&#…

DTO 和 POCO(或 POJO)有什么区别

DTO 和 POCO&#xff08;或 POJO&#xff09;有什么区别原文链接&#xff1a;https://ardalis.com/dto-or-poco/作者&#xff1a;Ardalis Steve在讨论 .NET 和 C# 中的软件开发时经常出现的两个术语是 DTO 和 POCO。一些开发人员交替使用这些术语。那么&#xff0c;DTO 和 POCO…

Nginx server之Nginx作为反向代理服务器

2019独角兽企业重金招聘Python工程师标准>>> 一&#xff1a;Nginx介绍 1.Nginx简介&#xff1a;nginx [engine x]是一个俄罗斯人编写的HTTP和反向代理服务器&#xff0c;另外它也可以作为邮件代理服务器。 它已经在众多流量很大的俄罗斯网站上使用了很长时间&#x…

为什么现在老师这么难,值得大家深思

致各位老师不知不觉&#xff0c;顶级教师圈已经陪伴大家走过3个月的时光。我们每天为教师们推送教育资讯&#xff0c;得到了许多关心和支持。为了感谢各位教师们的支持和鼓励&#xff0c;顶级教师圈今日特举办限时资源福利活动。我们精心整理了100G海量教学资源&#xff0c;包含…

学计算机要买什么样的电脑,大学开学要买电脑吗?又要买什么样的电脑?看准这些再买也不迟!...

电脑是我们生活中重要的通讯工具&#xff0c;方便了我们学习办公、工作设计、娱乐游戏&#xff0c;我们也越来越依赖电脑处理不同的事情&#xff0c;尤其是现在的开学季&#xff0c;很多准大学生都准备给自己购置一台电脑&#xff0c;但是由于自己不懂&#xff0c;买了怕被坑&a…

如果格局决定人生,那到底什么决定了格局

全世界只有3.14 %的人关注了青少年数学之旅真正决定人与人之间的差距的&#xff0c;其实是我们对事物的见识与内心的格局&#xff0c;见识的深浅决定人生的深浅&#xff0c;格局的大小决定了人生之路是宽是窄。今天给大家推荐几个有深度、有想法的公众号&#xff0c;希望能够给…

海尔微型计算机一键还原怎么操作,教你电脑一键还原怎么操作

当系统使用的时间久了&#xff0c;出现了不好修复的问题&#xff0c;可使用电脑的一键还原来解决。但一些用户不知电脑一键还原怎么操作呢?其实方法并不难&#xff0c;有需要的用户&#xff0c;请来看看下面的步骤吧一键还原又叫做一键恢复&#xff0c;计算机系统出现问题时就…

我从大厂面试中学到的关于 C# 的知识

我从工作面试中学到的关于 C# 的知识原文链接&#xff1a;https://michaelscodingspot.com/what-i-learned-about-c-from-job-interviews/作者&#xff1a;Michael Shpilt我最近参加了一些最大的科技公司的一系列工作面试。在没有透露姓名的情况下&#xff0c;我得到了世界排名…