摘要:背景現(xiàn)在寫(xiě)客戶端或者網(wǎng)頁(yè)的時(shí)候越來(lái)越多的需要與長(zhǎng)連接打交道尤其是在這個(gè)老板動(dòng)不動(dòng)就要搞一個(gè)聊天系統(tǒng)的時(shí)代后端大哥們于是分分鐘就能造一個(gè)基于或者的消息協(xié)議出來(lái)但是問(wèn)題在于每做一個(gè)新項(xiàng)目后端大哥們就能造出一個(gè)新協(xié)議而且能有各種神奇的限制比如說(shuō)要
背景
現(xiàn)在寫(xiě)客戶端或者網(wǎng)頁(yè)的時(shí)候, 越來(lái)越多的需要與長(zhǎng)連接打交道, 尤其是在這個(gè)老板動(dòng)不動(dòng)就要搞一個(gè)聊天系統(tǒng)的時(shí)代, 后端大哥們于是分分鐘就能造一個(gè)基于TCP或者WebSockets的消息協(xié)議出來(lái). 但是問(wèn)題在于每做一個(gè)新項(xiàng)目, 后端大哥們就能造出一個(gè)新協(xié)議, 而且能有各種神奇的限制. 比如說(shuō)要在長(zhǎng)連接當(dāng)中保持一個(gè)狀態(tài)機(jī), 發(fā)送某條消息后收到的下一條消息一定是XXX, 或者完全一個(gè)JSON就直接丟了出來(lái)等等. 雖然都能用, 但是卻需要在各種地方維護(hù)著不同的底層通信庫(kù), 沒(méi)有章法可依, 所以草擬了這個(gè)協(xié)議.
目前最熱門的消息協(xié)議莫過(guò)于MQTT和gRPC了, 前者被定義為A lightweight messaging protocol for small sensors and mobile devices, optimized for high-latency or unreliable networks, 即一個(gè)為傳感器和移動(dòng)設(shè)備定制的消息協(xié)議. 最大的特點(diǎn)莫過(guò)于其固定消息頭只有2字節(jié), 以及QoS服務(wù)質(zhì)量控制了. 對(duì)于前者, 無(wú)可厚非, 任何一個(gè)長(zhǎng)連接的消息協(xié)議都應(yīng)該可以做到如此, 甚至更簡(jiǎn)單(STMP便是如此), 其次其QoS設(shè)計(jì)使得通信層面就變得很復(fù)雜, 使得其更像一個(gè)消息隊(duì)列協(xié)議, 而不是簡(jiǎn)單的通信協(xié)議. 而gRPC則是一個(gè)基于ProtocolBuffers發(fā)展起來(lái)的RPC協(xié)議以實(shí)現(xiàn). 集成度很高, 底層基于HTTP 2, 所以通用性很好, 如果是做大項(xiàng)目并且團(tuán)隊(duì)有一定的技術(shù)/運(yùn)維積累的話, 是非常推薦的選擇, 但是這和STMP不沖突, STMP面向的是對(duì)協(xié)議健壯性要求不高, 只需要一個(gè)能用的規(guī)范的企業(yè)/團(tuán)隊(duì)中, 你可以用在Web端, 也可以用在客戶端, 或者智能家居等嵌入式設(shè)備中, 反觀gRPC, 則顯得過(guò)于龐雜.
簡(jiǎn)介協(xié)議取名STMP, 意思是最簡(jiǎn)單的消息協(xié)議(The simplest message protocol). 項(xiàng)目托管在GitHub上, 包含了完整的協(xié)議文檔以及相關(guān)實(shí)現(xiàn), 詳細(xì)了解請(qǐng)移步GitHub, 同時(shí)歡迎提交PR/Issue, 地址是https://github.com/acrazing/stmp.
簡(jiǎn)單來(lái)說(shuō), STMP有以下特點(diǎn):
非常精簡(jiǎn)的固定頭部, 僅有一字節(jié)(二進(jìn)制序列化)
支持二進(jìn)制序列化(TCP)以及文本序列化(WebSockets), 文本序列化支持消息分包傳送(傳遞二進(jìn)制數(shù)據(jù))
與IP協(xié)議掩碼類似的上層路由控制
負(fù)載編碼格式對(duì)協(xié)議透明
心跳檢測(cè)
四種消息類型: 心跳, 請(qǐng)求, 通知, 回復(fù)
與HTTP協(xié)議類似的返回狀態(tài)碼控制
消息字段定義一個(gè)全雙工的通信系統(tǒng)中, 雙端需要有效識(shí)別對(duì)方發(fā)來(lái)的消息, 并作出相應(yīng)的處理, 選擇是否回應(yīng)等操作, 所以除了實(shí)際的負(fù)載之外, 還需要若干標(biāo)志字段. STMP中, 完整的消息字段列表如下, 需要注意的是并不是每條消息都會(huì)包含所有的這些字段, 需要根據(jù)網(wǎng)絡(luò)環(huán)境以及消息類型確定應(yīng)該包含的字段列表. 但是如果某條消息包含了以下這些字段中的某一些字段的話,排序順序一定與字段在下面出現(xiàn)的順序相同.
消息類型(KIND): 表示一條消息的類型, 可能的取值有:
0: 心跳消息(Ping Message)
1: 請(qǐng)求消息(Request Message)
2: 通知消息(Notify Message)
3: 回復(fù)消息(Response Message)
消息編碼格式(ENCODING): 表示負(fù)載的編碼格式, 上層應(yīng)用/編解碼層收到消息后, 可以通過(guò)此字段對(duì)負(fù)載進(jìn)行解碼操作, 由于頭部長(zhǎng)度限制, 可能的取值范圍為0-7, 已經(jīng)約定的編碼格式如下:
0: 保留格式, 表示不包含負(fù)載, 此時(shí)消息中一定不存在PS以及PAYLOAD字段
1: Protocol Buffers, 參考 Protocol Buffers
2: JSON, 參考 JSON
3: MessagePack, 參考 MessagePack
4: BSON, 參考 BSON
5: 原始二進(jìn)制數(shù)據(jù)
消息ID(ID): 消息的臨時(shí)ID, 取值范圍為0x0000-0xFFFF, 用于請(qǐng)求與回復(fù)消息當(dāng)中, 請(qǐng)求方應(yīng)該保證在超時(shí)的時(shí)限內(nèi)此ID唯一, 回復(fù)方在回復(fù)時(shí)帶上此ID以供發(fā)送方識(shí)別
消息請(qǐng)求動(dòng)作(ACTION): 請(qǐng)求的動(dòng)作, 用于上層應(yīng)用進(jìn)行路由控制, 取值范圍為0x00000000-0xFFFFFFFF, 即32位整型, 上層應(yīng)用中可以寫(xiě)成xxx.xxx.xxx.xxx的形式, 與IP類似. 接收方在收到相應(yīng)的動(dòng)作后必需能夠正確識(shí)別, 并轉(zhuǎn)交給相應(yīng)的處理器進(jìn)行處理. 其中0x00-0xFF為保留動(dòng)作, 用于協(xié)議內(nèi)部使用. 目前已使用的動(dòng)作有:
0x00: 版本協(xié)商(Check Versions)
狀態(tài)碼(STATUS): 處理結(jié)果狀態(tài)碼, 用在回復(fù)消息中, 表明對(duì)請(qǐng)求的處理結(jié)果, 取值范圍為0x00-0xFF, 其中0x00-0x7F為保留取值, 含義與ACTION無(wú)關(guān), 0x80-0xFF為用戶定義的狀態(tài)值, 含義根據(jù)ACTION不同有可能不同. 目前已定義的狀態(tài)碼有(和HTTP類似, 只不過(guò)換了個(gè)值而已):
0x00: Ok, 200
0x10: MovedPermanently, 301
0x11: Found, 302
0x12: NotModified, 304
0x20: BadRequest, 400
0x21: Unauthorized, 401
0x22: PaymentRequired, 402
0x23: Forbidden, 403
0x24: NotFound, 404
0x25: RequestTimeout, 408
0x26: RequestEntityTooLarge, 413
0x27: TooManyRequests, 429
0x30: InternalServerError, 500
0x31: NotImplemented, 501
0x32: BadGateway, 502
0x33: ServiceUnavailable, 503
0x34: GatewayTimeout, 504
0x35: VersionNotSupported, 505
負(fù)載長(zhǎng)度(PS): 表示PAYLOAD的長(zhǎng)度, 以字節(jié)為單位, 取值范圍為0x00000000-0xFFFFFFFF, 即負(fù)載最大長(zhǎng)度為4Gb, 此字段存在與否由網(wǎng)絡(luò)環(huán)境與ENCODING決定, 如果ENCODING為0, 或者網(wǎng)絡(luò)環(huán)境能夠正確的分包(比如WebSockets環(huán)境), 則一定不存在此字段, 否則一定存在此字段.
負(fù)載(PAYLOAD): 實(shí)際的負(fù)載, 長(zhǎng)度由PS或者網(wǎng)絡(luò)分包結(jié)果確定, 編碼方式由ENCODING決定, 協(xié)議本身不負(fù)責(zé)負(fù)載的編解碼, 需要交由上層的應(yīng)用進(jìn)行解釋.
消息類型如前所述, STMP中消息分類四種類型, 不同的消息類型可能包含的字段及含義有所不同, 詳細(xì)如下:
心跳消息雙端為了保證對(duì)方連接有效性, 必需定期發(fā)送一個(gè)心跳消息給對(duì)方, 此消息一定不包含任何除了KIND外的其它任何字段. 同時(shí)此消息不需要 回復(fù), 如果一方在約定的時(shí)間內(nèi)沒(méi)有收到對(duì)方發(fā)送的心跳消息, 則表明對(duì)方已經(jīng)斷開(kāi)連接或者出現(xiàn)異常, 應(yīng)該立即斷開(kāi)連接.
請(qǐng)求消息此消息表示發(fā)送方請(qǐng)求接收方返回某一個(gè)資源, 如果在指定的時(shí)間內(nèi)未收到接收方的回復(fù), 則放棄等待, 并向上層應(yīng)用返回一個(gè)STATUS為0x25的回復(fù), 表示請(qǐng)求超時(shí).
此消息一定包含KIND, ENCODING, ID, ACTION字段, 可能包含PS, PAYLOAD字段, 一定不包含STATUS字段.
此消息表示發(fā)送方向接收方發(fā)送一個(gè)通知, 接收方無(wú)需回復(fù)此消息.
此消息一定包含KIND, ENCODING, ACTION字段, 可能包含PS, PAYLOAD字段, 一定不包含ID, STATUS字段.
回復(fù)消息此消息表示發(fā)送方向接收方發(fā)送一個(gè)回復(fù)消息以回復(fù)對(duì)方曾經(jīng)發(fā)送的某一條請(qǐng)求消息, 此消息的ID為接收方發(fā)送的此條請(qǐng)求消息的ID. 如果上層應(yīng)用在指定的時(shí)間內(nèi)未返回消息, 則向發(fā)送方發(fā)送一個(gè)STATUS為0x34的回復(fù)消息, 表明上層應(yīng)用處理超時(shí).
此消息一定包含KIND, ENCODING, ID, STATUS字段, 可能包含PS, PAYLOAD字段, 一定不包含ACTION字段.
消息序列化針對(duì)不同的網(wǎng)絡(luò)環(huán)境, 協(xié)議制定了兩套不同的序列化方式以應(yīng)對(duì), 主要原因是瀏覽器環(huán)境中將字符串轉(zhuǎn)換成ArrayBuffer再通過(guò)WebSockets發(fā)送性能實(shí)在無(wú)法直視(實(shí)現(xiàn)方式可以參考stmp/impl/js/stmp/text.ts, 主要是將UTF-16編碼和字符串轉(zhuǎn)換成UTF-8的Uint8Array), 同時(shí)為了更好的Web端調(diào)試, 所以制定了一套文本序列化方案.
二進(jìn)制序列化二進(jìn)制序列化中, 固定頭部占一個(gè)字節(jié), 包含KIND以及ENCODING字段, 如果KIND為0, 則ENOCDING也必需為0, 表示一個(gè)心跳消息. 完整的結(jié)構(gòu)如下:
| 0 ... 7 | 8 ... 15 | 16 ... 23 | 24 ... 31 | | FixedHeader | ID | ACTION | | ACTION | STATUS | | PS | | PAYLOAD ... |
其中的多字節(jié)字段, 包括ID, ACTION, PS字段, 如果存在的話, 一定以BigEndian的方式傳遞. 此外, 固定頭部如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | KIND | ENCODING | 0 | 0 | 0 |
最后三個(gè)位為保留位(未用到), 全部置零.
文本就序列化所有的字段通過(guò)字符|連接, 即:
KIND(1)|ENCODING(1)|ID?(1-5)|ACTION?(1-10)|STATUS?(1-3)|PS?(1-10)|PAYLOAD?(...)
消息分割, 在使用文本序列化方式傳遞二進(jìn)制數(shù)據(jù)時(shí), 瀏覽器環(huán)境不能高效的將二者混雜在一起, 所以允許分成兩個(gè)包進(jìn)行傳送, 前者傳遞頭部信息, 后者傳遞實(shí)際的二進(jìn)制PAYLOAD, 此時(shí)ENCODING一定不為0, 同時(shí), PAYLOAD在頭部包中不存在. WebSockets自身保證了包的有序性.
對(duì)于一個(gè)心跳消息, 只有一個(gè)KIND字段, 所以其結(jié)果一定為"0".
區(qū)分文本消息與二進(jìn)制消息這是比較有趣的地方, 文本消息和二進(jìn)制消息可以通過(guò)首字節(jié)完全區(qū)別開(kāi)來(lái): 對(duì)于文本消息, 首字節(jié)為"0", "1", "2", "3"中的一個(gè), 即0x30-0x33, 而對(duì)于二進(jìn)制消息, 要么為0x00(心跳消息), 要么大于或者等于0x40, 因?yàn)?b>KIND不為0時(shí)其值一定大于0b01000000.
版本協(xié)商協(xié)議版本有兩個(gè)字段, 分別為MAJOR和MINOR, 二者取值范圍均為0到15, 即0x0到0xF, 可以序列化為MAJOR.MINOR的形式.
當(dāng)前協(xié)議版本為0.1.
客戶端在發(fā)起連接成功后, 需要發(fā)送一個(gè)ACTION為0x00的消息給服務(wù)端, 消息ID必需為0, 負(fù)載編碼方式為Raw, 負(fù)載為客戶端可接受的版本號(hào)
列表. 服務(wù)端在收到此消息后, 如果可以處理客戶端發(fā)送過(guò)來(lái)的版本列表中的某一個(gè), 則回復(fù)一個(gè)STATUS為Ok的回復(fù)消息, 負(fù)載為所選擇的協(xié)議版本
號(hào), 如果不能處理, 則返回一個(gè)VersionNotSupported錯(cuò)誤消息, 負(fù)載為空, 并且關(guān)閉連接.
在二進(jìn)制消息中, 一個(gè)版本號(hào)序列化為1字節(jié)長(zhǎng)度的信息, 其中前4位為MAJOR, 后4位為MINOR值. 多個(gè)版本號(hào)直接連接在一起. 在文本消息中, 一個(gè)版本號(hào)序列化為2字節(jié)長(zhǎng)度的信息, 其中前1字節(jié)為MAJOR, 后1字節(jié)為MINOR值, 多個(gè)版本號(hào)直接相連.
實(shí)現(xiàn)目前僅實(shí)現(xiàn)了Golang和JS的簡(jiǎn)單的消息編解碼部分, 地址在: go版本, js版本, 還有很多工作要做T_T, 如果有人提PR就好了?????.
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/84446.html
前言 RxHttp截止本文發(fā)表已經(jīng)推廣了4個(gè)禮拜,目前已經(jīng)有了141個(gè)star,如下: showImg(https://user-gold-cdn.xitu.io/2019/5/20/16ad5f3b6d10d9be); 其中一文,Android 史上最優(yōu)雅的實(shí)現(xiàn)文件上傳、下載及進(jìn)度的監(jiān)聽(tīng)更是得到了大神劉皇叔微信公眾號(hào)的推送,歡迎讀者關(guān)注劉皇叔微信公眾號(hào)「劉望舒」,每天都有精彩的文章推送,真的很棒...
摘要:接下來(lái)繼續(xù)介紹三種架構(gòu)模式,分別是查詢分離模式微服務(wù)模式多級(jí)緩存模式。分布式應(yīng)用程序可以基于實(shí)現(xiàn)諸如數(shù)據(jù)發(fā)布訂閱負(fù)載均衡命名服務(wù)分布式協(xié)調(diào)通知集群管理選舉分布式鎖和分布式隊(duì)列等功能。 SpringCloud 分布式配置 SpringCloud 分布式配置 史上最簡(jiǎn)單的 SpringCloud 教程 | 第九篇: 服務(wù)鏈路追蹤 (Spring Cloud Sleuth) 史上最簡(jiǎn)單的 S...
摘要:內(nèi)容主要有四個(gè)方面趨勢(shì)基礎(chǔ)實(shí)踐調(diào)試。一趨勢(shì)這一章節(jié)主要介紹近幾年和未來(lái)的趨勢(shì),包括兩大瀏覽器和對(duì)的態(tài)度,以及淘寶天貓和阿里云的實(shí)踐情況。完整性是指為了避免網(wǎng)絡(luò)中傳輸?shù)臄?shù)據(jù)被非法篡改,使用算法來(lái)保證消息的完整性。 摘要: 本文邀請(qǐng)阿里云CDN HTTPS技術(shù)專家金九,分享Tengine的一些HTTPS實(shí)踐經(jīng)驗(yàn)。內(nèi)容主要有四個(gè)方面:HTTPS趨勢(shì)、HTTPS基礎(chǔ)、HTTPS實(shí)踐、HTTPS...
摘要:解決問(wèn)題即時(shí)通信要解決三方面的問(wèn)題雙全工通信低延時(shí)支持跨域各種即時(shí)通信技術(shù)輪詢客戶端定時(shí)向服務(wù)器發(fā)送請(qǐng)求,服務(wù)器接到請(qǐng)求后馬上返回響應(yīng)信息并關(guān)閉連接。優(yōu)點(diǎn)實(shí)現(xiàn)真正的即時(shí)通信,而不是偽即時(shí)。 解決問(wèn)題 即時(shí)通信要解決三方面的問(wèn)題: 雙全工通信 低延時(shí) 支持跨域 各種即時(shí)通信技術(shù) 輪詢 客戶端定時(shí)向服務(wù)器發(fā)送Ajax請(qǐng)求,服務(wù)器接到請(qǐng)求后馬上返回響應(yīng)信息并關(guān)閉連接。優(yōu)點(diǎn):后端程序編寫(xiě)比...
閱讀 3490·2021-09-22 16:00
閱讀 3575·2021-09-07 10:26
閱讀 3153·2019-08-30 15:55
閱讀 2923·2019-08-30 13:48
閱讀 1417·2019-08-30 12:58
閱讀 2235·2019-08-30 11:15
閱讀 1033·2019-08-30 11:08
閱讀 622·2019-08-29 18:41