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