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("[email protected]")
    .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                                                               
#include                                                              
#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 = "[email protected]";                                         
    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                                                               
#include                                                              
#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

你可能感兴趣的:(Protocol Buffer快速上手)