通信协议格式
pomelo核心提供了兩種connector,sioconnector和hybridconnector。其中sioconnector基于socket.io,使用json作為其通信格式,hybridconnector則用于tcp/websocket的通信,它底層使用的是二進(jìn)制協(xié)議。雖然在sioconnector中,socket.io的實(shí)現(xiàn)很好,對(duì)于超時(shí)、握手等都做了處理,并且使用json作為通信格式,方便了協(xié)議的定制和修改,但同時(shí)也帶來了較多的通訊冗余數(shù)據(jù)。hybridconnector則是使用了二進(jìn)制版本通訊協(xié)議,同時(shí)提供了route字典壓縮和protobuf壓縮,提高帶寬利用率,以滿足諸如移動(dòng)環(huán)境的需求,同時(shí)上層接口仍保持json格式的接口,對(duì)以前版本之前的代碼不產(chǎn)生任何影響,保留兼容性。在本部分,主要介紹hybridconnector實(shí)現(xiàn)的具體的通信協(xié)議。
pomelo的二進(jìn)制協(xié)議包含兩層編碼:package和message。message層主要實(shí)現(xiàn)route壓縮和protobuf壓縮,message層的編碼結(jié)果將傳遞給package層。package層主要實(shí)現(xiàn)pomelo應(yīng)用基于二進(jìn)制協(xié)議的握手過程,心跳和數(shù)據(jù)傳輸編碼,package層的編碼結(jié)果可以通過tcp,websocket等協(xié)議以二進(jìn)制數(shù)據(jù)的形式進(jìn)行傳輸。message層編碼可選,也可替換成其他二進(jìn)制編碼格式,都不影響package層編碼和發(fā)送。
Pomelo協(xié)議層的結(jié)構(gòu)如下圖所示:
pomelo package
package協(xié)議主要用來封裝在面向連接的二進(jìn)制流的通訊協(xié)議(如:tcp)上的pomelo數(shù)據(jù)包。package分為控制包和數(shù)據(jù)包兩種類型。前者用來實(shí)現(xiàn)pomelo應(yīng)用層面的控制流程,包括客戶端和服務(wù)器的握手,心跳和服務(wù)器主動(dòng)斷開連接的通知等控制信息。后者則是用來在客戶端和服務(wù)器之間傳輸應(yīng)用數(shù)據(jù)。
package格式
package分為header和body兩部分。header描述package包的類型和包的長(zhǎng)度,body則是需要傳輸?shù)臄?shù)據(jù)內(nèi)容。具體格式如下:
- type - package類型,1個(gè)byte,取值如下。
- 0x01: 客戶端到服務(wù)器的握手請(qǐng)求以及服務(wù)器到客戶端的握手響應(yīng)
- 0x02: 客戶端到服務(wù)器的握手ack
- 0x03: 心跳包
- 0x04: 數(shù)據(jù)包
- 0x05: 服務(wù)器主動(dòng)斷開連接通知
- length - body內(nèi)容長(zhǎng)度,3個(gè)byte的大端整數(shù),因此最大的包長(zhǎng)度為2^24個(gè)byte。
- body - 二進(jìn)制的傳輸內(nèi)容。
各個(gè)package類型的具體描述和控制流程如下。
握手
握手流程主要提供一個(gè)機(jī)會(huì),讓客戶端和服務(wù)器在連接建立后,進(jìn)行一些初始化的數(shù)據(jù)交換。交換的數(shù)據(jù)分為系統(tǒng)和用戶兩部分。系統(tǒng)部分為pomelo框架所需信息,用戶部分則是用戶可以在具體應(yīng)用中自定義的內(nèi)容。
握手的內(nèi)容為utf-8編碼的json字符串(不壓縮),通過body字段傳輸。
握手請(qǐng)求:
{"sys": {"version": "1.1.1","type": "js-websocket"}, "user": {// any customized request data} }- sys.version - 客戶端的版本號(hào)。每個(gè)客戶端SDK的每一個(gè)版本都有一個(gè)固定的版本號(hào)。在握手階段客戶端將該版本號(hào)上傳給服務(wù)器,服務(wù)器可以由此來判斷當(dāng)前客戶端是否合適與服務(wù)器通訊。
- sys.type - 客戶端的類型。可以通過客戶端類型和版本號(hào)一起來確定客戶端是否合適。
握手響應(yīng):
{"code": 200, // response code"sys": {"heartbeat": 3, // heartbeat interval in second"dict": {}, // route dictionary"protos": {} // protobuf definition data}, "user": {// any customized response data} }- code - 握手響應(yīng)的狀態(tài)碼。目前的取值:200代表成功,500為處理用戶自定義握手流程時(shí)失敗,501為客戶端版本號(hào)不符合要求。
- sys.heartbeat - 可選,心跳時(shí)間間隔,單位為秒,沒指定表示不需要心跳。
- dict - 可選,route字段壓縮的映射表,沒指定表示沒有字典壓縮。
- protos - 可選,protobuf壓縮的數(shù)據(jù)定義,沒有表示沒有protobuf壓縮。
- user - 可選,用戶自定義的握手?jǐn)?shù)據(jù),沒有表示沒有用戶自定義的握手?jǐn)?shù)據(jù)。
握手的流程如下:
當(dāng)?shù)讓舆B接建立后,客戶端向服務(wù)器發(fā)起握手請(qǐng)求,并附帶必要的數(shù)據(jù)。服務(wù)器檢驗(yàn)握手?jǐn)?shù)據(jù)后,返回握手響應(yīng)。如果握手成功,客戶端向服務(wù)器發(fā)送一個(gè)握手ack,握手階段至此成功結(jié)束。
心跳
心跳包的length字段為0,body為空。
心跳的流程如下:
服務(wù)器可以配置心跳時(shí)間間隔。當(dāng)握手結(jié)束后,客戶端發(fā)起第一個(gè)心跳。服務(wù)器和客戶端收到心跳包后,延遲心跳間隔的時(shí)間后再向?qū)Ψ桨l(fā)送一個(gè)心跳包。
心跳超時(shí)時(shí)間為2倍的心跳間隔時(shí)間。服務(wù)器檢測(cè)到心跳超時(shí)并不會(huì)主動(dòng)斷開客戶端的連接。客戶端檢測(cè)到心跳超時(shí),可以根據(jù)策略選擇是否要主動(dòng)斷開連接。
數(shù)據(jù)
數(shù)據(jù)包用來在客戶端和服務(wù)器之間傳輸數(shù)據(jù)所用。數(shù)據(jù)包的body是由上層傳下來的任意二進(jìn)制數(shù)據(jù),package層不會(huì)對(duì)body內(nèi)容做任何處理。
服務(wù)器主動(dòng)斷開
當(dāng)服務(wù)器主動(dòng)斷開客戶端連接時(shí)(如:踢掉某個(gè)在線玩家),會(huì)先向客戶端發(fā)送一個(gè)控制消息,然后再斷開連接。客戶端可以通過這個(gè)消息來判斷是否是服務(wù)器主動(dòng)斷開連接的。
pomelo message
message協(xié)議的主要作用是封裝消息頭,包括route和消息類型兩部分,不同的消息類型有著不同的消息頭,在消息頭里面可能要打入message id(即requestId)和route信息。由于可能會(huì)有route壓縮,而且對(duì)于服務(wù)端push的消息,message id為空,對(duì)于客戶端請(qǐng)求的響應(yīng),route為空,因此message的頭格式比較復(fù)雜。
消息頭分為三部分,flag,message id,route。如下圖所示:
從上圖可以看出,pomelo消息頭是可變的,會(huì)根據(jù)具體的消息類型和內(nèi)容而改變。其中:
- flag位是必須的,占用一個(gè)byte,它決定了后面的消息類型和內(nèi)容的格式;
- message id和route則是可選的。其中message id采用varints 128變長(zhǎng)編碼方式,根據(jù)值的大小,長(zhǎng)度在0~5byte之間。route則根據(jù)消息類型以及內(nèi)容的大小,長(zhǎng)度在0~255byte之間。
標(biāo)志位flag
flag占用message頭的第一個(gè)byte,其內(nèi)容如下
現(xiàn)在只用到了其中的4個(gè)bit,這四個(gè)bit包括兩部分,占用3個(gè)bit的message type字段和占用1個(gè)bit的route標(biāo)識(shí),其中:
- message type用來標(biāo)識(shí)消息類型,范圍為0~7,現(xiàn)在消息共有四類,request,notify,response,push,值的范圍是0~3。不同的消息類型有著不同的消息內(nèi)容,下面會(huì)有詳細(xì)分析。
- 最后一位的route表示route是否壓縮,影響route字段的長(zhǎng)度。
這兩部分之間相互獨(dú)立,互不影響。
消息類型
不同類型的消息,對(duì)應(yīng)不同消息頭,消息類型通過flag字段的第2-4位來確定,其對(duì)應(yīng)關(guān)系以及相應(yīng)的消息頭如下圖:
上面的 - 表示不影響消息類型的bit位。
route壓縮標(biāo)志位
route主要分為壓縮和未壓縮兩種,由flag的最后一位(route壓縮標(biāo)志位)指定,當(dāng)flag中的route標(biāo)志為0時(shí),表示未壓縮的route,為1則表示是壓縮route。route通過系統(tǒng)生成和用戶自定義的字典進(jìn)行壓縮,具體內(nèi)容見pomelo壓縮協(xié)議。route字段的編碼會(huì)依賴flag的這一位,其格式如下圖:
上圖是不同的flag標(biāo)志對(duì)應(yīng)的route字段的內(nèi)容:
- flag的最后一位為1時(shí),后面跟的是一個(gè)uInt16表示的route字典編號(hào),需要通過查詢字典來獲取route;
- flag最后一位為0是,后面route則由一個(gè)uInt8的byte,用來表示route的字節(jié)長(zhǎng)度。之后是通過utf8編碼后的route字符串,其長(zhǎng)度就是前面一位byte的uInt8的值,因此route的長(zhǎng)度最大支持256B。
總結(jié)
在本部分,介紹了pomelo提供的hybridconnector的線上協(xié)議,包括package層和message層。當(dāng)用戶使用hybridconnector的時(shí)候,可以根據(jù)這里提供的協(xié)議信息,在客戶端可以依據(jù)此協(xié)議完成與服務(wù)端的通信。
總結(jié)
- 上一篇: TCP协议及帧格式
- 下一篇: Ireport 子报表分页