首页 > 其他分享 >ProtoBuf简介

ProtoBuf简介

时间:2023-05-26 14:25:17浏览次数:44  
标签:set ProtoBuf proto 简介 name 类型 message protobuf

proto简介

一、protobuf的定义

protobuf是一种用于序列化结构数据的工具,实现数据的存储与交换,与编程语言和开发平台无关。

序列化:将结构数据或者对象转换成能够用于存储和传输的格式。

反序列化:在其他的计算环境中,将序列化后的数据还原为结构数据和对象。

定义数据的结构,然后使用protoc编译器生成源代码,在各种数据流中使用各种语言进行编写和读取结构数据。甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。

二、protobuf的优缺点

2.1、优点

  • 性能高效:与XML相比,protobuf更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
  • 语言无关、平台无关:protobuf支持Java、C++、Python 等多种语言,支持多个平台。
  • 扩展性、兼容性强:只需要使用protobuf对结构数据进行一次描述,即可从各种数据流中读取结构数据,更新数据结构时不会破坏原有的程序。

2.2、缺点

  • 不适合用来对基于文本的标记文档(如 HTML)建模。
  • 自解释性较差,数据存储格式为二进制,需要通过proto文件才能了解到内部的数据结构。

三、protobuf的使用流程

3.1、protobuf在Linux下的安装过程

$ sudo apt-get install autoconf automake libtool curl make g++ unzip
$ git clone https://github.com/google/protobuf.git
$ cd protobuf
$ git submodule update --init --recursive
$ ./autogen.sh
$ ./configure
$ make
$ make check
$ sudo make install
$ sudo ldconfig

3.2、定义proto文件

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;

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

  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }
  
  repeated PhoneNumber phone = 4;
}

message是消息体,包含了多个fields(数据项),每一个fields都是key-value类型。

3.3、protoc编译器

使用proto文件定义好结构数据后,可以使用protoc编译器生成结构数据的源代码,这些源代码提供了读写结构数据的接口,从而能够构造、初始化、读取、序列化、反序列化结构数据。使用以下命令生成相应的接口代码:

// $SRC_DIR: .proto所在的源目录
// --cpp_out: 生成C++代码
// $DST_DIR: 生成代码的目标目录
// xxx.proto: 要针对哪个proto文件生成接口代码

protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/xxx.proto

编译完成后将会生成一个xxx.pb.h和xxx.pb.cpp文件,会提供类似SerializeToOstream()、set_name()、name()等方法。

3.4、调用接口进行序列化、反序列化

/*
下面的代码即为protoc编译器生成的原结构数据的接口,
提供了构造函数、初始化、序列化、反序列化和读取数据的方法,
因此可以调用这些接口进行序列化与反序列化。
*/

// 构造函数
Person person;
// 初始化
person.set_name("John Doe");
person.set_id(1234);
person.set_email("[email protected]");
fstream output("myfile", ios::out | ios::binary);
// 序列化结构数据到文件中
person.SerializeToOstream(&output);

fstream input("myfile", ios::in | ios::binary);
Person person;
// 从文件中反序列化出结构数据
person.ParseFromIstream(&input);
// 读取结构数据
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;

四、protobuf的应用场景

  • 压缩效率高:服务器间的海量数据传输与通信,可以节省磁盘和带宽,protobuf适合处理大数据集中的单个小消息,但并不适合处理单个的大消息。
  • 解析速度快:可以提高服务器的吞吐能力。

五、protobuf与json和XML的对比

  • XML、JSON、protobuf都具有数据结构化和数据序列化的能力
  • XML、JSON更注重数据结构化,关注可读性和语义表达能力;protobuf 更注重数据序列化,关注效率、空间、速度,可读性较差,语义表达能力不足。
  • protobuf的应用场景更为明确,XML、JSON的应用场景更为丰富。

参考:
https://www.iteye.com/blog/maoyidao-1236916
https://www.jianshu.com/p/cae40f8faf1e
https://www.jianshu.com/p/a24c88c0526a

protobuf语法详解

一、包(package)

为.proto文件添加package声明符,可以防止不同 .proto项目间消息类型的命名发生冲突

package foo.bar;
message Open { ... }

message Foo {
  ...
  foo.bar.Open open = 1;
  ...
}

protobuf包类型的解析和C++类似,都是由内而外进行解析。对于C++,产生的类会被包装在C++的命名空间中,如上例中的Open会被封装在 foo::bar空间中。

二、选项(option)

option会影响特定环境下的处理方式,但是不会改变整个文件声明的含义

option optimize_for = CODE_SIZE;
1

三、消息类型(message)

message用于定义结构数据,可以包含多种类型字段(field),每个字段声明以分号结尾。message经过protoc编译后会生成对应的class类,field则会生成对应的方法

syntax = "proto3"; // 表示使用的protobuf版本是proto3

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
1234567

3.1、常规消息类型

3.1.1、字段修饰符

在proto3中,去掉了required和optional,对于原始数据类型字段不再提供 hasxxx()方法,只有单个字段或者重复字段:

  • 单个字段:表示字段可以出现0次或者1次。
  • repeated:表示字段可以重复任意次。

3.1.2、字段类型

3.1.2.1、标量类型

protobuf标量数据类型与各平台的数据类型对应如下表:

3.1.2.2、枚举类型

protobuf中的enum类型和C++中的枚举类型相似,表示字段取值的集合

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}

枚举类型中第一个元素的值必须从0开始,而且proto3中删除了default标记,默认值为第一个元素

当枚举类型是在某一个消息内部定义,但是希望在另一个消息中使用时,需要采用MessageType.EnumType的语法格式。

3.1.2.3、Any类型

protobuf中的Any类型与C++中的泛型概念类似,可以定义为任意的类型。在序列化的时候可以通过PackFrom()方法将任意的数据类型打包为Any类型,反序列化的时候通过UnpackTo()把Any类型还原为原始类型。

// 要使用Any类型必须导入该proto文件
import "google/protobuf/any.proto";

message ErrorStatus {
  string message = 1;
  repeated google.protobuf.Any details = 2;
}

// Storing an arbitrary message type in Any.
NetworkErrorDetails details = ...;
ErrorStatus status;
status.add_details()->PackFrom(details);

// Reading an arbitrary message from Any.
ErrorStatus status = ...;
for (const Any& detail : status.details()) {
  if (detail.Is<NetworkErrorDetails>()) {
    NetworkErrorDetails network_error;
    detail.UnpackTo(&network_error);
    ... processing network_error ...
  }
}

3.1.2.4、oneof类型

protobuf中的oneof类似与C++中的联合体类型相似,所有的字段共享内存,最多只能同时设置一个字段,设置oneof的任何字段会自动清除所有其他字段,可以使用case()或WhichOneof()方法检查oneof中使用的是哪个字段。

message SampleMessage {
  oneof test_oneof {
    string name = 4;
    SubMessage sub_message = 9;
  }
}

【oneof特性】:

  • 设置oneof会自动清除其它oneof字段的值:
SampleMessage message;
message.set_name("name");
CHECK(message.has_name());
message.mutable_sub_message();   // Will clear name field.
CHECK(!message.has_name());
  • oneof不能声明为repeated类型。
  • 注意不要出现内存崩溃问题:
SampleMessage message;
SubMessage* sub_message = message.mutable_sub_message();
message.set_name("name");      // Will delete sub_message
sub_message->set_...            // Crashes here
  • 可以在oneof内部添加和删除field,但是删除和添加oneof要小心

3.1.2.5、map类型

protobuf中的map类似与STL中的关联型容器相似,map是key-value类型,key可以是int或者string,value可以是自定义message

map<key_type, value_type> map_field = N;

// 与上述定义等价
message MapFieldEntry {
  key_type key = 1;
  value_type value = 2;
}
repeated MapFieldEntry map_field = N;

【map特性】:

  • map不能定义为repeated类型。
  • 当为.proto文件产生生成文本格式的时候,map会按照key 的顺序排序,数值化的key会按照数值排序。
  • 从序列化中解析时,如果有重复的key,只会使用第一个key。

3.1.3、默认值说明

  • string类型,默认值是空字符串。
  • bytes类型,默认值是空bytes。
  • bool类型,默认值是false。
  • 数字类型,默认值是0。
  • 枚举类型,默认值是第一个枚举值,即0。
  • repeated修饰的属性,默认值是空。

3.1.4、标识号

在消息类型中,每一个字段都有一个唯一的标识符(Tag),不应该随意改动

[1-15]内的标识号在编码时只占用一个字节,包含标识符和字段类型,[16-2047]之间的标识符占用2个字节建议为频繁出现的字段使用[1-15]间的标识符

如果考虑到以后可能扩展元素,可以预留一些标识符或者字段。注意不能在一个reserved声明中混合使用字段名和标识符。

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
}

最小的标识符可以从1开始,最大到2^29 - 1,或536,870,911。不可以使用[19000-19999]之间的标识符, Protobuf协议实现中预留了这些标识符。在.proto文件中使用这些预留标识号,编译时就会报错。

3.2、多个消息类型

一个.proto文件中可以定义多个消息类型

syntax = "proto3";

// SearchRequest 搜索请求
message SearchRequest {
    string query = 1;           // 查询字符串
    int32  page_number = 2;     // 页码
    int32  result_per_page = 3; // 每页条数
}

// SearchResponse 搜索响应
message SearchResponse {
    ...
}

3.3、嵌套消息类型

在protobuf中message之前可以嵌套使用

message SearchResponse {
    message Result {
        string url = 1;
        string title = 2;
        repeated string snippets = 3;
    }
    repeated Result results = 1;
}

内部声明的消息message名称只可在内部直接使用,在外部使用需要添加父级message名称(Parent.Type):

message SomeOtherMessage {
    SearchResponse.Result result = 1;
}

支持多层嵌套:

message Outer {                // Level 0
    message MiddleAA {         // Level 1
        message Inner {        // Level 2
            int64 ival = 1;
            bool  booly = 2;
        }
    }
    message MiddleBB {         // Level 1
        message Inner {        // Level 2
            int32 ival = 1;
            bool  booly = 2;
        }
    }
}

3.4、更新消息类型

如果一个已有的消息类型已无法满足新的需求,比如需要添加一个额外的字段,但是同时旧版本写的代码仍然可用。在更新消息类型需要遵循以下规则:

  • 不要更改任何已有字段的标识号
  • int32、uint32、int64、uint64,和bool是全部兼容的,这意味着可以将这些类型中的一个转换为另外一个,而不会破坏向前、 向后的兼容性
  • sint32和sint64是互相兼容的,但是它们与其他整数类型不兼容。
  • string和bytes是兼容的,只要bytes是有效的UTF-8编码。
  • 嵌套消息与bytes是兼容的,只要bytes包含该消息的一个编码过的版本。
  • fixed32与sfixed32是兼容的,fixed64与sfixed64是兼容的。

四、RPC服务(service)

如果想要将消息类型用在远程方法调用(RPC)系统中,可以在.proto文件中定义一个RPC服务接口

service UserService {
    //  包含方法名、方法参数和返回值,
    // 接收SearchRequest并返回一个SearchResponse
    rpc GetUser(Request) returns (Response); 
}

gRPC在使用protobuf时非常有效,如果使用特殊的protobuf插件可以直接从.proto文件中产生相关的RPC代码。

五、其他

5.1 导入proto文件(import)

如果希望在当前proto文件中引用其他的proto文件中的内容,可以使用import:

import "other_project/other_protos.proto";

参考:
https://developers.google.cn/protocol-buffers/docs/proto3#oneof
https://segmentfault.com/a/1190000007917576#item-1-10
https://www.jianshu.com/p/e06ba6249edc

protobuf语法风格

一、代码风格

  • 每一行的代码长度不要超过80。
  • 使用两个空格进行缩进。

二、文件格式

文件命名应该采用蛇形命名法(即用下划线连接),如:lower_snake_case.proto。所有文件应以下列方式排列:

  1. License header (if applicable)
  2. File overview
  3. Syntax
  4. Package
  5. Imports (sorted)
  6. File options
  7. Everything else

三、包

包名应该是小写的,并且应该对应于目录层次结构。例如,如果一个文件位于my/Package/*中,那么包名应该是*my.Package

四、消息类型和字段

消息名使用驼峰命名法,例如:SongServerRequest,字段名和扩展名使用小写的下划线分隔式,例如:song_name。

message SongServerRequest {
  required string song_name = 1;
}
123
const string& song_name() { ... }
void set_song_name(const string& x) { ... }

如果字段名包含数字,则该数字应出现在字母之后,而不是下划线之后。例如:song_name1。

五、repeated字段

repeated字段使用复数命名

repeated string keys = 1;
repeated MyMessage accounts = 17;

六、枚举类型

枚举名使用使用驼峰命名法,成员使用大写的下划线分隔式:

enum Foo {
  FOO_UNSPECIFIED = 0;
  FOO_FIRST_VALUE = 1;
  FOO_SECOND_VALUE = 2;
}

七、服务

服务名称和任何RPC方法名称均使用驼峰命名法

service FooService {
  rpc GetSomething(FooRequest) returns (FooResponse);
}

参考:
https://developers.google.cn/protocol-buffers/docs/style

protobuf使用实例

一、描述proto文件

proto文件名称为addressbook.proto。

syntax = "proto3";
import "google/protobuf/any.proto";

// package类似于namespace,可以避免命名冲突
package AddressBookInfo;

// message类似于class
message Person
{
    string name = 1;
    int32 id = 2;
    string email = 3;
    
    // 枚举类型
    enum PhoneType
    {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
    }
    
    message PhoneNumber
    {
        string number = 1;
        // proto3中enum没有default选项,把第一个值作为default
        PhoneType type = 2;
    }
    
    // repeated表示message或者filed可以重复多次
    repeated PhoneNumber phones = 4;
}

message Address
{
	string address = 1;
}

message AddressBook
{
    string owner = 1;
    repeated Person person_infos = 2;

    /* 
    ** oneof类似于union类型,某一个时刻只能设置一个field,所有的field共享同一段内存。
    ** 设置oneof字段将自动清除oneof的所有其他字段,即只能同时设置(set_)一个,不然就会core dump。
    ** 可以在oneof内部添加和删除field,但是删除和添加oneof要小心。
    ** oneof中数据成员的编号建议承上启下,尽量不要随意编号。
    */
    oneof PayType
    {
        string type_ali = 3;
        string type_wx = 4;
    }

    /*
    ** map是key-value类型,key可以是int或者string,value可以是自定义message。
    ** Any用来实现泛型,可以表示任意类型。
    */
    map<string, google.protobuf.Any> owner_address = 5;
}

二、编译proto文件

使用protoc编译器对proto文件进行编译,生成addressbook.pb.haddressbook.pb.cc

三、生成protobuf API

打开addressbook.pb.haddressbook.pb.cc,可以看到自动生成了很多的API,后续可以使用这些API读写数据。




四、使用API写入和读取数据

addressbook.cpp文件中阐述了如何使用protobuf自动生成的API读写数据,基本上覆盖了常用的消息类型。

具体的API及使用方式可以参考:https://developers.google.cn/protocol-buffers/docs/reference/cpp-generated

#include <iostream>
#include <fstream>
#include "addressbook.pb.h"
#include <google/protobuf/any.h>

using namespace std;

void SaveInfo()
{
	AddressBookInfo::AddressBook adbook;

	// 使用set_设置message中filed的值,函数名都是小写(即使在proto中是大写)
	adbook.set_owner("eric");

	// Person1
	// 使用add_添加message,返回的是指针类型
	AddressBookInfo::Person *person1 = adbook.add_person_infos();
	person1->set_name("mark");
	person1->set_id(123456);
	person1->set_email("[email protected]");

	AddressBookInfo::Person::PhoneNumber *person1_phone = person1->add_phones();
	person1_phone->set_number("12345678");
	// 枚举类型中的元素名称是唯一的,所以可以直接用作用域限定符访问元素,不需要通过枚举名访问
	person1_phone->set_type(AddressBookInfo::Person::HOME);

	person1_phone = person1->add_phones();
	person1_phone->set_number("1234");
	person1_phone->set_type(AddressBookInfo::Person::WORK);

	// Person2
	AddressBookInfo::Person *person2 = adbook.add_person_infos();
	person2->set_name("mike");
	person2->set_id(654321);
	person2->set_email("[email protected]");

	AddressBookInfo::Person::PhoneNumber *person2_phone = person2->add_phones();
	person2_phone->set_number("87654321");
	person2_phone->set_type(AddressBookInfo::Person::HOME);

	person2_phone = person2->add_phones();
	person2_phone->set_number("5678");
	person2_phone->set_type(AddressBookInfo::Person::WORK);

	// map和any类型的初始化,mutable返回的是非const指针类型
	google::protobuf::Map<string, google::protobuf::Any> *owner_address = adbook.mutable_owner_address();
	// 定义一个Any类型,用于接收message
	google::protobuf::Any *any = new google::protobuf::Any;
	AddressBookInfo::Address adbook_address;
	adbook_address.set_address("HB");
	// 使用PackFrom将message类型存储为Any类型
	any->PackFrom(adbook_address);
	// map类型的初始化方式和STL中的map类似
	(*owner_address)[adbook.owner()] = *any;

	// 设置oneof中某一个成员的值,之后如果再使用set_则会core dump
	adbook.set_type_ali("AliPay");

	fstream output("address_book_file", ios::out | ios::trunc | ios::binary);
	// 使用SerializeToOstream将序列化后的数据写入文件中
	if (!adbook.SerializeToOstream(&output))
	{
		cerr << "Failed to write address book." << endl;
	}
}

void ShowMsg(const AddressBookInfo::AddressBook &adbook)
{
	cout << adbook.owner() << endl;
	// 对于重复message,_size表示有多少个重复message,使用索引取出每一个message
	for (int i = 0; i < adbook.person_infos_size(); ++i)
	{
		const AddressBookInfo::Person &person = adbook.person_infos(i);
		// 取出各个字段的值
		cout << person.name() << endl;
		cout << person.id() << endl;
		cout << person.email() << endl;
		for (int j = 0; j < person.phones_size(); ++j)
		{
			// 使用索引获取枚举类型的所有值
			const AddressBookInfo::Person::PhoneNumber &person_phone = person.phones(j);
			switch(person_phone.type()) 
			{
				case AddressBookInfo::Person::MOBILE :
					cout << "MOBILE phone #: ";
					break;
				case AddressBookInfo::Person::HOME :
					cout << "HOME phone #: ";
					break;
				case AddressBookInfo::Person::WORK :
					cout << "WORK phone #: ";
					break;
			}
			cout << person_phone.number() << endl;
		}
	}
	cout << adbook.type_ali() << endl;
}

void ShowMapMsg(const AddressBookInfo::AddressBook &adbook) 
{
	// 取出map类型的字段成员
	const google::protobuf::Map<string, google::protobuf::Any> &adbook_map = adbook.owner_address();
	// 使用迭代器访问map(和STL中的map类似)
	auto iter = adbook_map.begin();
	cout << iter->first << endl;
	google::protobuf::Any any = iter->second;
	AddressBookInfo::Address adbook_address;
	// 使用UnpackTo从Any类型解析出message,注意参数中有一个&
	if (any.UnpackTo(&adbook_address))
	{
		cout << adbook_address.address() << endl;
	}
	else
	{
		cout << "UnpackTo data error" << endl;
	}
}

void LoadInfo()
{
	AddressBookInfo::AddressBook adbook;
	fstream input("address_book_file", ios::in | ios::binary);
	// 使用ParseFromIstream从文件中反序列化
	if (!input)
	{
		cout << ": File not found.  Creating a new file." << endl;
	}
	else if (!adbook.ParseFromIstream(&input))
	{
		cout << "Failed to parse address book." << endl;
	}
	ShowMsg(adbook);
	cout << endl;
	ShowMapMsg(adbook);
}

int main(int argc, char const *argv[])
{
	SaveInfo();
	LoadInfo();
	// 删除所有已分配的内存(注意any是堆上的内存,清除内存的操作要小心)
	google::protobuf::ShutdownProtobufLibrary();
	return 0;
}

五、编译所有的CPP文件

使用g++编译所有的CPP文件,注意编译参数需要加上:-std=c++11、-lprotobuf、-lpthread

六、编译和运行中遇到的问题

(1)如果没有添加-std=c++11选项则会出现以下问题:

(2)针对oneof类型,如果同时设置了多个字段或者字段编号混乱,则会出现以下问题:

#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include <stdlib.h>

#include <iostream>
#include <string>
using namespace std;
#include "../proto/person.pb.h"

#if 0
int main(int argc, char** argv)
{
    //1.定义要传送数据的对象  对应message后跟的名字
    Person p;  
    //2.初始化对象
    p.set_name("大圣");
    p.set_email("[email protected]");
    p.set_id(1700);

    cout << p.name() << " " << p.id() << " " << p.email() << endl;
    //3.将这个数据对象序列化
    string out;
    p.SerializeToString(&out);

    //4.使用string对象里面存放的数据,反序列化数据对象
    Person monkey;
    monkey.ParseFromString(out);
    cout << monkey.name() << " " << monkey.id() << " " << monkey.email() << endl;
    system("Pause");
    return 0;
}
#else
//传递一个信息类型的字符串,那么这个工厂就会帮我们构造出一个对应的类的实例
//"Person"字符串-->Person的实例 -->返回的是一个基类Message类的指针
//基类指针 指向子类实例
//create_message -->delete 来删除对象实例
google::protobuf::Message* create_message(const char* typeName) {
    //message类
    google::protobuf::Message* message = NULL;

    // 根据名字,找到message的描述对象
    const google::protobuf::Descriptor* descriptor =
        google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(typeName);
    if (descriptor) {
        //根据描述对象到对象工厂里面,生成对应的模板对象
        const google::protobuf::Message* prototype =
            google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
        if (prototype) {
            //根据模板对象克隆一个
            message = prototype->New();
        }
    }
    return message;
}

int main(int argc, char** argv)
{
    //基类的message-->Person类的实例
    google::protobuf::Message* msg = create_message("Person");

    /*
    Person* hou = (Person*)msg;
    hou->set_name("大圣");
    cout << hou->name() << endl;
    */

    /*
        每个message对象包含两个对象:
        google::protobuf::Descriptor 描述对象-->获取message的描述信息 字段名以及类型等-->包含每一个field的描述
        google::protobuf::Reflection 反射对象--> 使用这个对象 + field描述, 能get/set每个field的值
    */

    const google::protobuf::Descriptor* des = msg->GetDescriptor();
    const google::protobuf::Reflection* ref = msg->GetReflection();

    //设置
    // name
    // 1. 通过des获取
    const google::protobuf::FieldDescriptor* fd_des = des->FindFieldByName("name"); //通过字段名字设置数据
    // 2.通过ref进行设置
    ref->SetString(msg, fd_des, "大圣");

    //age
    fd_des = des->FindFieldByNumber(2); //通过字段标识号设置数据
    ref->SetInt32(msg, fd_des, 1500);
    //email
    fd_des = des->FindFieldByName("email");
    ref->SetString(msg, fd_des, "[email protected]");
    //数组 使用AddInt32一个一个追加进去
    fd_des = des->FindFieldByName("set");
    ref->AddInt32(msg, fd_des, 1);
    ref->AddInt32(msg, fd_des, 2);
    ref->AddInt32(msg, fd_des, 3);
    ref->AddInt32(msg, fd_des, 4);

    //获取
    for (int i = 0; i < des->field_count(); i++)
    {
        //获取每一个field描述对象
        const google::protobuf::FieldDescriptor* fd = des->field(i);
        //(1) 获取名字
        cout << fd->name() << endl;     //字段名字 不是数据 如:name age
        cout << fd->type_name() << endl;     //类型
        cout << fd->cpp_type_name() << endl;     //c++类型 bytes被换成了string
        // 判断字段规则
        // fd->is_repeated() 是否是数组
        // fd->is_optional() 是否是非必须
        // fd->is_required() 是否是必须
        if (!fd->is_repeated()) {
            //获取数据
            switch (fd->cpp_type())
            {
            case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT32:
                cout << ref->GetInt32(*msg, fd) << endl;
                break;
            case google::protobuf::FieldDescriptor::CppType::CPPTYPE_STRING:
                cout << ref->GetString(*msg, fd) << endl;
                break;
            }
        }
        else {
            for (int j = 0; j < ref->FieldSize(*msg, fd); j++)  //ref->FieldSize(*msg, fd)获取数组长度
            {
                switch (fd->cpp_type())
                {
                case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT32:
                    cout << ref->GetRepeatedInt32(*msg, fd,j) << " ";  //使用GetRepeatedInt32 便利数组
                    break;
                case google::protobuf::FieldDescriptor::CppType::CPPTYPE_STRING:
                    cout << ref->GetRepeatedString(*msg, fd,j) << " ";
                    break;
                }
            }
            cout << endl;
        }
        
       

        cout << fd->number() << endl;        //获取字段标识号
    }
    //删除
    delete msg;
    system("Pause");
    return 0;
}
#endif

linux 安装 protoc

  • 环境 #54~20.04.1-Ubuntu SMP
  • protoc 安装

下载地址:https://github.com/protocolbuffers/protobuf/releases 找到自己需要的版本

这里下载版本:https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.9.2.tar.gz

cd /tmp
wget https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.9.2.tar.gz
tar -zxvf v3.9.2.tar.gz
cd /protobuf-3.9.
sudo ./autogen.sh
sudo ./configure
sudo make
sudo make install

确认是否安装

protoc --version

出现异常

protoc: error while loading shared libraries: libprotoc.so.20: cannot open shared object file: No such file or directory

查阅资料,解决办法

export LD_LIBRARY_PATH=/usr/local/lib

再次测试,命令正常

export LD_LIBRARY_PATH=/usr/local/lib 加到 /etc/profile 文件中,以免下次使用失效

将 protoc 命令放到 GOBIN 目录

which protoc
cp /usr/local/bin/protoc $GOBIN/
  • 安装 protoc-gen-go
go install github.com/golang/protobuf/[email protected]
  • 安装 protoc-gen-grpc-gateway
go install github.com/grpc-ecosystem/grpc-gateway/[email protected]
  • 安装 protoc-gen-swagger
go install github.com/grpc-ecosystem/grpc-gateway/[email protected]
  • 安装 protoc-gen-gofast
go install github.com/gogo/protobuf/protoc-gen-gofast@latest

标签:set,ProtoBuf,proto,简介,name,类型,message,protobuf
From: https://www.cnblogs.com/miaochuanjie/p/17434551.html

相关文章

  • ProtoBuf_Win编译安装
    ProtoBufWindows编译和安装1.先安装cmakeprotocolbuffer的编译需要安装cmake,可到https://cmake.org/下载并安装。2.下载ProtoBufprotobufv3.19.4开源链接:ReleaseProtocolBuffersv3.19.4·protocolbuffers/protobuf(github.com)3.解压之后,按照cmake文件夹中的rea......
  • Singleton 单例模式简介与 C# 示例【创建型】【设计模式来了】
     〇、简介1、什么是单例模式?一句话解释:  单一的类,只能自己来创建唯一的一个对象。单例模式(SingletonPattern)是日常开发中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时......
  • C语言——简介
    C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、仅产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。C语言描述问题比汇编语言迅速、工作量小、可读性好、易于调试、修改和移植,而代码质量与汇编语言相当。C语言一般只比汇编语言代码生成的目标程序......
  • npm简介
    npm(NodePackageManager)是Node.js的包管理器,它是随同Node.js一起安装的。npm允许开发者在自己的项目中方便地安装、更新、卸载和管理各种开源的Node.js模块。使用npm,你可以通过命令行界面(CLI)执行以下常用操作:1.安装包:通过`npminstall`命令安装指定的包。例如:npminstallpa......
  • C++ 简介
     C++是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。C++被认为是一种中级语言,它综合了高级语言和低级语言的特点。C++是由BjarneStroustrup于1979年在新泽西州美利山贝尔实验室开始设计开发的。C++进......
  • GB28181协议简介
    1、GB/T28181—2016协议简介近年来,国内视频监控应用发展迅猛,系统接入规模不断扩大,涌现了大量平台提供商,平台提供商的接入协议各不相同,终端制造商需要给每款终端维护提供各种不同平台的软件版本,造成了极大的资源浪费。各地视频大规模建设后,省级、国家级集中调阅,对重特大事件通过......
  • 《workflow跳远》activiti篇——activiti简介入门
    工作流为什么出现最初,开发人员开发一个流程,例如请假流程,员工提出请假申请——领导同意——财务人员记录,一般通过状态字段来跟踪流程变化,设zt=0为初始状态,zt=1为保存状态,zt=2提交到领导,zt=3提交到财务,zt=4财务人员记录完成。员工、领导、财务人员这些不同角色,通过状态字段的取值决......
  • DOS简介及常用命令汇总
    1.什么是DOSDOS是英文DiskOperatingSystem的缩写,即为磁盘操作系统,两者是完全一样的概念。 DOS还能有效地管理各种软硬件资源,对它们进行合理的调度,所有的软件和硬件都在DOS的监控和管理之下,有条不紊地进行着自己的工作。 在windows操作系统中,最常见的DOS是MS-DOS,它是个人......
  • (个人简介)Coding Home - 漂流瓶jz
    个人简介:华东师范大学计算机专业硕士。目前是Web前端开发。GitHub    jzplp(漂流瓶jz)·GitHub新浪博客   漂流瓶jz_新浪博客......
  • python的守护线程(简介、作用及代码实例)
    转载:(14条消息)python的守护线程(简介、作用及代码实例)_python守护线程的作用_HXH.py的博客-CSDN博客python守护线程简介守护线程的理解:如果当前python线程是守护线程,那么意味着这个线程是“不重要”的,“不重要”意味着如果他的主进程结束了但该守护线程没有运行完,守护进程就会被......