協(xié)議
- 什么是協(xié)議:協(xié)議是一種約定,通過(guò)約定,不同的進(jìn)程可以對(duì)一段數(shù)據(jù)產(chǎn)生相同的理解,可相互協(xié)作。進(jìn)程間的通信一定需要協(xié)議。
協(xié)議設(shè)計(jì)目標(biāo)
- 解析效率:互聯(lián)網(wǎng)業(yè)務(wù)具有高并發(fā)的特點(diǎn),解析效率決定了使用協(xié)議的CPU成本
- 編碼長(zhǎng)度:信息編碼后的長(zhǎng)度,編碼長(zhǎng)度決定了使用協(xié)議的網(wǎng)絡(luò)帶寬及存儲(chǔ)成本
- 易于實(shí)現(xiàn):互聯(lián)網(wǎng)業(yè)務(wù)需要輕量級(jí)的協(xié)議,而不是大而全
- 可讀性:編碼后的數(shù)據(jù)的可讀性決定了使用協(xié)議的調(diào)試及維護(hù)成本(某些情況下需要可讀性)
- 兼容性:互聯(lián)網(wǎng)的需求具有靈活多變的特點(diǎn),協(xié)議會(huì)經(jīng)常升級(jí),使用協(xié)議的雙方是否可以獨(dú)立升級(jí)協(xié)議、增減協(xié)議中的字段是非常重要的
- 跨平臺(tái)跨語(yǔ)言:互聯(lián)網(wǎng)的業(yè)務(wù)涉及到不同的平臺(tái)和語(yǔ)言
- 安全可靠:防止數(shù)據(jù)被破解
協(xié)議設(shè)計(jì)最核心解決的問(wèn)題
1、序列化/反序列化
2、判斷包的完整性
協(xié)議設(shè)計(jì)進(jìn)階問(wèn)題
3、協(xié)議升級(jí)
4、協(xié)議安全
5、數(shù)據(jù)壓縮
序列化/反序列化
什么是序列化和反序列化?
- 序列化:把對(duì)象轉(zhuǎn)換為字節(jié)序列的過(guò)程稱(chēng)為對(duì)象的序列化
-
反序列化:把字節(jié)序列恢復(fù)為對(duì)象的過(guò)程稱(chēng)為對(duì)象的反序列化
協(xié)議.png
序列化方法
- TLV編碼及其變體(TLV是tag, length和value的縮寫(xiě)):比如protobuf
- 文本流編碼:比如xml json
- 固定結(jié)構(gòu)編碼:協(xié)議約定了傳輸字段類(lèi)型和字段含義,和TLV的方式類(lèi)似,但是沒(méi)有了tag和length,只有value,比如tcp
- 內(nèi)存dump:把內(nèi)存中的數(shù)據(jù)直接輸出,不做任何序列化操作。反序列化的時(shí)候,直接還原內(nèi)存(不建議在互聯(lián)網(wǎng)使用,在嵌入式方面可以使用)
主流序列化協(xié)議
主流序列化協(xié)議:xml、json、protobuf
- xml 指可擴(kuò)展標(biāo)記語(yǔ)言(eXtensible Markup Language)。是一種通用和重量級(jí)的數(shù)據(jù)交換格式,以文本方式存儲(chǔ)
- json (JavaScript Object Notation,JS 對(duì)象簡(jiǎn)譜) 是一種通用和輕量級(jí)的數(shù)據(jù)交換格式,以文本結(jié)構(gòu)進(jìn)行存儲(chǔ)
- protocol buffer是Google的一種獨(dú)立和輕量級(jí)的數(shù)據(jù)交換格式,以二進(jìn)制結(jié)構(gòu)進(jìn)行存儲(chǔ)。
類(lèi)型 | 通用性 | 大小 | 格式 |
---|---|---|---|
xml | 通用 | 重量級(jí) | 文本格式 |
json | 通用 | 輕量級(jí) | 文本格式(方便調(diào)試) |
protobuf(編譯器,生成對(duì)應(yīng)語(yǔ)言的代碼) | 獨(dú)立 | 輕量級(jí) | 二進(jìn)制格式 |
判斷包的完整性
為了能讓對(duì)端知道如何給包分解,目前一般有以下做法:
1、以固定大小字節(jié)數(shù)來(lái)分界,如每個(gè)包100字節(jié),對(duì)端每收齊100個(gè)字節(jié),就當(dāng)成一個(gè)包來(lái)解析
2、以特定符號(hào)來(lái)分界,如每個(gè)包都以特定的字符來(lái)結(jié)尾(如\r\n),當(dāng)在字節(jié)流中國(guó)讀取到該字符時(shí),則表明上一個(gè)包到此為止
3、固定包頭+包體結(jié)構(gòu),這種結(jié)構(gòu)中一般包頭部分是一個(gè)固定字節(jié)長(zhǎng)度的結(jié)構(gòu),并且包頭中會(huì)有一個(gè)特定的字段指定包體的大小。收包時(shí),先接收固定字節(jié)數(shù)的頭部,解出這個(gè)包完整長(zhǎng)度,按此長(zhǎng)度接收包體。這是目前各種網(wǎng)絡(luò)應(yīng)用用的最多的一種包格式
4、在序列化后的buffer前面增加一個(gè)字符流的頭部,其中有個(gè)字段存儲(chǔ)包總長(zhǎng)度,根據(jù)特殊字符(比如\n或者\(yùn)0)判斷頭部的完整性。這樣通常比3要麻煩一些,http和redis采用的就是這種方式。收包的時(shí)候,先判斷已收到的數(shù)據(jù)中是否包含結(jié)束符,收到結(jié)束符后解析包頭,解出這個(gè)包完整長(zhǎng)度,按此長(zhǎng)度接收包體
協(xié)議設(shè)計(jì)參考范例
字段 | 類(lèi)型 | 長(zhǎng)度(字節(jié)) | 說(shuō)明 |
---|---|---|---|
STAG | unsigned short | 2 | 通信協(xié)議數(shù)據(jù)包的開(kāi)始標(biāo)志 |
version | unsigned short | 2 | 通信協(xié)議的版本號(hào) |
checksum | unsigned char | 1 | 計(jì)算協(xié)議數(shù)據(jù)校驗(yàn)和,如果為加密數(shù)據(jù),則計(jì)算密文校驗(yàn)和。校驗(yàn)和計(jì)算范圍:協(xié)議頭CheckSum字段后數(shù)據(jù),協(xié)議體全部數(shù)據(jù)。 |
type | unsigned char | 1 | 0表示協(xié)議體是json格式,其它值未定義。設(shè)備心跳消息類(lèi)型的值為0xA0 |
seqno | unsigned int | 4 | 通信數(shù)據(jù)報(bào)文的序列號(hào),應(yīng)答報(bào)文序列號(hào)必須與請(qǐng)求報(bào)文序列號(hào)相同 |
length | unsigned short | 2 | 報(bào)文內(nèi)容長(zhǎng)度,即從該字段后報(bào)文內(nèi)容長(zhǎng)度 |
reserve | unsigned int | 4 | 預(yù)留字節(jié),設(shè)備心跳消息類(lèi)型的值為devid |
協(xié)議升級(jí)
1、通過(guò)版本號(hào)指明協(xié)議版本,即是通過(guò)版本號(hào)辨別不同類(lèi)型的協(xié)議
2、支持協(xié)議頭部可擴(kuò)展,即是在設(shè)計(jì)協(xié)議頭部的時(shí)候有一個(gè)字段用來(lái)指明頭部的長(zhǎng)度
協(xié)議安全
1、xxtea 固定 key
2、AES 固定 key
3、openssl
4、signal protocol 端到端的通訊加密協(xié)議
數(shù)據(jù)壓縮
1、deflate
2、gzip
3、lzw
2021.8.20 17:28 深圳