Protocol Buffer快速上手

Protocol Buffer 簡介

Protocol Buffer是Google開源的一種在不同編程語言之間通信的技術(shù)巫延。它的核心思想就是收奔,通過一個.proto文件蹭劈,定義好消息格式,再用不同語言相關(guān)的工具(官方默認提供Java鳖谈、C++凝危、Python波俄、Go),將.proto指定的消息格式轉(zhuǎn)化成語言相關(guān)的源代碼媒抠。

Java中的使用舉例

官網(wǎng)上有現(xiàn)成的Java的例子弟断,基本上非常簡單,按步驟來就可以趴生,主要就是三板斧:
1.定義.proto文件阀趴,說明消息的格式
2.使用protocol buffer compiler將.proto文件轉(zhuǎn)化成.java文件
3.使用Java protocol buffer API來生成或者解析消息昏翰。

定義proto文件

package tutorial;

option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phone = 4;
}

message AddressBook {
  repeated Person person = 1;
}

關(guān)鍵字說明:

  • package:proto文件的組織也是有層次關(guān)系的,這個層次關(guān)系不一定和Java包的層次關(guān)系是對應的刘急。例如在一個proto文件可能用到另一個proto文件中定義的消息結(jié)構(gòu)棚菊,那么可以用import來實現(xiàn)。
  • java_package:產(chǎn)生的類的包名叔汁。
  • java_outer_classname:產(chǎn)生的類的類名统求。注意我們在代碼中使用Java Protocol Buffer API的時候,主要用到的是.proto文件里面中的message所指定的類据块,例如上面的Person類码邻,而一般不會直接用到AddressBookProtos類,AddressBookProtos類只是一個裝載這些消息的類而已另假。
  • required:必須提供此值像屋,否則 message 會被認為是未初始化的。
  • optional:可以設置也可以不設置此值边篮。如果未設置 optional field 將使用默認值己莺。
  • repeated:可以理解為動態(tài)長度的數(shù)組。
  • Tag: =1 =2 用于指定 field 的唯一的 tag戈轿,在使用于二進制編碼中凌受。在 Protocol Buffers 編碼時,Tag 值為 1 ~ 15 會比值為 15 以上的少消耗 1 個字節(jié)的空間思杯。

生成Java類

編譯工具需要單獨下載胜蛉。
安裝好編譯工具后,使用以下的命令就可以生成java相關(guān)的類了智蝠。

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto

SRC_DIR指定了原代碼的目錄腾么,DST_DIR指定了生成的類的目錄奈梳。另外當然還要加上咱們的.proto文件杈湾。
為了正常使用生成的Java類,還要下載Protocol Buffer相關(guān)的Java庫攘须,http://central.maven.org/maven2/com/google/protobuf/protobuf-java/2.6.1/protobuf-java-2.6.1.jar漆撞。

生成和解析消息

每個生成的解析類都包含了用來生成和解析二進制消息的方法,包括:

byte[] toByteArray();: serializes the message and returns a byte array containing its raw bytes.
static Person parseFrom(byte[] data);: parses a message from the given byte array.
void writeTo(OutputStream output);: serializes the message and writes it to an OutputStream.
static Person parseFrom(InputStream input);: reads and parses a message from an InputStream.

生成消息:

Person john =
  Person.newBuilder()
    .setId(1234)
    .setName("John Doe")
    .setEmail("jdoe@example.com")
    .addPhone(
      Person.PhoneNumber.newBuilder()
        .setNumber("555-4321")
        .setType(Person.PhoneType.HOME))
    .build();

解析:

import com.example.tutorial.AddressBookProtos.AddressBook;
import com.example.tutorial.AddressBookProtos.Person;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;

class ListPeople {
  // Iterates though all people in the AddressBook and prints info about them.
  static void Print(AddressBook addressBook) {
    for (Person person: addressBook.getPersonList()) {
      System.out.println("Person ID: " + person.getId());
      System.out.println("  Name: " + person.getName());
      if (person.hasEmail()) {
        System.out.println("  E-mail address: " + person.getEmail());
      }

      for (Person.PhoneNumber phoneNumber : person.getPhoneList()) {
        switch (phoneNumber.getType()) {
          case MOBILE:
            System.out.print("  Mobile phone #: ");
            break;
          case HOME:
            System.out.print("  Home phone #: ");
            break;
          case WORK:
            System.out.print("  Work phone #: ");
            break;
        }
        System.out.println(phoneNumber.getNumber());
      }
    }
  }

  // Main function:  Reads the entire address book from a file and prints all
  //   the information inside.
  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.err.println("Usage:  ListPeople ADDRESS_BOOK_FILE");
      System.exit(-1);
    }

    // Read the existing address book.
    AddressBook addressBook =
      AddressBook.parseFrom(new FileInputStream(args[0]));

    Print(addressBook);
  }
}

C中的使用舉例

使用protoc-c之前于宙,需要安裝浮驳,安裝的方法見這里
首先需要定義一個proto文件捞魁,這個步驟就不再具體說明至会,我們以addressbook.proto為例說明。有了proto文件后谱俭,使用protoc-c生成.h和.c文件奉件,例如:

protoc-c --c_out=. addressbook.proto

生成的文件如下:

addressbook.pb-c.c
addressbook.pb-c.h

一個生成二進制數(shù)據(jù)的例子宵蛀,pack_demo.c

#include <stdio.h>                                                              
#include <stdlib.h>                                                             
#include "addressbook.pb-c.h"                                                   
                                                                                
int main (int argc, const char * argv[])                                        
{                                                                               
    int person_number = 1;                                                      
    int phone_number_size = 1;                                                  
    int len = 0;                                                                
    void *buf = NULL;                                                           
    Tutorial__Person__PhoneNumber **phone_number_array = NULL;                  
    Tutorial__Person **person_array = NULL;                                     
    Tutorial__AddressBook address_book = TUTORIAL__ADDRESS_BOOK__INIT; // AMessage
    Tutorial__Person person = TUTORIAL__PERSON__INIT;                           
    Tutorial__Person__PhoneType phone_type = TUTORIAL__PERSON__PHONE_TYPE__HOME;
    Tutorial__Person__PhoneNumber phone_number = TUTORIAL__PERSON__PHONE_NUMBER__INIT;
                                                                                
    phone_number.number = "13401167356";                                        
    phone_number.type= TUTORIAL__PERSON__PHONE_TYPE__MOBILE;                    
    person.name = "JiangJiafu";                                                 
    person.id = 1;                                                              
    person.email = "jiangjf@sugon.com";                                         
    phone_number_array = malloc(sizeof(Tutorial__Person__PhoneNumber *) * phone_number_size);
    person.n_phone = phone_number_size;                                         
    person.phone = phone_number_array;                                          
    person.phone[0] = &phone_number;                                            
    person_array = malloc(sizeof(Tutorial__Person *) * person_number);          
    if (!person_array)                                                          
    {                                                                           
        exit(-1);                                                               
    }                                                                           
    address_book.n_person = phone_number_size;                                  
    address_book.person = person_array;                                         
    address_book.person[0] = &person;                                           
                                                                                
    len = tutorial__address_book__get_packed_size(&address_book);               
    buf = malloc(len);                                                          
tutorial__address_book__pack(&address_book, buf);
fprintf(stderr,"Writing %d serialized bytes\n",len); // See the length of message
    fwrite(buf,len,1,stdout); // Write to stdout to allow direct command line piping
    free(person_array);                                                         
    free(phone_number_array);                                                   
    free(buf); // Free the allocated serialized buffer                          
    return 0;
}

解析二進制的例子(unpack.c):

#include <stdio.h>                                                              
#include <stdlib.h>                                                             
#include "addressbook.pb-c.h"                                                   
#define MAX_MSG_SIZE 1024                                                       
                                                                                
static size_t                                                                   
read_buffer (unsigned max_length, uint8_t *out)                                 
{                                                                               
    size_t cur_len = 0;                                                         
    size_t nread;                                                               
    while ((nread=fread(out + cur_len, 1, max_length - cur_len, stdin)) != 0)   
    {                                                                           
        cur_len += nread;                                                       
        if (cur_len == max_length)                                              
        {                                                                       
            fprintf(stderr, "max message length exceeded\n");                   
            exit(1);                                                            
        }                                                                       
    }                                                                           
    return cur_len;                                                             
}                                                                               
                                                                                
int main (int argc, const char * argv[])                                        
{                                                                               
    int i = 0;                                                                  
    Tutorial__AddressBook *address_book = NULL;                                 
    // Read packed message from standard-input.                                 
    uint8_t buf[MAX_MSG_SIZE];                                                  
    size_t msg_len = read_buffer (MAX_MSG_SIZE, buf);                           
                                                                                
    // Unpack the message using protobuf-c.                                     
    address_book = tutorial__address_book__unpack(NULL, msg_len, buf);          
    if (address_book == NULL)                                                   
    {                                                                           
        fprintf(stderr, "error unpacking incoming message\n");                  
        exit(1);                                                                
} 
// display the message's fields.                                            
    printf("Received: person number=%d\n", address_book->n_person);  // required field
    for (i = 0; i < address_book->n_person; ++i)                                
    {                                                                           
        printf("Person name: %s\n", address_book->person[i]->name);             
        printf("Person id: %d\n", address_book->person[i]->id);                 
    }                                                                           
    // Free the unpacked message                                                
    tutorial__address_book__free_unpacked(address_book, NULL);                  
    return 0;                                                                   
} 

執(zhí)行./pack_demo | ./unpack_demo,可以看到生成的二進制被解析的結(jié)果:

Writing 50 serialized bytes
Received: person number=1
Person name: JiangJiafu
Person id: 1

Makefile:

INC_DIR = -I/usr/local/include                                                  
CFLAGS = -I.                                                                    
LDFLAGS = -L/usr/local/lib -lprotobuf-c                                         
all: pack_demo unpack_demo                                                      
                                                                                
pack_demo: pack_demo.c addressbook.pb-c.c                                       
        gcc $(INC_DIR) $(CFLAGS) pack_demo.c addressbook.pb-c.c -o pack_demo -lm $(LDFLAGS) 
                                                                                
unpack_demo: unpack_demo.c addressbook.pb-c.c                                   
        gcc $(INC_DIR) $(CFLAGS) unpack_demo.c addressbook.pb-c.c -o unpack_demo -lm $(LDFLAGS) 
.PHONY: all clean                                                               
                                                                                                                                                                
clean:                                                                          
        rm -rf pack_demo
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末县貌,一起剝皮案震驚了整個濱河市术陶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌煤痕,老刑警劉巖梧宫,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異摆碉,居然都是意外死亡塘匣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門巷帝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來馆铁,“玉大人,你說我怎么就攤上這事锅睛〔壕蓿” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵现拒,是天一觀的道長辣垒。 經(jīng)常有香客問我,道長印蔬,這世上最難降的妖魔是什么勋桶? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮侥猬,結(jié)果婚禮上例驹,老公的妹妹穿的比我還像新娘。我一直安慰自己退唠,他們只是感情好鹃锈,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瞧预,像睡著了一般屎债。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上垢油,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天盆驹,我揣著相機與錄音,去河邊找鬼滩愁。 笑死躯喇,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的硝枉。 我是一名探鬼主播廉丽,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼秸讹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了雅倒?” 一聲冷哼從身側(cè)響起璃诀,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蔑匣,沒想到半個月后劣欢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡裁良,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年凿将,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片价脾。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡牧抵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出侨把,到底是詐尸還是另有隱情犀变,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布秋柄,位于F島的核電站获枝,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏骇笔。R本人自食惡果不足惜省店,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望笨触。 院中可真熱鬧懦傍,春花似錦、人聲如沸芦劣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽持寄。三九已至源梭,卻和暖如春娱俺,著一層夾襖步出監(jiān)牢的瞬間稍味,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工荠卷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留模庐,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓油宜,卻偏偏與公主長得像掂碱,于是被迫代替她去往敵國和親怜姿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

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