日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2023/12/4 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 看懂通信协议:自定义通信协议设计之TLV编码应用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??

因?yàn)橹皬氖逻^電信信令類工作,接觸較多的則是ASN.1中的BER、PER編碼,其中BER是基于TLV方式進(jìn)行編碼,本文主要介紹一下TLV在自定義協(xié)議中的應(yīng)用。

通過該文章,你可以肉眼看懂一些類似二進(jìn)制通信協(xié)議,并可以嘗試封裝自己的通信協(xié)議

1. 通信協(xié)議

協(xié)議可以使雙方不需要了解對(duì)方的實(shí)現(xiàn)細(xì)節(jié)的情況下進(jìn)行通信,因此雙方可以是異構(gòu)的,server可以是c++,client可以是java,基于相同的協(xié)議,我們可以用自己熟識(shí)的語(yǔ)言工具來實(shí)現(xiàn)。

協(xié)議一般由一個(gè)或多個(gè)消息組成,簡(jiǎn)單的來說,消息就像是一個(gè)Table,由表頭(消息的字段定義,包括名稱與數(shù)據(jù)類型)與行(字段值)組成。

2. 自定義通信協(xié)議

約定好雙方交換數(shù)據(jù)的編解碼方式,包括一致的基本數(shù)據(jù)類型,業(yè)務(wù)類型,字節(jié)序、消息內(nèi)容等。

3. 編碼方式

可以跟據(jù)業(yè)務(wù)需要進(jìn)行定制,如對(duì)編解碼速度、網(wǎng)絡(luò)帶寬、用戶量等進(jìn)行考量

3.1. 基于字符串編碼

報(bào)頭(4字節(jié)描述數(shù)據(jù)體長(zhǎng)度)+數(shù)據(jù)(字符串+分隔符或直接使用JSON),該方式實(shí)現(xiàn)簡(jiǎn)單,在編解碼階段成本低、但在數(shù)據(jù)類型轉(zhuǎn)時(shí)成本較高,同時(shí)可能會(huì)較占用帶寬。

3.2. 基于二進(jìn)制編碼

將協(xié)議以特定格式編碼為字節(jié)數(shù)組,該種方式相較字符串編碼方式實(shí)現(xiàn)要求要高一些,但帶寬占用相對(duì)小一些,本文主要介紹其中一種較常用的編碼方式TLV,即Tag\Length\Value。

4. TLV編碼介紹( 其中一種實(shí)現(xiàn)介紹 )

TLV:TLV是指由數(shù)據(jù)的類型Tag,數(shù)據(jù)的長(zhǎng)度Length,數(shù)據(jù)的值Value組成的結(jié)構(gòu)體,幾乎可以描任意數(shù)據(jù)類型,TLV的Value也可以是一個(gè)TLV結(jié)構(gòu),正因?yàn)檫@種嵌套的特性,可以讓我們用來包裝協(xié)議的實(shí)現(xiàn)。

以下將分別針對(duì)Tag、Length、Value進(jìn)行解說:

4.1. Tag 描述Value的數(shù)據(jù)類型,TLV嵌套時(shí)可以用于描述消息的類型

Tag由一個(gè)或多個(gè)字節(jié)組成,上圖描述首字節(jié)0~7位的具體含義

1) Tag首節(jié)字說明
  • 第6~7位:表示TLV的類型,00表示TLV描述的是基本數(shù)據(jù)類型(Primitive Frame, int,string,long...),01表示用戶自定義類型(Private Frame,常用于描述協(xié)議中的消息)。
  • 第5位:表示Value的編碼方式,分別支持Primitive及Constructed兩種編碼方式, Primitive指以原始數(shù)據(jù)類型進(jìn)行編碼,Constructed指以TLV方式進(jìn)行編碼,0表示以Primitive方式編碼,1表示以Constructed方式編碼。
  • 第0~4位:當(dāng)Tag Value小于0x1F(31)時(shí),首字節(jié)0~4位用來描述Tag Value,否則0~4位全部置1,作為存在后續(xù)字節(jié)的標(biāo)志,Tag Value將采用后續(xù)字節(jié)進(jìn)行描述。

2) Tag后續(xù)字節(jié)說明

后續(xù)字節(jié)采用每個(gè)字節(jié)的0~6位(即7bit)來存儲(chǔ)Tag Value, 第7位用來標(biāo)識(shí)是否還有后續(xù)字節(jié)。

  • 第7位:描述是否還有后續(xù)字節(jié),1表示有后續(xù)字節(jié),0表示沒有后續(xù)字節(jié),即結(jié)束字節(jié)。
  • 第0~6位:填充Tag Value的對(duì)應(yīng)bit(從低位到高位開始填充),如:Tag Value為:0000001 11111111 11111111 (10進(jìn)制:131071), 填充后實(shí)際字節(jié)內(nèi)容為:10000111 11111111 01111111。

以下提供Tag編碼的JAVA實(shí)現(xiàn)

/*** 生成 Tag ByteArray** @param tagValue Tag 值,即協(xié)議中定義的交易類型 或 基本數(shù)據(jù)類型* @param frameType TLV類型,Tag首字節(jié)最左兩bit為00:基本類型,01:私有類型(自定義類型)* @param dataType 數(shù)據(jù)類型,Tag首字節(jié)第5位為0:基本數(shù)據(jù)類型,1:結(jié)構(gòu)類型(TLV類型,即TLV的V為一個(gè)TLV結(jié)構(gòu))* @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的長(zhǎng)度

描述Value部分所占字節(jié)的個(gè)數(shù),編碼格式分兩類:定長(zhǎng)方式(DefiniteForm)和不定長(zhǎng)方式(IndefiniteForm),其中定長(zhǎng)方式又包括短形式與長(zhǎng)形式。

1) 定長(zhǎng)方式

定長(zhǎng)方式中,按長(zhǎng)度是否超過一個(gè)八位,又分為短、長(zhǎng)兩種形式,編碼方式如下:

  • 短形式: 字節(jié)第7位為0,表示Length使用1個(gè)字節(jié)即可滿足Value類型長(zhǎng)度的描述,范圍在0~127之間的。

  • 長(zhǎng)形式:
    即Value類型的長(zhǎng)度大于127時(shí),Length需要多個(gè)字節(jié)來描述,這時(shí)第一個(gè)字節(jié)的第7位置為1,0~6位用來描述Length值占用的字節(jié)數(shù),然后直將Length值轉(zhuǎn)為byte后附在其后,如: Value大小占234個(gè)字節(jié)(11101010),由于大于127,這時(shí)Length需要使用兩個(gè)字節(jié)來描述,10000001 11101010

以下提供Length定長(zhǎng)方式的JAVA實(shí)現(xiàn)

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// 長(zhǎng)形式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) 不定長(zhǎng)方式

Length所在八位組固定編碼為0x80,但在Value編碼結(jié)束后以兩個(gè)0x00結(jié)尾。這種方式使得可以在編碼沒有完全結(jié)束的情況下,可以先發(fā)送部分?jǐn)?shù)據(jù)給對(duì)方。

4.3. Value 描述數(shù)據(jù)的值

由一個(gè)或多個(gè)值組成 ,值可以是一個(gè)原始數(shù)據(jù)類型(Primitive Data),也可以是一個(gè)TLV結(jié)構(gòu)(Constructed Data)

1) Primitive Data 編碼

2) Constructed Data 編碼

5. TLV編碼應(yīng)用

如果各位看官充分消化了第4點(diǎn)TLV的描述,自然可以很容易將其應(yīng)用到自定義協(xié)議之中,其實(shí)我們只要定制各種TLV自定義類型(Private Frame)與協(xié)議中的消息一一對(duì)應(yīng)更行了

下面將以一個(gè)簡(jiǎn)單的協(xié)議來描述TLV的應(yīng)用,假設(shè)該協(xié)議消息定義如下:

消息名稱設(shè)備故障碼(DEVICE_FAULT_1)Tag值1
公共字段定義
名稱字段Tag值長(zhǎng)度類型
設(shè)備編號(hào)DeviceNo14Integer
設(shè)備版本號(hào)DeviceVersion212String
請(qǐng)求定義
名稱字段Tag值長(zhǎng)度類型
錯(cuò)誤碼FaultCode34Integer
響應(yīng)定義
名稱字段Tag值長(zhǎng)度類型
響應(yīng)碼ResponseCode34Integer
響應(yīng)信息ResponseMsg4-1String

5.1 基本數(shù)據(jù)類型約定

這時(shí)需要對(duì)基本數(shù)據(jù)類型(Primitive Data)進(jìn)行約定,以便通信雙方以一致的方式進(jìn)行數(shù)據(jù)轉(zhuǎn)換,這也作為協(xié)議制定的一部分

基本數(shù)據(jù)類型約定

名稱類型標(biāo)記:Tag長(zhǎng)度:Length值范圍:Value
布爾Boolean10進(jìn)制:1, 2進(jìn)制:0000000111:true .. 0:false
小整型Tiny10進(jìn)制:2, 2進(jìn)制:000000101-127 .. 127
無符號(hào)小整型UTiny10進(jìn)制:3, 2進(jìn)制:0000001110 .. 255
短整型Short10進(jìn)制:4, 2進(jìn)制:000001002-32768 .. 32767
無符號(hào)短整型UShort10進(jìn)制:5, 2進(jìn)制:0000010120 .. 65535
整型Integer10進(jìn)制:6, 2進(jìn)制:000001104-2147483648 .. 2147483648
無符號(hào)整型UInteger10進(jìn)制:7, 2進(jìn)制:0000011140 .. 4294967295
長(zhǎng)整型Long10進(jìn)制:8, 2進(jìn)制:000010008-2^64 .. 2^64
無符號(hào)長(zhǎng)整型ULong10進(jìn)制:9, 2進(jìn)制:0000100180 .. 2^128-1
單精浮點(diǎn)類型Float10進(jìn)制:10, 2進(jìn)制:000010104-2^128 .. 2^128
雙精浮點(diǎn)類型Double10進(jìn)制:11, 2進(jìn)制:000010118-2^1024 .. 2^1024
字符類型Char10進(jìn)制:12, 2進(jìn)制:000011001ASCII
字符串類型String10進(jìn)制:13, 2進(jìn)制:00001101可變由一個(gè)或多個(gè)Char組成
組合類型Complex10進(jìn)制:14, 2進(jìn)制:00001110可變由一個(gè)或多個(gè)基本類型1~9組成,由協(xié)議兩端雙方進(jìn)行約定編解碼
空類型Null10進(jìn)制:15, 2進(jìn)制:000011110

上表需要關(guān)注的是數(shù)據(jù)類型對(duì)應(yīng)的Tag值與Length值

5.2 協(xié)議消息約定

名稱消息標(biāo)記:Tag
設(shè)備故障碼DEVICE_FAULT_11

5.3 示例

通過三層TLV嵌套,完成協(xié)議消息的封包

  • 第一層:與協(xié)義消息對(duì)應(yīng)
  • 第二層:與消息字段對(duì)應(yīng)
  • 第三層:與字段值對(duì)應(yīng),包括其值的類型信息

Tips:每層嵌套都有2個(gè)或以上的字節(jié)增加(Tag和Length),一般通信雙方可以按照協(xié)議對(duì)數(shù)據(jù)類型進(jìn)行推定,所以大家可以根據(jù)實(shí)際需要,決定是否省略第三層的Tag和Length,即可通過配置文件或其它方式讓程序了解字段的類型,從而降低數(shù)據(jù)包的大小,節(jié)省流量。

6 總結(jié)

從上面可以看出,TLV是一種與業(yè)務(wù)無關(guān)的編碼方式,可以較容易用來實(shí)現(xiàn)自定義協(xié)議

轉(zhuǎn)載于:https://my.oschina.net/maxid/blog/206546

總結(jié)

以上是生活随笔為你收集整理的看懂通信协议:自定义通信协议设计之TLV编码应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。