Protobuf語(yǔ)法介紹

Protobuf語(yǔ)法介紹

何為Protobuf

我們先看看官方文檔給出的定義和描述

protocol buffers 是一種語(yǔ)言無(wú)關(guān)、平臺(tái)無(wú)關(guān)、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)的方法,它可用于(數(shù)據(jù))通信協(xié)議、數(shù)據(jù)存儲(chǔ)等。

Protocol Buffers 是一種靈活,高效,自動(dòng)化機(jī)制的結(jié)構(gòu)數(shù)據(jù)序列化方法-可類(lèi)比 XML,但是比 XML 更?。? ~ 10倍)、更快(20 ~ 100倍)、更為簡(jiǎn)單。

你可以定義數(shù)據(jù)的結(jié)構(gòu),然后使用特殊生成的源代碼輕松的在各種數(shù)據(jù)流中使用各種語(yǔ)言進(jìn)行編寫(xiě)和讀取結(jié)構(gòu)數(shù)據(jù)。你甚至可以更新數(shù)據(jù)結(jié)構(gòu),而不破壞由舊數(shù)據(jù)結(jié)構(gòu)編譯的已部署程序。

簡(jiǎn)單來(lái)講, ProtoBuf 是結(jié)構(gòu)數(shù)據(jù)序列化方法,可簡(jiǎn)單類(lèi)比于,其具有以下特點(diǎn):

  • 語(yǔ)言無(wú)關(guān)、平臺(tái)無(wú)關(guān)。即 ProtoBuf 支持 Java、C++、Python 等多種語(yǔ)言,支持多個(gè)平臺(tái)
  • 高效。即比 XML 更?。? ~ 10倍)、更快(20 ~ 100倍)、更為簡(jiǎn)單
  • 擴(kuò)展性、兼容性好。你可以更新數(shù)據(jù)結(jié)構(gòu),而不影響和破壞原有的舊程序

使用Protobuf

在protobuf中,協(xié)議是由一系列的消息組成的。因此最重要的就是定義通信時(shí)使用到的消息格式。一個(gè)Protobuf 消息(對(duì)應(yīng)JAVA類(lèi)),由至少一個(gè)字段(對(duì)應(yīng)Java類(lèi)屬性)組合而成。

消息的定義

消息的定義很簡(jiǎn)單,就是message關(guān)鍵字加上消息的名字

message xxx{
    
} 

字段的定義

字段定義格式:

限定修飾符 | 數(shù)據(jù)類(lèi)型 | 字段名稱(chēng) | = | 字段編碼

required string room_id  = 1; // 直播間id

限定修飾符(包含required、optional、Repeated)

required:

表示是一個(gè)必須字段,必須相對(duì)于發(fā)送方,在發(fā)送消息之前必須設(shè)置該字段的值,對(duì)于接收方,必須能夠識(shí)別該字段的意思。發(fā)送之前沒(méi)有設(shè)置required字段或者無(wú)法識(shí)別required字段都會(huì)引發(fā)編解碼異常,導(dǎo)致消息被丟棄。

optional:

表示是一個(gè)可選字段,可選對(duì)于發(fā)送方,在發(fā)送消息時(shí),可以有選擇性的設(shè)置或者不設(shè)置該字段的值。對(duì)于接收方,如果能夠識(shí)別可選字段就進(jìn)行相應(yīng)的處理,如果無(wú)法識(shí)別,則忽略該字段,消息中的其它字段正常處理。---因?yàn)閛ptional字段的特性,很多接口在升級(jí)版本中都把后來(lái)添加的字段都統(tǒng)一的設(shè)置為optional字段,這樣老的版本無(wú)需升級(jí)程序也可以正常的與新的軟件進(jìn)行通信,只不過(guò)新的字段無(wú)法識(shí)別而已,因?yàn)椴⒉皇敲總€(gè)節(jié)點(diǎn)都需要新的功能,因此可以做到按需升級(jí)和平滑過(guò)渡。

repeated:

表示該字段可以包含0~N個(gè)元素。其特性和optional一樣,但是每一次可以包含多個(gè)值??梢钥醋魇窃趥鬟f一個(gè)數(shù)組的值。類(lèi)比于Java這邊的List

數(shù)據(jù)類(lèi)型

protobuf定義了一套基本的數(shù)據(jù)類(lèi)型,幾乎都映射了Java語(yǔ)言的基礎(chǔ)數(shù)據(jù)類(lèi)型(主要以Java為例)

詳見(jiàn)下面表格

protobuf.jpg
字段的默認(rèn)值
  • 對(duì)于strings,默認(rèn)是一個(gè)空string
  • 對(duì)于bytes,默認(rèn)是一個(gè)空的bytes
  • 對(duì)于bools,默認(rèn)是false
  • 對(duì)于數(shù)值類(lèi)型,默認(rèn)是0
  • 對(duì)于枚舉,默認(rèn)是第一個(gè)定義的枚舉值,必須為0

字段名稱(chēng)

字段的命名方式與Java的命名方式大致一致

字段編碼值

字段編碼是一個(gè)序列化和反序列化的標(biāo)記值,有了該值,通信雙方才能互相識(shí)別對(duì)方的字段。當(dāng)然相同的編碼值,其限定修飾符和數(shù)據(jù)類(lèi)型必須相同。編碼值的取值范圍為 1~2^32(4294967296)

其中 1~15的編碼時(shí)間和空間效率都是最高的,編碼值越大,其編碼的時(shí)間和空間效率就越低(相對(duì)于1-15),當(dāng)然一般情況下相鄰的2個(gè)值編碼效率的是相同的,除非2個(gè)值恰好實(shí)在4字節(jié),12字節(jié),20字節(jié)等的臨界區(qū)。比如15和16

1900~2000編碼值為Google protobuf 系統(tǒng)內(nèi)部保留值,建議不要在自己的項(xiàng)目中使用。protobuf 還建議把經(jīng)常要傳遞的值把其字段編碼設(shè)置為1-15之間的值

完整的消息定義示例

message CSSendLiveRoomMsgReq
{
    required string room_id  = 1; // 直播間id
    required uint32 local_msgid  = 2; // 發(fā)送客戶端本地的消息id,用以處理回執(zhí)消息
    required ImMsgBody im_message_body = 3; // 消息內(nèi)容
    optional string from_username = 4;
    optional uint32 priority = 5;   // 消息優(yōu)先級(jí)別
}

枚舉值

枚舉的定義和Java 相同,使用enum關(guān)鍵字,但是有一些限制。

枚舉值必須大于等于0的整數(shù)。

使用分號(hào);分隔枚舉變量而不是Java 語(yǔ)言中的逗號(hào),

示例:

enum ACCOUNT_TYPE
{
    ACCOUNT_TYPE_IM_ACCOUNT = 1;
    ACCOUNT_TYPE_VIVO_OPENID = 2;
    ACCOUNT_TYPE_ANONYMOUS_ACCOUNT = 3;
}

使用其他消息類(lèi)型

你可以將其他消息類(lèi)型用作字段類(lèi)型。已我們現(xiàn)在IM的為例,在每一個(gè)CSSendLiveRoomMsgReq消息中包含ImMsgBody消息,此時(shí)可以在相同的.proto文件中定義一個(gè)ImMsgBody消息類(lèi)型,然后在CSSendLiveRoomMsgReq消息中指定一個(gè)ImMsgBody類(lèi)型的字段

示例:

// 直播間消息下發(fā)
message CSNotifyLiveRoomMsg
{
    required string room_id = 1; // 直播間id
    required uint64 msg_id = 2; //消息ID
    required uint64 timestamp = 3; //消息生成的時(shí)間戳
    required ImMsgBody im_message_body = 4;
    optional string from_username = 5;
}

message ImMsgBody
{
    required int32 message_type = 1;
    optional uint32 message_flag = 2; //消息標(biāo)志位,留待后續(xù)試用,可以用來(lái)做一些特殊處理的標(biāo)志,比如要不要做離線緩存等
    oneof real_message
    {
        ImTextMessage text_message = 3;
        ImVoiceMessage voice_message = 4;
        ImAppMessage app_message = 5;
        ImImageMessage image_message = 6;
        ImVideoMessage video_message = 7;
        ImFileMessage file_message = 8;
        ImLocateMessage locate_message = 9;
        ImH5Message h5_message = 10;
    }
}

Oneof

如果你的消息中有很多可選字段, 并且同時(shí)至多一個(gè)字段會(huì)被設(shè)置, 你可以加強(qiáng)這個(gè)行為,使用oneof特性節(jié)省內(nèi)存,Oneof字段就像可選字段, 除了它們會(huì)共享內(nèi)存, 至多一個(gè)字段會(huì)被設(shè)置。 設(shè)置其中一個(gè)字段會(huì)清除其它字段。

為了在.proto定義Oneof字段, 你需要在名字前面加上oneof關(guān)鍵字, 比如下面例子的ImMsgBody:

message ImMsgBody
{
    required int32 message_type = 1;
    optional uint32 message_flag = 2; 
    oneof real_message
    {
        ImTextMessage text_message = 3;
        ImVoiceMessage voice_message = 4;
        ImAppMessage app_message = 5;
        ImImageMessage image_message = 6;
        ImVideoMessage video_message = 7;
        ImFileMessage file_message = 8;
        ImLocateMessage locate_message = 9;
        ImH5Message h5_message = 10;
    }
}

oneof的特性

  • 設(shè)置oneof會(huì)自動(dòng)清楚其它oneof字段的值. 所以設(shè)置多次后,只有最后一次設(shè)置的字段有值
  • 如果解析器遇到同一個(gè)oneof中有多個(gè)成員,只有最后一個(gè)會(huì)被解析成消息
  • oneof不支持repeated
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 翻譯查閱外網(wǎng)資料過(guò)程中遇到的比較優(yōu)秀的文章和資料,一是作為技術(shù)參考以便日后查閱,二是訓(xùn)練英文能力。此文翻譯自 Pr...
    401閱讀 69,128評(píng)論 1 39
  • 定義一個(gè)消息類(lèi)型指定字段類(lèi)型分配標(biāo)識(shí)號(hào)指定字段規(guī)則添加更多消息類(lèi)型添加注釋保留標(biāo)識(shí)符(Reserved)從.pro...
    MrChenyz閱讀 2,531評(píng)論 0 0
  • protobuf詳解 1、安裝 源碼包下載 git clone https://github.com/goog...
    yongfutian閱讀 5,529評(píng)論 0 0
  • 漸變的面目拼圖要我怎么拼? 我是疲乏了還是投降了? 不是不允許自己墜落, 我沒(méi)有滴水不進(jìn)的保護(hù)膜。 就是害怕變得面...
    悶熱當(dāng)乘涼閱讀 4,480評(píng)論 0 13
  • 感覺(jué)自己有點(diǎn)神經(jīng)衰弱,總是覺(jué)得手機(jī)響了;屋外有人走過(guò);每次媽媽不聲不響的進(jìn)房間突然跟我說(shuō)話,我都會(huì)被嚇得半死!一整...
    章魚(yú)的擁抱閱讀 2,386評(píng)論 4 5

友情鏈接更多精彩內(nèi)容