tag過大怎麼辦
官方wiki:訊息壓縮
protobuf是一種序列化技術,能夠大大減少傳輸無效資料
先看一下傳統的c++通訊格式
#pragma pack(1)
struct player
;#pragma pack()
因為名字有長有短,需要定義乙個字元陣列。也就是這個訊息必然會傳送32+4=36個位元組。實際運用中會有很多的浪費。
而protobuf所採用的資料格式會去掉那些無效的資料,可以很大的壓縮整個訊息流的長度。
同時也更方便的使用變長字串。與json相比,不需要在訊息流中定義變數名字。
採用variant編碼。第乙個位元組標視後續資料的tag以及type。
後續每個位元組的最高位為控制位,剩餘7位儲存資料。如果最高位位1,代表下乙個位元組依舊為本次的資料。
例如傳輸乙個 unsigned int a = 1
這個a變數序列化後的二進位制為兩個位元組 (pomelo-protobuf採用的是小端格式,以小端儲存為例,下同)
第乙個位元組:其中第一位flag,中間4位表示tag(使用者設定,4位表示不下怎麼辦看最後
),最後3位表示type,也就是說最多支援2^3=8種型別
flag
tagtype
0***x
000flag之後的位元組是具體的value,最高位是標誌位,為1的話表示下乙個位元組依舊為本次的資料
flag
value
00000001
如果傳輸的資料大於127,例如200的二進位制表示為11001000。序列後為3個位元組,第乙個位元組同上。第二三個位元組如下:
flag
value
11001000
flag
value
00000001
有符號整型的話。如果是整數,直接*2儲存 負數的話 *2-1儲存。取值的話根據%2的餘數判斷符號
字串的話,需要在flag之後的位元組寫入長度
在專案/config 內有clientprotos.json和serverprotos.json兩個配置檔案,分別對應客戶端的訊息和伺服器的訊息
配置格式:支援required message repeated關鍵字 檢視原始碼得知optional與required關鍵字功能相同
,"repeated path path":2
,"required float speed":3
}}
上文定義了乙個onmove的訊息。裡面包含有3個字段 entityid path speed。最後的數字就是tag(不允許重複)
其中path為自定義型別。也就是message定義的型別。並且是個陣列。
翻譯成c++是這樣的:
struct path
;struct onmove
;
pomelo-protobuf內建資料型別:pomelo-protobuf/lib/constant.js
module.exports =
}
修改pomelo-protobuf/test下的部分**,測試用例如下
定義訊息結構如下:
}
傳送資料如下:
tc[
'pomelo-protobuf-test']=
;
除錯可見被序列化成了671個位元組的二進位製流:
第乙個位元組=10,tag=1就是我們定義的那個,type=2也就是string或者message型別。
解析器本身也會讀取結構定義,所以知道第乙個型別具體是什麼。詳見decoder.decodemsg
flag
tagtype
00001
010第二個位元組=4,代表字串長度為4
flag
value
00000100
第三個位元組=97,字串資料開始,97就是字元『a』 。3-5位元組一次是bcd
flag
value
01100001
第乙個位元組=18: tag=2,type=2
flag
tagtype
000010
010第二個位元組=138,實際資料為10需要聯絡下乙個位元組
flag
value
100001010
第三個位元組=5。聯絡上乙個位元組10,最終長度為5*2^8+10=650位元組
flag
value
00000110
其餘位元組就是字串內容了,直接跳到buffer[659]
第乙個位元組=24: tag=3,type=0
flag
tagtype
000011
000第二個位元組=127,實際資料為127
flag
value
01111111
第乙個位元組=32: tag=4,type=0
flag
tagtype
000100
000第二個位元組=200,flag=1代表後續還有資料,value=72
flag
value
11001000
第三個位元組=1,value=1。結合第二個位元組,這個字段:1*2^8+72=200
flag
value
00000001
第乙個位元組=40: tag=5,type=0
flag
tagtype
000101
000第二個位元組=239,flag=1代表後續還有資料,value=101
flag
value
11101111
第三個位元組=1,value=1。結合第二個位元組,這個字段:1*2^8+101=239。解析器知道該字段為有符號的。
%2=1,所以最終結果為 (239+1)/2 *(-1)=-120**在:encoder.encodesint32
flag
value
00000001
第乙個位元組=48: tag=6,type=0
flag
tagtype
000110
000第二個位元組=2,代表該陣列有2個元素,分別是1和2。陣列實際就是遞迴分析,只是裡面元素不再需要flag位元組,因為陣列型別已經標註過了。
我們可以分析下原始碼寫入tag的地方
function
encodetag
(type, tag)
實際上也就是tag*8+type位元組當作uint32寫入。
讀取就是
function
gethead()
;}encoder.
decodeuint32
=function
(bytes)
}return n;};
function
getbytes
(flag)
while
(b >=
128);if
(!flag)
return bytes;
}
也就是說tag超過4位表示的依舊可以支援。
測試如下。設定第乙個元素tag=50
第乙個位元組=146,因為超過128會繼續讀取第二個位元組3。合併成整數3*128+18=402 注意第一位元組的146要去掉最高位
計算出tag=402/8=50,type=402%8=2
pomelo原始碼解析之模組解析(五)
var sio require sioconnector port,host,opts sio.on connection function siosocket 再看一下siosocket的實現 繼承自eventemitter 發現處理了disconnect,error,message訊息,原封不動...
pomelo原始碼解析之元件Remote
首先提出問題 1.元件remote是什麼?2.remote的作用是什麼?remote模組是遠端通訊模組服務端監聽模組,作用是作為各個模組間通訊物件的存在。變數中儲存.return paths var paths var role master server should not come here ...
JDk原始碼解析之四 Vector原始碼解析
具體的三個屬性 解釋看圖中注釋。vector沒有採取arraylist臨界值擴容的辦法,而是每次不夠的時候,直接根據capacity的值來增加。具體怎麼增加後面會說。vector的構造方法如下。簡單粗暴,如果呼叫無參建構函式,直接就將初始容量設定成了10,最終在右側的構造方法裡,將陣列的長度設定為1...