Protocol Buffer快速上手

Protocol Buffer 简介

Protocol Buffer是Google开源的一种在不同编程语言之间通信的技术。它的核心思想就是,通过一个.proto文件,定义好消息格式,再用不同语言相关的工具(官方默认提供Java、C++、Python、Go),将.proto指定的消息格式转化成语言相关的源代码。

Java中的使用举例

官网上有现成的Java的例子,基本上非常简单,按步骤来就可以,主要就是三板斧:
1.定义.proto文件,说明消息的格式
2.使用protocol buffer compiler将.proto文件转化成.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;
}

关键字说明:

  • package:proto文件的组织也是有层次关系的,这个层次关系不一定和Java包的层次关系是对应的。例如在一个proto文件可能用到另一个proto文件中定义的消息结构,那么可以用import来实现。
  • java_package:产生的类的包名。
  • java_outer_classname:产生的类的类名。注意我们在代码中使用Java Protocol Buffer API的时候,主要用到的是.proto文件里面中的message所指定的类,例如上面的Person类,而一般不会直接用到AddressBookProtos类,AddressBookProtos类只是一个装载这些消息的类而已。
  • required:必须提供此值,否则 message 会被认为是未初始化的。
  • optional:可以设置也可以不设置此值。如果未设置 optional field 将使用默认值。
  • repeated:可以理解为动态长度的数组。
  • Tag: =1 =2 用于指定 field 的唯一的 tag,在使用于二进制编码中。在 Protocol Buffers 编码时,Tag 值为 1 ~ 15 会比值为 15 以上的少消耗 1 个字节的空间。

生成Java类

编译工具需要单独下载
安装好编译工具后,使用以下的命令就可以生成java相关的类了。

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

SRC_DIR指定了原代码的目录,DST_DIR指定了生成的类的目录。另外当然还要加上咱们的.proto文件。
为了正常使用生成的Java类,还要下载Protocol Buffer相关的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

一个生成二进制数据的例子,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;                                                                   
} 

执行./pack_demo | ./unpack_demo,可以看到生成的二进制被解析的结果:

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容