protobuf使用

protobuf、json、xml

这三个都是一个序列化数据结构的格式,用于序列化和反序列化的一个数据载体。
作为序列化和反序列化的数据载体,程序使用这种数据格式一般的过程是

  1. A进程对某个特定数据机构进行数据填充
  2. 使用库函数基于特定数据结构进行序列化(protobuf、json、xml)
  3. A进程将序列化的数据发送给B进程
  4. B进程使用库函数将序列化数据反序列化提取到特定数据结构
    这样整个过程下来,本质上,上面这三种数据结构就是能够可以将结构化的数据在进程间传输。

比较

序列化(反序列化)效率(时间消耗):protobuf > json > xml
序列化字节数:protobuf < json < xml
可读性:xml > json > protobuf

XML

文本数据格式,有效数据利用率较低;
本地配置、ui配置

protobuf

二进制数据格式;
业务内部使用,各个服务之间的rpc调用,即时通讯项目;
谷歌私有的协议,不属于国际标准。

proto文件编写语法

  • repeated – 修饰为数组属性
  • message – struct

模板代码

Welsey.Struct.proto

// 定义语法规则,通常proto3好于proto2,proto2好于proto1
// 默认是proto2
syntax="proto3";

// 定义作用域,下面定义的最后都会在该作用域下
package Welsey.Struct;

// 是否使用轻量化的库
// option optimize_for = LITE_RUNTIME;

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

message Phone
{
    PhoneType type = 1;
    string number = 2;
}

message Person
{
    // 后面的那个序号表示:在protobuf数据包序列化和反序列化时,对应字段的排序位置,一般直接按照顺序写1、2、3...即可
    double value_double     = 1;                    // double
    float value_float       = 2;                    // float
    int32 value_int32       = 3;                    // int32 -- 使⽤变⻓编码,对于负值的效率很低,如果你的域有可能有负值,请使⽤sint64替代
    uint32 value_uint32     = 4;                    // uint32 -- 使⽤变⻓编码
    uint64 value_uint64     = 5;                    // uint64 -- 使⽤变⻓编码
    sint32 value_sint32     = 6;                    // int32 -- 使⽤变⻓编码,这些编码在负值时比int32高效的多
    sint64 value_sint64     = 7;                    // int64 -- 使⽤变⻓编码,有符号的整型值。编码时通常比int64高效
    fixed32 value_fixed32   = 8;                    // uint32 -- 总是4个字节,如果数值总是⽐总是⽐2^28⼤的话,这个类型会⽐uint32⾼效。
    fixed64 value_fixed64   = 9;                    // uint64 -- 总是8个字节,如果数值总是⽐总是⽐2^56⼤的话,这个类型会⽐uint64⾼效。
    sfixed32 value_sfixed32 = 10;                   // int32 -- 总是4个字节
    sfixed64 value_sfixed64 = 11;                   // int64 -- 总是8个字节
    bool value_bool         = 12;                   // bool
    string value_string     = 13;                   // string -- ⼀个字符串必须是UTF-8编码或者7-bit ASCII编码的⽂本。
    bytes value_bytes       = 14;                   // 可能包含任意顺序的字节数据

    repeated Phone phone    = 15;                   // repeated -- 数组
}

main.cpp

#include "Welsey.Struct.pb.h"
#include <string>
#include <iostream>

using namespace std;
    
bool ProtobufEncode(std::string &strPb)
{
    Welsey::Struct::Person person;
    person.set_value_double(1.1);
    person.set_value_float(1.2);
    person.set_value_int32(2);
    person.set_value_uint32(3);
    person.set_value_uint64(4);
    person.set_value_sint32(5);
    person.set_value_sint64(6);
    person.set_value_fixed32(7);
    person.set_value_fixed64(8);
    person.set_value_sfixed32(9);
    person.set_value_sfixed64(10);
    person.set_value_bool(true);
    person.set_value_string(string("welsey\01", 8));
    person.set_value_bytes(string("welsey\02", 8));

    Welsey::Struct::Phone *phone = person.add_phone();
    phone->set_number("123456");
    phone->set_type(Welsey::Struct::HOME);

    phone = person.add_phone();
    phone->set_number("654321");
    phone->set_type(Welsey::Struct::MOBILE);

    /**
     * @brief 进行序列化
     */
    size_t pbSize = person.ByteSizeLong();        // 获取序列化后的大小    
    strPb.clear();
    strPb.resize(pbSize);
    uint8_t *szData = (uint8_t *)strPb.c_str();

    // 将person序列化后的数据存储在szData
    if (!person.SerializeToArray(szData, pbSize))   // 拷贝序列化后的数据
    {
        std::cout << "person pb msg SerializeToArray failed." << std::endl;
        return false;
    }
    return true;
}

static void printPerson(Welsey::Struct::Person &person)
{
    std::cout << "double:\t" << person.value_double() << std::endl;
    std::cout << "float:\t" << person.value_float() << std::endl;
    std::cout << "int32:\t" << person.value_int32() << std::endl;
    std::cout << "uint32:\t" << person.value_uint32() << std::endl;
    std::cout << "uint64:\t" << person.value_uint64() << std::endl;
    std::cout << "sint32:\t" << person.value_sint32() << std::endl;
    std::cout << "sint64:\t" << person.value_sint64() << std::endl;
    std::cout << "fixed32:\t" << person.value_fixed32() << std::endl;
    std::cout << "fixed64:\t" << person.value_fixed64() << std::endl;
    std::cout << "sfixed32:\t" << person.value_sfixed32() << std::endl;
    std::cout << "sfixed64:\t" << person.value_sfixed64() << std::endl;
    std::cout << "bool:\t" << person.value_bool() << std::endl;
    std::cout << "string:\t" << person.value_string() <<"\t" << person.value_bytes().size() << std::endl;
    std::cout << "bytes:\t" << person.value_bytes() <<"\t" << person.value_bytes().size() << std::endl;

    for (int i = 0; i < person.phone_size(); i++)
    {
        const Welsey::Struct::Phone &phone = person.phone(i);
        std::cout << "phone num:\t" << phone.number()<<"\t"<<phone.type()<< std::endl;
    }
}

bool ProtobufDecode(std::string &strPb)
{
    Welsey::Struct::Person person;
    person.ParseFromArray(strPb.c_str(), strPb.size()); // 反序列化

    printPerson(person);

    return true;
}

int main(void)
{
    std::string strPb;

    ProtobufEncode(strPb);  // 序列化后是二进制

    ProtobufDecode(strPb);  
    return 0;
}

makefile

all : target

target : Welsey.Struct.proto main.cpp
	protoc -I=./ --cpp_out=./ ./*.proto				# --cpp_out 生成protobuf与cpp相关的文件
	protoc -I=./ --java_out=./ ./*.proto			# --java_out 生成protobuf与java相关的文件
	protoc -I=./ --python_out=./ ./*.proto			# --python_out 生成protobuf与python相关的文件
	g++ -std=c++11 -o target main.cpp Welsey.Struct.pb.cc -lprotobuf -lpthread -L/usr/local/protobuf/lib
posted @ 2022-05-20 11:57  呵哈呵  阅读(35)  评论(0)    收藏  举报