游戲開發(fā)-協(xié)議-protobuf原理詳解

如果你有看過上一篇《游戲開發(fā)-協(xié)議設(shè)計(jì)-protobuf》诱贿,就會(huì)了解到prtobuf的what和how,那么這一篇主要分析一下why的問題,protobuf為何解析速度快茴厉,占用空間小旅掂,以及兼容性好赏胚,它是如何做到的,我們將從 占用空間商虐,解析速度觉阅,兼容三個(gè)問題著手進(jìn)行分析崖疤。

上一篇發(fā)出后也有同學(xué)留言,說直接二進(jìn)制read和write比protobuf會(huì)節(jié)省空間典勇,某種程度上他說的對(duì)劫哼,但是需要在極端條件下才成立,一般情況下protobuf 還是比二進(jìn)制序列化節(jié)省空間的割笙,具體為何权烧,以下會(huì)詳細(xì)介紹。

占用空間

一條消息數(shù)據(jù)伤溉,用protobuf序列化后的大小是json的10分之一般码,xml格式的20分之一,是二進(jìn)制序列化的10分之一(極端情況下乱顾,會(huì)大于等于直接序列化)侈询,總體看來ProtoBuf的優(yōu)勢(shì)還是很明顯的。那么protobuf是如何做到的糯耍,我們主要從以下4個(gè)方面進(jìn)行分析扔字。

1、數(shù)據(jù)緊湊

相對(duì)于json或者xml温技,protobuf沒有定義標(biāo)簽革为,直接生成的二進(jìn)制,令消息非常緊湊舵鳞,這個(gè)和我們直接定義消息震檩,然后順序解析二進(jìn)制數(shù)據(jù)相似。期間沒有多余的數(shù)據(jù)蜓堕。

一個(gè)message的信息結(jié)構(gòu)如下:

每個(gè)field由一個(gè)tag和value組成抛虏,每個(gè)field之間在字節(jié)流中緊密相連,這意味著消息的信息沒用冗余套才,保持最緊湊的樣子迂猴。

2、剔除無效字段

剔除無值字段一般json和xml這種標(biāo)簽式結(jié)構(gòu)數(shù)據(jù)在序列化的時(shí)候也會(huì)處理背伴,同樣protobuf也做了處理沸毁。我們先看一下一個(gè)常規(guī)定義的的二進(jìn)制信息:


此種常規(guī)定義,可以對(duì)數(shù)據(jù)順序?qū)懭肷导牛缓笤夙樞蜃x取息尺,也支持?jǐn)?shù)據(jù)的序列化。但會(huì)帶來一個(gè)問題疾掰,某些字段沒有賦值的情況下搂誉,不得不傳一個(gè)默認(rèn)值。比如field3的值是一個(gè)int静檬,占位4個(gè)字節(jié)炭懊。如果client的field2沒有賦值浪汪,而不寫入一個(gè)默認(rèn)值(比如0),那么server解包就會(huì)偏移量就會(huì)出錯(cuò)凛虽,最終整個(gè)包的數(shù)據(jù)讀不出死遭。

protobuf 是如何解決這個(gè)問題,因?yàn)樗肓藅ag凯旋。

我們看下tag的組成呀潭。

tag是由:fieldNumber和wireType組成,fieldNumber定義字段的標(biāo)識(shí)位至非,以此來處理寫入和讀取順序钠署,wireType定義字段類型,以此來定義單個(gè)field的占用字節(jié)大小荒椭。

wireType 可以支持的類型如下:

我們看下每個(gè)的field解析方式:

讀取field的時(shí)候谐鼎,先讀取tag,然后基于tag知道value的數(shù)據(jù)類型趣惠,獲取value狸棍。write也一樣,單個(gè)field的寫入也是先寫入tag再寫入value味悄。

因?yàn)槊總€(gè)field都定義了tag草戈,如若field沒有賦值,編碼的時(shí)候它的tag不會(huì)被寫入流中侍瑟,相應(yīng)也不會(huì)有它的value,如此解析期間因?yàn)闆]有此字段的tag唐片,可以直接無視,讀取其他field涨颜。如此去除無效字段之后费韭,可以有效的節(jié)省空間。

比如上述的常規(guī)定義的的二進(jìn)制信息,在field2沒有賦值的情況下庭瑰,protobuf可以如此處理星持。

3、Varints &?Zigzag

Varints

我們?cè)谏厦娴膚ireType中也看到有一種Varint類型的field定義见擦,這是要說第三個(gè)特點(diǎn)钉汗。

Varint 是一種緊湊的表示數(shù)字的方法羹令。它用一個(gè)或多個(gè)字節(jié)來表示一個(gè)數(shù)字鲤屡,值越小的數(shù)字使用越少的字節(jié)數(shù)。這能減少用來表示數(shù)字的字節(jié)數(shù)福侈。我們看下它的算法:

我們知道酒来,?int32數(shù)據(jù)類型?,一般需要4 個(gè) 字節(jié)?來表示肪凛,采用 Varint編碼之后堰汉,對(duì)于很小的 int32 類型的數(shù)字辽社,比如小于127的,則可以用 1 個(gè) 字節(jié)?來存儲(chǔ)翘鸭,小于255的2個(gè)字節(jié)來存儲(chǔ)滴铅,依次類推,最優(yōu)占用字節(jié)的大小就乓。當(dāng)然這也有不好的一面汉匙,采用 Varint 表示法,太大的數(shù)字則需要 5 個(gè) byte 來表示生蚁。不過一般不會(huì)所有的消息中的數(shù)字都是大數(shù)噩翠,而且大數(shù)的概率比較低,所以大多數(shù)情況下邦投,采用 Varint 后伤锚,可以用更少的字節(jié)數(shù)來表示數(shù)字信息。

ZigZag

我們知道有符號(hào)的整型數(shù)值志衣,因?yàn)椴捎玫氖茄a(bǔ)碼屯援,所以一個(gè)負(fù)數(shù)會(huì)比正數(shù)占用的字節(jié)多,比如-1,二進(jìn)制結(jié)構(gòu)是11111111 11111111 11111111 11111111,如果我們還是采用 Varint 表示一個(gè)負(fù)數(shù)念脯,那么需要 5 個(gè) byte玄呛。為此 protobuf?定義了 sint32 ,采用 zigzag 編碼和二。

我們看下zigzag的算法:

Zigzag 編碼用無符號(hào)數(shù)來表示有符號(hào)數(shù)字徘铝,正數(shù)和負(fù)數(shù)交錯(cuò),無論正負(fù)都可以采用較少的 byte 來表示惯吕。

4惕它、字符串

一般單個(gè)字符串的定義,需要兩個(gè)部分組成:leg+value ,如下圖所示:

leg 表示字符串?dāng)?shù)據(jù)的長(zhǎng)度废登,value是字符串真實(shí)數(shù)據(jù)淹魄,protobuf里面這個(gè)leg 采用Varint定義,一般情況下一個(gè)字節(jié)足夠了堡距。

解析速度

解析速度快甲锡,主要?dú)w功于protobuf對(duì)message 沒有動(dòng)態(tài)解析,沒有了動(dòng)態(tài)解析的處理序列化速度自然快了羽戒。就比如xml 缤沦,獲取文件之后,還需要解析標(biāo)簽易稠、節(jié)點(diǎn)缸废、字段,每一個(gè)都需要遍歷,而protobuf不需要企量,直接將field裝入流测萎。

我們知道.proto文件定義了整個(gè)message的結(jié)構(gòu),但這只是一個(gè)定義的配置文件届巩,結(jié)合compiler的使用硅瞧,單個(gè)message的read和write代碼已經(jīng)被生成,無需再基于配置文件解析恕汇,直接操作field到二進(jìn)制流里面零酪,這個(gè)速度就好比你直接操作IO一樣快,沒有其他代價(jià)拇勃。

我們看下生成的message代碼(還是LoginMsg)

write

read

我們看到四苇,read的代碼中tag的生成(轉(zhuǎn)換為int)也已經(jīng)幫你處理,所以都是基于流直接操作方咆。

兼容性

兼容性什么意思月腋,就是說message需要支持向上兼容,不能說單個(gè)message的升級(jí)瓣赂,就會(huì)導(dǎo)致old message解析出錯(cuò)榆骚,這是我們不能忍受的,開發(fā)過程中煌集,需求總變妓肢,誰都無法保證協(xié)議不會(huì)有變更。

比如這種場(chǎng)景下:message 需要增加一個(gè)字段苫纤,如若client沒有升級(jí)碉钠,sever升級(jí)了,此時(shí)client 請(qǐng)求的message格式必定是old 格式卷拘,server 采用的new message來解析喊废,此時(shí)會(huì)出現(xiàn)找不到新字段的問題,流數(shù)據(jù)錯(cuò)亂之后栗弟,后續(xù)的數(shù)據(jù)都會(huì)亂污筷。

那么protobuf是如何處理?這時(shí)候fieldNumber就派上用途了乍赫,看似可又可無的設(shè)計(jì)瓣蛀,其實(shí)包含很大用處。

fieldNumber 為每個(gè)field定義一個(gè)編號(hào)雷厂,其一保證不重復(fù)惋增,其二保證其在流中的位置。如若當(dāng)前數(shù)據(jù)流中有某個(gè)字段罗侯,而解析方?jīng)]有相關(guān)的解析代碼器腋,解析放會(huì)直接skip 吊這個(gè)field,而且讀數(shù)據(jù)的position也會(huì)后移钩杰,保證后續(xù)讀取不出問題纫塌。

如上,線讀取tag讲弄,某個(gè)字段沒有被賦值措左,就沒有這個(gè)字段的tag,解析方不處理避除,如若有某個(gè)字段怎披,而沒有解析方法,就skip了瓶摆,不影響消息的處理凉逛。老數(shù)據(jù)依然被獲取。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末群井,一起剝皮案震驚了整個(gè)濱河市状飞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌书斜,老刑警劉巖诬辈,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異荐吉,居然都是意外死亡焙糟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門样屠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穿撮,“玉大人,你說我怎么就攤上這事痪欲』烨桑” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵勤揩,是天一觀的道長(zhǎng)咧党。 經(jīng)常有香客問我,道長(zhǎng)陨亡,這世上最難降的妖魔是什么傍衡? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮负蠕,結(jié)果婚禮上蛙埂,老公的妹妹穿的比我還像新娘。我一直安慰自己遮糖,他們只是感情好绣的,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般屡江。 火紅的嫁衣襯著肌膚如雪芭概。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天惩嘉,我揣著相機(jī)與錄音罢洲,去河邊找鬼。 笑死文黎,一個(gè)胖子當(dāng)著我的面吹牛惹苗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播耸峭,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼桩蓉,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了劳闹?” 一聲冷哼從身側(cè)響起院究,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎玷或,沒想到半個(gè)月后儡首,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡偏友,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蔬胯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片位他。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡氛濒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鹅髓,到底是詐尸還是另有隱情舞竿,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布窿冯,位于F島的核電站骗奖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏醒串。R本人自食惡果不足惜执桌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望芜赌。 院中可真熱鬧仰挣,春花似錦、人聲如沸缠沈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至颓芭,卻和暖如春顷锰,著一層夾襖步出監(jiān)牢的瞬間丰歌,已是汗流浹背枫疆。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人玛界。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像悼吱,于是被迫代替她去往敵國(guó)和親慎框。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容