[Note] Google protocol buffer 基本语法和简单实例

简介

  • Protocol Buffers (a.k.a., protobuf) 是google 的一种数据交换的格式,它独立于语言,独立于平台。提供了三种语言的实现:java、c++ 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。

相对于传统的Json、XML数据存储格式,PB是一种更加轻量化、更加高效的结构化数据存储格式。

基本用法

    数据结构体:  
        message message_name{message_body;}  
        message_body格式:  
            例如required int32 query = 1[defaut=10];  
            形式为:rule type name = value[other_rule];  
    规则:  
        required表示必须具有该值域;  
        optional表示可选的值域;  
        repeated表示可重复的值域(即>=0);  
        其中requered/optional是常用rule,而repeated则不常用同时因为是历史遗留现使用repeated int32 samples=4[packed=true];形式;  
      
    value值:  
        value值最小为1,是底层编码时使用其中1-15占一位,>15则会占多位;  
        不同的message中的value值互不干扰,常以1开始计数。  
      
    数据类型之基本类型:  
    .proto Type             C++ Type            Java Type   
    double                  double              double   
    float                   float               float   
    int32                   int32               int   
    int64                   int64               long   
    uint32                  uint32              int   
    uint64                  uint64              long   
    sint32                  int32               int   
    sint64                  int64               long   
    fixed32                 uint32              int   
    fixed64                 uint64              long   
    sfixed32                int32               int   
    sfixed64                int64               long   
    bool                    bool                boolean   
    string                  string              String   
    bytes                   string              ByteString   
      
    数据类型之复杂类型:  
        复杂类型主要包括:枚举,其他message,groups等。  
        枚举定义例如:enum Corpus{WEB=0;LOCAL=1}  
            枚举定义在message中。  
            可以使用其他message作为类型来定义成员。  
            groups我的理解有些像C++中的union结构。  
      
    嵌套定义:  
        可以嵌套定义message结构,而嵌套定义的message被其他message作为成员类型时需要形式为outmessage.inmessage形式。  
      
    包结构:  
        定义形式:package foo.bar;  
        对应C++中则生成两个命名空间foo和bar,且bar定义在foo中;  
        可以通过import "myproject/other_protos.proto";来引入.proto文件;  
        引用其他package中message时需要完整的package路径;  
      
    Services:  
        主要用于RPC系统中,在.proto中定义接口;  
        定义形式如例子:  
        service SearchService {  
            rpc Search(SearchRequest) return (SearchResponse);  
        }  
      
    .proto文件编译:  
        格式:  
            protoc -–proto_path=(.proto文件路径) -–cpp_out=(.cc .java生成文件路径) (.proto文件路径)/?.proto  
            -–proto_path  简化为:  --I  
            其中可根据需要更改:cpp_out选项为java_out/python_out。  
        例子:  
            protoc -I=./ --cpp_out=./ model.proto 


复制代码

简单实例

测试环境:

Ubuntu 18.04
GCC 7.2.0
protoc 3.15.8

  1. 编写.proto文件
syntax = "proto3";
package test;

message person

{
    int32   id = 1; // ID
    string  name = 2;   // str
    repeated int32  email = 3;  //optional field
}
复制代码

Protobuf 有两个大版本,proto2 和 proto3,同比 python 的 2.x 和 3.x 版本,如果是新接触的话,同样建议直接入手 proto3 版本。所以下文的描述都是基于 proto3 的。
proto3 相对 proto2 而言,简言之就是支持更多的语言(Ruby、C#等)、删除了一些复杂的语法和特性、引入了更多的约定等。

syntax = "proto3"; // PB协议版本

import "google/protobuf/any.proto"; // 引用外部的message,可以是本地的,也可以是此处比较特殊的 Any

package jet.protobuf; // 包名,其他 proto 在引用此 proto 的时候,就可以使用 test.protobuf.PersonTest 来使用,
// 注意:和下面的 java_package 是两种易混淆概念,同时定义的时候,java_package 具有较高的优先级

option java_package = "com.jet.protobuf"; // 生成类的包名,注意:会在指定路径下按照该包名的定义来生成文件夹
option java_outer_classname="PersonTestProtos"; // 生成类的类名,注意:下划线的命名会在编译的时候被自动改为驼峰命名

message PersonTest {  
    int32 id = 1; // int 类型  
    string name = 2; // string 类型  
    string email = 3;  
    Sex sex = 4; // 枚举类型  
    repeated PhoneNumber phone = 5; // 引用下面定义的 PhoneNumber 类型的 message  
    map<string, string> tags = 6; // map 类型  
    repeated google.protobuf.Any details = 7; // 使用 google 的 any 类型  

    // 定义一个枚举  
    enum Sex {      
        DEFAULT = 0;      
        MALE = 1;      
        Female = 2;  
    }  
    
    // 定义一个 message  
    message PhoneNumber {    
        string number = 1;    
        PhoneType type = 2;    
        
        enum PhoneType {      
            MOBILE = 0;      
            HOME = 1;      
            WORK = 2;    
        }  
        
    }
    
}
复制代码
  1. 生成对应类
protoc test.proto --cpp_out ./
复制代码

会在当前目录下生成两个文件test.pb.cc test.pb.h

  1. 编写测试用的c++代码
#include <stdio.h>
#include <string.h>
#include "test.pb.h"
using namespace std;

int main()
{
    test::person a;
    a.set_id(8);
    a.set_name("Kios");

    string tmp;
    bool ret = a.SerializeToString(&tmp);
    if(ret)
    {
        cout << "encode success!" << endl;
    }
    else
    {
	cout << "encode failed!" << endl;
    }

    test::person b;
    ret = b.ParseFromString(tmp);
    if(ret)
    {
        cout << "decode success!\n id = " << b.id() << "\n name = " << b.name().c_str() << endl;
    }
    else
        {
        cout << "decode faild!\n" << endl;
	}
    return 0;
}
复制代码
  1. 指定链接库并编译
g++ test.pb.cc main.cpp -o main.o -L/usr/local/protobuf/lib/ -I/usr/local/protobuf/include/ -I./ -lprotobuf --std=c++11
//值得注意的是要记得指定c++版本
复制代码

5.查看运行结果

(base) seemmo@ubuntu:~/projects/proto_try$ ./main.o
> encode success!
> decode success!
> id = 8
> name = Kios
复制代码

需要注意的一些环境问题

  • command not found

一般为环境变量未设置好,将<.../protobuf/bin>添加至PATH

  • 编译通过后运行时报 error while loading shared libraries: libprotoc.so.9: cannot open shared object file:No such...
动态链接库设置问题
第一步:创建 /etc/ld.so.conf.d/libprotobuf.conf 输入 /usr/local/protobuf/lib/ 执行 sudo ldconfig
第二步:vim /etc/profile --> 输入export LD_LIBRARY_PATH=/usr/local/protobuf/lib:$LD_LIBRARY_PATH --> # source /etc/profile
复制代码

临时使用也可以直接输入:
export LD_LIBRARY_PATH = /usr/local/protobuf/lib:$LD_LIBRARY_PATH,
使用export赋值的变量能够被子进程继承,而直接使用=的不能;
但是在子进程中,无论使用export还是=都无法改变父进程的值。

参考链接:blog.csdn.net/happyday_d/…

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享