泰州网站制作报价,站长工具seo综合查询全面解析,建筑工程联系方式公开网,医院做网站的风格为什么80%的码农都做不了架构师#xff1f; 因为之前从事过电信信令类工作#xff0c;接触较多的则是ASN.1中的BER、PER编码#xff0c;其中BER是基于TLV方式进行编码#xff0c;本文主要介绍一下TLV在自定义协议中的应用。 通过该文章#xff0c;你可以肉… 为什么80%的码农都做不了架构师 因为之前从事过电信信令类工作接触较多的则是ASN.1中的BER、PER编码其中BER是基于TLV方式进行编码本文主要介绍一下TLV在自定义协议中的应用。 通过该文章你可以肉眼看懂一些类似二进制通信协议并可以尝试封装自己的通信协议 1. 通信协议 协议可以使双方不需要了解对方的实现细节的情况下进行通信因此双方可以是异构的server可以是cclient可以是java基于相同的协议我们可以用自己熟识的语言工具来实现。 协议一般由一个或多个消息组成简单的来说消息就像是一个Table由表头(消息的字段定义包括名称与数据类型)与行(字段值)组成。 2. 自定义通信协议 约定好双方交换数据的编解码方式包括一致的基本数据类型业务类型字节序、消息内容等。 3. 编码方式 可以跟据业务需要进行定制如对编解码速度、网络带宽、用户量等进行考量 3.1. 基于字符串编码 报头(4字节描述数据体长度)数据(字符串分隔符或直接使用JSON)该方式实现简单在编解码阶段成本低、但在数据类型转时成本较高同时可能会较占用带宽。 3.2. 基于二进制编码 将协议以特定格式编码为字节数组该种方式相较字符串编码方式实现要求要高一些但带宽占用相对小一些本文主要介绍其中一种较常用的编码方式TLV即Tag\Length\Value。 4. TLV编码介绍( 其中一种实现介绍 ) TLVTLV是指由数据的类型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)时首字节04位用来描述Tag Value否则0~4位全部置1作为存在后续字节的标志Tag Value将采用后续字节进行描述。 2) Tag后续字节说明 后续字节采用每个字节的06位即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位置为10~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-1String5.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_115.3 示例 通过三层TLV嵌套完成协议消息的封包 第一层与协义消息对应第二层与消息字段对应第三层与字段值对应包括其值的类型信息 Tips每层嵌套都有2个或以上的字节增加(Tag和Length)一般通信双方可以按照协议对数据类型进行推定所以大家可以根据实际需要决定是否省略第三层的Tag和Length即可通过配置文件或其它方式让程序了解字段的类型从而降低数据包的大小节省流量。 6 总结 从上面可以看出TLV是一种与业务无关的编码方式可以较容易用来实现自定义协议 转载于:https://my.oschina.net/maxid/blog/206546