摘要: 在网络通信开发过程中,很可能需要使用到自定义通信协议,因为之前曾从事过电信信令类工作,接触较多的则是ASN.1中的BER、PER编码,其中BER是基于TLV方式进行编码,本文主要介绍一下TLV编码及其应用。

因为之前从事过电信信令类工作,接触较多的则是[ASN.1](http://zh.wikipedia.org/zh-cn/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结构,正因为这种嵌套的特性,可以让我们用来包装协议的实现。![](https://ahq02g.dm1.livefilestore.com/y2pGVRwLpxg1OC06Gsg7iN_tG0sHAzb83R45u43PaLfZDshVot43rvfzX62n89oZmaKkjhNFoH0DShGWgsBC6qKHqr5WBmY6XpAfjkOvpxyPIw/TLV.png?psid=1)以下将分别针对Tag、Length、Value进行解说: ####4.1. Tag 描述Value的数据类型,TLV嵌套时可以用于描述消息的类型 ![](https://ahq02g.dm2303.livefilestore.com/y2pE8maaJOVi2hTlZv13O7S6LxqLsbTzFf7HCG-J-Rnxhg2UWvmKHMTT2tvFMs3zjJGEb7WIdgQE3d8Wu6HroKynVJG2n1j_yFr4ckHlad1-7w/TLV_DISC.png?psid=1) 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将采用后续字节进行描述。![](https://ahq02g.dm2302.livefilestore.com/y2ps4GDdL09TgISfrKPQk3Y3px1l-EH0YhcDg7tPR5Nme7OAKXRYZDUKqXGL7gYD8nnN8DG2qwNIJDOICKsL3szoZzGYNsT-V4lTpdfX2t1_vY/TAG_FB.png?psid=1)#####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。![](https://ahq02g.dm1.livefilestore.com/y2p0tGpzvc24_EpddfBEZsdEqUBHaRIMFSom4izyJN4ryrf2boD7g4FfqyVtiSqmd5UOc9TuNxHwmsCmkm2JFD8hL-HlOYIcixa6BMgc9_RbgY/TAG_NB.png?psid=1)以下提供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之间的。![](https://ahq02g.dm2303.livefilestore.com/y2pTUflngsP_OT4c4ReAdXLsOjD88bI2HcMc1nKHN6bouH9UDCYdGhKk33-EVJ-66Ms2zFv56R724HjvFb1OwB1_DBt1HxA40dtO6qKAfzTpJI/LENGTH-S.png?psid=1)* 长形式: 即Value类型的长度大于127时,Length需要多个字节来描述,这时第一个字节的第7位置为1,0~6位用来描述Length值占用的字节数,然后直将Length值转为byte后附在其后,如: Value大小占234个字节(11101010),由于大于127,这时Length需要使用两个字节来描述,10000001 11101010![](https://ahq02g.dm2302.livefilestore.com/y2pPaMKjeIKEYAljAyvYv2qXf-zukGgyLXdqTgHVOp3e-J7PyObfa_uLeTJPHa7Ny5gPMEeE-LB-_AnOE1YVIC_gA08rP8vfh17yQuw7ngjow8/LENGTH-L.png?psid=1)以下提供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结尾。这种方式使得可以在编码没有完全结束的情况下,可以先发送部分数据给对方。![](https://ahq02g.dm2301.livefilestore.com/y2p8bAu4O1EEq4cCoORp0uogPl7-CCyC2k31Rdimj1MyNQHVFp47GgO-0oJdsMhshg8zZND53TsNP6lcigss-FvdC8OD_zu4icx49H5NyCzU8w/LENGHT-D.png?psid=1)####4.3. Value 描述数据的值 > 由一个或多个值组成 ,值可以是一个原始数据类型(Primitive Data),也可以是一个TLV结构(Constructed Data)#####1) Primitive Data 编码 ![](https://ahq02g.dm2303.livefilestore.com/y2pr4FIH8dqYIpQvawEBEajtRXuWPFHRb9zS3EeMttlyi_TJjWTIQgg9MQw2v_qVr740-w6kcn_e6RseACqeUlIeYXiTozKo6lT-1HYuv6rdYY/P-DATA.png?psid=1) #####2) Constructed Data 编码 ![](https://ahq02g.dm2304.livefilestore.com/y2pafCW8TjTzhOrF86tdHK7Qrfl_01j4lZFrKYObH_Y1ACBcMmo1dat9Eohp30bJKLuDVxo_Y_nwN1wy93gddHzgVh_SbJcXTQD48At8DE2SQI/C-DATA.png?psid=1)###5. TLV编码应用 > 如果各位看官充分消化了第4点TLV的描述,自然可以很容易将其应用到自定义协议之中,其实我们只要定制各种TLV自定义类型(Private Frame)与协议中的消息一一对应更行了下面将以一个简单的协议来描述TLV的应用,假设该协议消息定义如下:|消息名称|设备故障码(DEVICE_FAULT_1)|Tag值|1|| |: |公共字段定义||||| |名称|字段|Tag值|长度|类型| |设备编号|DeviceNo|1|4|Integer| |设备版本号|DeviceVersion|2|12|String| |请求定义||||| |名称|字段|Tag值|长度|类型| |错误码|FaultCode|3|4|Integer| |响应定义||||| |名称|字段|Tag值|长度|类型| |响应码|ResponseCode|3|4|Integer| |响应信息|ResponseMsg|4|-1|String| ####5.1 基本数据类型约定 > 这时需要对基本数据类型(Primitive Data)进行约定,以便通信双方以一致的方式进行数据转换,这也作为协议制定的一部分基本数据类型约定|名称|类型|标记:Tag|长度:Length|值范围:Value| |:---------------|:-----------------|:-----------------|:-------------|:----------| |布尔|Boolean|10进制:1, 2进制:00000001|1|1:true .. 0:false| |小整型|Tiny|10进制:2, 2进制:00000010|1|-127 .. 127| |无符号小整型|UTiny|10进制:3, 2进制:00000011|1|0 .. 255| |短整型|Short|10进制:4, 2进制:00000100|2|-32768 .. 32767| |无符号短整型|UShort|10进制:5, 2进制:00000101|2|0 .. 65535| |整型|Integer|10进制:6, 2进制:00000110|4|-2147483648 .. 2147483648| |无符号整型|UInteger|10进制:7, 2进制:00000111|4|0 .. 4294967295| |长整型|Long|10进制:8, 2进制:00001000|8|-2^64 .. 2^64| |无符号长整型|ULong|10进制:9, 2进制:00001001|8|0 .. 2^128-1| |单精浮点类型|Float|10进制:10, 2进制:00001010|4|-2^128 .. 2^128| |双精浮点类型|Double|10进制:11, 2进制:00001011|8|-2^1024 .. 2^1024| |字符类型|Char|10进制:12, 2进制:00001100|1|ASCII| |字符串类型|String|10进制:13, 2进制:00001101| 可变| 由一个或多个Char组成| |组合类型|Complex|10进制:14, 2进制:00001110|可变| 由一个或多个基本类型1~9组成,由协议两端双方进行约定编解码| |空类型|Null|10进制:15, 2进制:00001111|0|||> 上表需要关注的是数据类型对应的Tag值与Length值####5.2 协议消息约定|名称|消息|标记:Tag| |: |设备故障码|DEVICE_FAULT_1|1 |####5.3 示例 > 通过三层TLV嵌套,完成协议消息的封包 * 第一层:与协义消息对应 * 第二层:与消息字段对应 * 第三层:与字段值对应,包括其值的类型信息![](https://static.oschina.net/uploads/img/201511/12090646_KY7E.png)> Tips:每层嵌套都有2个或以上的字节增加(Tag和Length),一般通信双方可以按照协议对数据类型进行推定,所以大家可以根据实际需要,决定是否省略第三层的Tag和Length,即可通过配置文件或其它方式让程序了解字段的类型,从而降低数据包的大小,节省流量。###6 总结 > 从上面可以看出,TLV是一种与业务无关的编码方式,可以较容易用来实现自定义协议预告:将来或许会提供一个Go版本的实现

因为之前从事过电信信令类工作,接触较多的则是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值 长度 类型
设备编号 DeviceNo 1 4 Integer
设备版本号 DeviceVersion 2 12 String
请求定义        
名称 字段 Tag值 长度 类型
错误码 FaultCode 3 4 Integer
响应定义        
名称 字段 Tag值 长度 类型
响应码 ResponseCode 3 4 Integer
响应信息 ResponseMsg 4 -1 String

5.1 基本数据类型约定

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

基本数据类型约定

名称 类型 标记:Tag 长度:Length 值范围:Value
布尔 Boolean 10进制:1, 2进制:00000001 1 1:true .. 0:false
小整型 Tiny 10进制:2, 2进制:00000010 1 -127 .. 127
无符号小整型 UTiny 10进制:3, 2进制:00000011 1 0 .. 255
短整型 Short 10进制:4, 2进制:00000100 2 -32768 .. 32767
无符号短整型 UShort 10进制:5, 2进制:00000101 2 0 .. 65535
整型 Integer 10进制:6, 2进制:00000110 4 -2147483648 .. 2147483648
无符号整型 UInteger 10进制:7, 2进制:00000111 4 0 .. 4294967295
长整型 Long 10进制:8, 2进制:00001000 8 -2^64 .. 2^64
无符号长整型 ULong 10进制:9, 2进制:00001001 8 0 .. 2^128-1
单精浮点类型 Float 10进制:10, 2进制:00001010 4 -2^128 .. 2^128
双精浮点类型 Double 10进制:11, 2进制:00001011 8 -2^1024 .. 2^1024
字符类型 Char 10进制:12, 2进制:00001100 1 ASCII
字符串类型 String 10进制:13, 2进制:00001101 可变 由一个或多个Char组成
组合类型 Complex 10进制:14, 2进制:00001110 可变 由一个或多个基本类型1~9组成,由协议两端双方进行约定编解码
空类型 Null 10进制:15, 2进制:00001111 0  

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

5.2 协议消息约定

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

5.3 示例

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

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

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

6 总结

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

预告:将来或许会提供一个Go版本的实现

自定义通信协议设计之TLV编码应用相关推荐

  1. 【转载】看懂通信协议:自定义通信协议设计之TLV编码应用

    0. TLV 相关资料 最近研究了TLV的相关知识点,收集部分资料如下所示: 学习TLV数据结构 通信协议之序列化 看懂通信协议:自定义通信协议设计之TLV编码应用 TLV编解码Java实现 我的开源 ...

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

    为什么80%的码农都做不了架构师?>>>    因为之前从事过电信信令类工作,接触较多的则是ASN.1中的BER.PER编码,其中BER是基于TLV方式进行编码,本文主要介绍一下TL ...

  3. java tlv协议_看懂通信协议:自定义通信协议设计之TLV编码应用

    因为之前从事过电信信令类工作,接触较多的则是ASN.1中的BER.PER编码,其中BER是基于TLV方式进行编码,本文主要介绍一下TLV在自定义协议中的应用. 通过该文章,你可以肉眼看懂一些类似二进制 ...

  4. Socket通用TCP通信协议设计及实现(防止粘包,可移植,可靠)

    Socket通用TCP通信协议设计及实现(防止粘包,可移植,可靠) 引文 我们接收Socket字节流数据一般都会定义一个数据包协议.我们每次开发一个软件的通信模块时,尽管具体的数据内容是不尽相同的,但 ...

  5. TLS协议分析 与 现代加密通信协议设计

    本文目标: 学习鉴赏TLS协议的设计,透彻理解原理和重点细节 跟进一下密码学应用领域的历史和进展 整理现代加密通信协议设计的一般思路 本文有门槛,读者需要对现代密码学有清晰而系统的理解,建议花精力补足 ...

  6. 串口、网口等自定义通信协议的问题

    自定义通信协议的问题 一.串口 1.通信分为网络通信和串口通信 2.协议格式 3.协议设计 4.代码实现 二.网口 1.TCP粘包与拆包 * 包的划分 * 出现TCP粘包的原因 * 粘包与拆包的几种情 ...

  7. 通信协议设计注意事项

    设计自定义通信协议有几个需要注意的情况:   1回执问题    2防堵塞设计   3超时 1回执问题 为了准确获取下发的命令的后续执行情况,就必须设计一种机制反馈命令的执行情况.例如手机短信可以开启短 ...

  8. 《M8围棋谱》自定义皮肤设计指南

    <M8围棋谱>自定义皮肤设计指南 版本:1.0 地址:http://docs.google.com/View?id=dhmvxcsd_3dxpmm6nz 作者:liigo,2009年10月 ...

  9. c语言自定义函数程序设计,ch3自定义函数设计 C语言 《解析C程序设计》.ppt

    ch3自定义函数设计 C语言 <解析C程序设计> 全局变量--外部变量 在函数外定义的变量 有效范围:从定义变量的位置开始到本源文件结束,及有extern声明的其它源文件 存储类型:缺省e ...

最新文章

  1. Survey | 基于图卷积网络的药物发现方法
  2. 暴涨!BTC忠实粉丝转向BCH为BCH网络添砖加瓦
  3. 鸿蒙系统几点更新,鸿蒙系统正式上线,第一批可升级鸿蒙系统手机公布,网友:终于等到了...
  4. 钉钉提示请勿通过开发者调试模式_钉钉开放平台demo调试异常问题解决:hostname in certificate didn't match...
  5. python3 Async/Await入门指南
  6. Synchronized和Lock区别
  7. Python_列表生成式
  8. 在华为推送平台给手机推送步骤
  9. 套接字socket 的地址族和类型、工作原理、创建过程
  10. Http-Only Cookie
  11. 使用avalon2 去构建一个 app-route
  12. 《码出高效 Java开发手册》书籍源码及相关代码示例
  13. imagesc和pcolor区别
  14. 发那科2021参数_FANUC常用参数
  15. vscode下golang build tags
  16. php中的implode,php implode函数 多维数组
  17. 远程办公软件之TeamViewer
  18. 小米Q1业绩:总营收769亿元,手机稳居全球前三
  19. eclipse下maven调试的技巧(Re-run Maven using the -X switch to enable full debug logging.)
  20. SpringCloud Gateway报500 Invalid host: lb://xxx

热门文章

  1. 【2016年第4期】基于仿真大数据的效能评估指标体系构建方法
  2. 创始团队如何保持对公司的控制权?Snapchat这里可能有一份指南
  3. 《Web应用开发》(头歌)
  4. 想创业,就别输不起!--leo看赢在中国第三季(7)-----尊重原版作者牌权。
  5. Xilinx 还是altera
  6. php源文件怎么打开,php文件怎么打开?
  7. Java import 和 import static
  8. XP局域网共享设置图文教程
  9. POJO、PO、BO、DTO、VO的含义以及应用场景
  10. 基于android的计步器文档,基于Android的计步器(Pedometer)的讲解(六)