protobuf编译以及概述

Download

源码: https://github.com/google/protobuf 
依赖: build-essential autoconf automake libtool curl

Installatin

假定下载的是全版本protobuf-3.4.x.zip。则安装步骤如下:

安装runtimes:

unzip protobuf-3.4.x.zip 
cd protobuf-3.4.x/
./autogen.sh 
./configure 
make
make check
sudo make install

以上命令如果出错,则根据提示执行如下的对应命令:

sudo apt install curl
sudo apt install autoconf
sudo apt install libtool
安装Python需要的库:

cd python/
python setup.py build
python setup.py test
sudo python setup.py install
如果以上步骤出错,则根据提示执行如下的对应命令:

sudo apt install python-pip
pip install --upgrade pip
pip install setuptools

路径

usr/local/bin
usr/local/lib,
usr/local/include 
是也系统默认路径之一,所以到这一步就可以使用protobuf了
$ protoc -I=./ --cpp_out=./ test.proto
到你的test.proto文件所在目录使用命令

protoc -I=./ --cpp_out=./ 生成C++版本的协议文件
一切OK的话,你回在当前目录看到.h和.cc文件

在/etc/profile 或者用户目录 ~/.bash_profile
添加下面内容
############################### add protobuf lib path ########################################
#(动态库搜索路径) 程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/protobuf/lib/
#(静态库搜索路径) 程序编译期间查找动态链接库时指定查找共享库的路径
export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/protobuf/lib/
#执行程序搜索路径
export PATH=$PATH:/usr/local/protobuf/bin/
#c程序头文件搜索路径
export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/local/protobuf/include/
#c++程序头文件搜索路径
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/local/protobuf/include/
#pkg-config 路径
export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/
######################################################################################

如果出现找不到符号和链接错误请记得加上链接选项 -lprotobuf 。并确认你的静态库路径是否生效了 echo $LIBRARY_PATH

概述

syntax

// rs.UserInfo.proto
syntax = "proto2";

package rs;
//import "xxx.proto";

message UserInfo{
    optional uint32 id = 1;
    required string name = 2;
    required string pswd = 3;

    enum Type{
        ROOT = 0,
        ADMIN = 1,
        USUAL = 2,
        VISITOR = 4,
    }
    required Type type = 4;

    repeated string phone = 5;
    repeated string email = 6;
}

basic

  • syntax表示使用语法版本
  • package类似c++中的namespace,归类并避免命名冲突
  • import引入其他文件
  • message用于定义结构体
  • enum用于枚举

字段修饰符

proto2:

  • required:必须的
  • optional:可选的
  • repeated:重复的(类比数组,可以为0)

proto3:

  • singular:单一的(默认,可以省略)
  • repeated:重复的

字段类型

varint
Varint 是一种紧凑的表示数字的方法。它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。这能减少用来表示数字的字节数。比如对于 int32 类型的数字,一般需要 4 个 byte 来表示。但是采用 Varint,对于很小的 int32 类型的数字,则可以用 1 个 byte 来表示。当然凡事都有好的也有不好的一面,采用 Varint 表示法,大的数字则需要 5 个 byte 来表示。从统计的角度来说,一般不会所有的消息中的数字都是大数,因此大多数情况下,采用 Varint 后,可以用更少的字节数来表示数字信息。

编码规则:

使用Little-Endian小端字节序;
每个字节最高位是标志位,0表示结束,1表示继续;后7为保存数据;
因为负数最高位为1,所以先要进行int2uint转换,采用ZigZag方法实现;

uint32 Zig(int32 value){
    return (uint)((value << 1) ^ (value >> 31));    
}

int32 Zag(uint32 value){
    return (-(value & 0x01)) ^ ((value >> 1) & ~( 1<< 31));
}

message存储格式
message采用key[-lenght]-value存储格式

key = field_num << 3 | data_type
对于Lenght-delimited即data_type == 2类型数据紧跟着length信息,标识value的长度
key、lenght、data_type == 1的value都采用varint编码
生成代码
使用protoc编译proto文件即可自动生成代码,下面以cpp为例

protoc UserInfo.proto --cpp_out=cpp
在cpp/rs目录下可以发现生成了两个文件UserInfo.pb.h,UserInfo.pb.cc 


下面我们分析下生成代码提供的类和方法
基类::google::protobuf::Message
序列化
SerializedAsString(),SerializedToString(std::string*):序列化为std::string
SerializedToArray(void*,int):序列化为byte数组
SerializedToOstream(ostream*):序列化到输出流
反序列化
ParseFromString(std::string& data):从字符串中反序列化
ParseFromArray(const void *,int):从字节序中反序列化
ParseFromIstream(istream*):从输入流中反序列化

getter、setter

all:

clear_field():重置字段
非repeated字段:

field():获取字段值,只读
set_field():设置字段
mutable_filed():返回字段指针,用于修改
repeated字段:

filed(int):获取字段对应索引值,只读
set_filed(int,x):设置字段对应索引值
mutable_filed(int):返回字段对应索引指针,用于修改
add_filed():添加字段
filed_size():返回字段元素个数


TestCase

#include "rs/UserInfo.pb.h"

using namespace rs;

int main(int argc, char* argv[]){
    UserInfo user;
    user.set_id(1);
    user.set_name("hw");
    user.set_pswd("123456");
    user.set_type(UserInfo_Type_ADMIN);
    user.add_phone("13012345678");
    std::string strUser = user.SerializeAsString();
    user.PrintDebugString();
    printf("protobuf bytes:%d\n\n", strUser.size());

    UserInfo user1;
    user1.ParseFromString(strUser);
    user1.PrintDebugString();
    char json[1024];
    snprintf(json, 1024, "{id:%d,name:%s,pswd:%s,type:%d,phone:[%s]}",
     user1.id(), user1.name().c_str(), user1.pswd().c_str(), user1.type(), 
     user1.phone(0).c_str());
    puts(json);

    printf("phone_size:%d\n", user1.phone_size());
    printf("json bytes:%d\n", strlen(json));

    return 0;
}

编译:g++ -g -Wall main.cpp rs/UserInfo.pb.cc -o test -lprotobuf

运行结果:

id: 1
name: "hw"
pswd: "123456"
type: ADMIN
phone: "13012345678"
protobuf bytes:29

id: 1
name: "hw"
pswd: "123456"
type: ADMIN
phone: "13012345678"
{id:1,name:hw,pswd:123456,type:1,phone:[13012345678]}
phone_size:1
json bytes:53

原创文章 96 获赞 48 访问量 6万+

猜你喜欢

转载自blog.csdn.net/wteruiycbqqvwt/article/details/103786039