proto文件应该如何撰写

什么是.pb.h 和 .pb.cc 文件?

protobuf的核心是一个.proto文件,我们自定义一个.proto来创建我们的协议数据,然后使用protocol buffer 编译器工具编译生成两个"文件名.pb.cc""文件名.pb.h"的文件。

Protocol Buffers,是Google公司开发的一种数据格式,类似于XML和json,是一种用于数据传输时将数据序列化和反序列化的一个跨平台(支持目前主流的各种语言)工具库,可用于数据存储、通信协议等方面。它不依赖于语言和平台并且可扩展性极强。

作用:

  • Protobuf支持多种编程语言,如Java、C++、Python等。你可以使用protobuf编译器将.proto文件编译成对应语言的源代码,从而实现跨平台、跨语言的数据交换。
  • 生成的代码提供了序列化和反序列化的功能,可以很容易地将数据结构转换为字节流,或将字节流转换为数据结构。
  • 使用protobuf编译器从.proto文件生成代码,可以省去手动编写序列化和反序列化代码的繁琐工作,减少出错的可能性。
  • 生成的代码通常包含数据结构的访问器(accessor)和修改器(mutator),使得操作数据结构更加方便。

优点

  1. 大家都懂:所有的设备或软件都可以使用同一种数据格式,就像大家都说同一种语言,沟通起来就方便了。
  2. 节省资源:protobuf使用了一种紧凑的二进制格式,所以传输的数据会更小,节省网络带宽,也节省存储空间。
  3. 容易修改:如果以后需要添加或修改数据字段,你只需要修改.proto文件,然后重新“翻译”一下就可以了,不需要改动已经写好的代码。
  4. 安全可靠:protobuf在序列化和反序列化的过程中,会对数据进行校验,确保数据的完整性和安全性。

        总结:使用protobuf并通过.proto文件定义数据协议,然后编译生成对应的源代码文件,可以带来高效性、灵活性、跨平台性、跨语言支持、自动生成代码、兼容性、可扩展性以及减少网络带宽和存储空间消耗等好处。这些优点使得protobuf在分布式系统、网络通信、数据存储等领域得到了广泛的应用。并且Protobuf支持向后兼容性和向前兼容性。向后兼容性意味着新版本的程序可以读取旧版本的数据;向前兼容性意味着旧版本的程序可以选择性地忽略新版本数据中的新增字段。

        通俗来说:想象一下,你正在开发一个应用,这个应用需要在不同的设备或不同的软件之间传输数据。这些数据可能包含用户的信息、订单详情、图片描述等等。但是,每台设备或每种软件都有它自己的方式来理解和存储这些数据,这就好像大家都说不同的语言,无法直接沟通。现在,你希望有一种方式,能让所有的设备或软件都理解并使用同一种数据格式。这就是protobuf的作用了。.proto文件就是protobuf的“蓝图”或“模板”。你可以在这个文件里,用简单明了的语法定义你的数据结构,比如一个用户信息可能包括姓名、年龄、地址等字段。然后,你可以使用protobuf的编译器(就像是一个翻译官)将这个.proto文件“翻译”成各种编程语言都可以理解的代码(比如C++的.pb.cc.pb.h文件)。这些生成的代码就像是一个“说明书”,它告诉每种语言的程序,如何理解这个数据结构,如何把这个数据结构转换成可以传输的数据(这个过程叫做序列化),以及如何把接收到的数据转换回原来的数据结构(这个过程叫做反序列化)。

什么时候需要用到proto文件?

        当需要在不同平台、不同编程语言之间进行数据交换、网络通信、数据持久化、微服务通信或游戏开发中的数据同步时,都可以使用.proto文件来定义数据结构和服务接口。

  1. 跨平台/跨语言的数据交换
    当你需要在不同的操作系统(如Windows、Linux、macOS)或不同的编程语言(如Java、C++、Python、Go等)之间进行数据交换时,使用.proto文件定义数据结构可以确保数据的一致性和可理解性。通过protobuf编译器,你可以将.proto文件编译成各种语言对应的代码,从而方便地在这些平台或语言之间传输和解析数据。

  2. 网络通信中的数据序列化和反序列化
    在网络通信中,数据通常需要在不同的系统或设备之间进行传输。为了节省网络带宽和存储空间,数据需要进行高效的序列化(即将数据结构转换为字节流)和反序列化(即将字节流转换回数据结构)。使用.proto文件定义数据结构,并通过protobuf库进行序列化和反序列化,可以确保数据的高效传输和正确性。

  3. 存储持久化数据
    当需要将数据持久化到文件或数据库中时,使用.proto文件定义的数据结构可以确保数据的规范性和可读性。通过protobuf编译器生成的代码,你可以方便地将数据结构转换为字节流并保存到文件或数据库中,也可以从文件或数据库中读取字节流并转换回数据结构。

  4. 微服务或分布式系统中的RPC通信
    在微服务架构或分布式系统中,服务之间的通信通常通过远程过程调用(RPC)来实现。使用.proto文件定义服务接口和数据结构,可以确保服务之间的通信协议的一致性和可维护性。通过protobuf编译器生成的代码,你可以轻松地实现服务的调用和被调用,实现高效的跨服务通信。

  5. 游戏开发中的数据同步
    在游戏开发中,经常需要在客户端和服务器之间同步游戏状态和数据。使用.proto文件定义游戏数据和状态的结构,可以确保数据在传输过程中的一致性和可靠性。同时,protobuf的紧凑二进制格式可以减少网络带宽的占用,提高游戏的性能和响应速度。

.proto文件撰写模板

编写 Protocol Buffers(protobuf)的 .proto 文件时,通常遵循以下步骤和最佳实践:

1.指定语法版本:
    首先,指定你正在使用的 protobuf 语法版本。目前主要有 proto2 和 proto3 两种版本。

syntax = "proto3"; // 或者 "proto2"

2.定义包名:
为你的 .proto 文件定义一个包名,这有助于防止不同项目中的消息类型名称冲突。

package your.package.name;

3.导入其他 .proto 文件:
如果你的 .proto 文件需要使用在其他 .proto 文件中定义的消息类型,你需要使用 import 语句导入它们。

import "other_protos/other.proto";

4.定义消息类型:
在 .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 phones = 4;  

}

5.定义字段规则:
在 proto3 中,字段规则只有 required(已被移除)、optional(也被移除,所有字段默认为可选)和 repeated(表示字段可以重复,即数组)。但在 proto3 中,通常不需要显式指定 optional,因为所有字段默认就是可选的。

在 proto2 中,你需要明确指定字段是 required、optional 还是 repeated。但请注意,required 字段在 proto3 中已被移除。

6.定义服务(可选):
如果你的 .proto 文件用于定义 gRPC 服务,你可以在其中定义服务及其方法。

service Greeter {  
    rpc SayHello (HelloRequest) returns (HelloReply) {}  

message HelloRequest {  
    string name = 1;  

message HelloReply {  
    string message = 1;  

}

7.使用保留字段:
如果你删除了某个字段,并且之后可能会重用该字段的标识符,你应该使用 reserved 关键字来保留该标识符,以确保不会在未来发生字段标识符冲突。

message Foo {  
    reserved 2, 15, 9 to 11;  
    reserved "foo", "bar";  
 
    int32 baz = 1;  

}

8.注释:
使用 // 添加单行注释,或使用 /* ... */ 添加多行注释。

9.编译 .proto 文件:
使用 Protocol Buffers 编译器 protoc 将 .proto 文件编译成你需要的编程语言的源代码文件。

//bash

protoc --python_out=. your_file.proto

上面的命令将 your_file.proto 编译成 Python 代码。根据你的需要,你可以使用不同的插件(如 --cpp_out=、--java_out= 等)来生成不同编程语言的代码。

测试和验证:
在将生成的代码集成到你的项目中之前,确保通过编写单元测试和集成测试来验证 .proto 文件的定义和生成的代码的正确性。

例子:

syntax = "proto2";

package InterVariable;
//这定义了一个名为 InterVariable 的包。在生成的 C++ 代码中,所有消息类都将被放置在这个命名空间内。

message VariableList{
required string name = 1;
required int32 row = 2;
required int32 col = 3;
required int32 type = 4;
repeated string value = 5;//把变量的值都用string类型存储起来,使用stringstream将string类型转换成需要的类型
}
message VariableResquest{
required int32 option = 1;//0 -> exit  1 -> modify 2-> select 3 -> modify + select
repeated VariableList variable_resquest_modify = 2;
repeated VariableList variable_resquest_select = 3;
}


message VariableResponse{
optional string res = 1;//如果是修改的请求,用res返回“Successful!”或者“Failed!"给客户端
repeated VariableList variable_response = 2;//如果是查询请求,返回的select的信息即查询的所有变量组成的链表给客户端
}

消息类型:VariableResquest

  • option:一个必需的32位整数字段,用于表示请求的选项。注释中说明了该字段的可能值及其含义(0 表示退出,1 表示修改,2 表示选择,3 表示修改和选择)。
  • variable_resquest_modify:一个可重复的 VariableList 字段,用于包含需要修改的变量列表。
  • variable_resquest_select:一个可重复的 VariableList 字段,用于包含需要选择的变量列表。

消息类型:VariableResponse

  • res:一个可选的字符串字段,用于在修改请求时返回成功或失败的消息。
  • variable_response:一个可重复的 VariableList 字段,用于在查询请求时返回查询到的变量列表。

总结:

        定义了一个用于处理变量请求和响应的消息结构。VariableList 消息用于表示一个变量,包括其名称、位置(行和列)、类型以及值。VariableResquest 消息用于表示一个请求,包括请求的类型(修改、选择等)以及相关的变量列表。VariableResponse 消息用于表示对请求的响应,包括响应消息和(在查询请求时)返回的变量列表。

相关解释和说明

在 Protocol Buffers(protobuf)中,字段的修饰符决定了字段的特性。在 proto3 语法中,required 已经被移除,而 optional 字段也没有明确的修饰符(因为所有字段默认都是可选的)。但在 proto2 语法中,这三个修饰符都存在,并具有以下含义:

  1. required(仅在 proto2 中):
    required 字段表示该字段在序列化消息时必须存在,并且在解析消息时也必须存在。如果消息缺少 required 字段,解析将失败并抛出异常。由于这个严格的要求,required 字段在 proto3 语法中被移除了,因为 proto3 的设计更偏向于简单性和向后兼容性。

  2. optional(在 proto2 中,但在 proto3 中默认):
    optional 字段表示该字段在序列化消息时可以存在,也可以不存在。在解析消息时,如果该字段不存在,它的值将被设置为默认值(对于基本类型,默认值通常是零或空字符串)。在 proto3 语法中,所有字段默认都是 optional 的,即使没有显式指定。

  3. repeated
    repeated 字段表示该字段可以包含任意数量的元素(包括零个)。在序列化消息时,这些元素会被重复地写入。在解析消息时,你可以通过迭代这个字段来获取所有的元素。repeated 字段常用于表示数组或列表。

三种修饰符的使用场景示例:

1.required

使用场景:当某个字段对于消息来说是必不可少的,如果缺少这个字段,消息就不完整或无效。

例子:假设你正在设计一个表示“人”的消息格式,其中“名字”是一个必须存在的字段。

message Person {  
    required string name = 1; // 名字是必须的  
    int32 age = 2; // 年龄是可选的  }

在这个例子中,如果序列化一个 Person 消息时没有包含 name 字段,那么序列化将失败。同样,如果尝试解析一个不包含 name 字段的 Person 消息,解析也会失败。

2.optional

使用场景:当某个字段对于消息来说是可选的,即使缺少这个字段,消息仍然是完整和有效的。

例子:在上面的 Person 消息中,age 字段是可选的。这意味着你可以序列化一个只有 name 字段的 Person 消息,而不包含 age 字段。

proto3 中,由于所有字段默认都是 optional 的,所以你不需要显式地使用 optional 修饰符。

3.repeated

使用场景:当某个字段可以包含多个值时,例如一个人的多个电话号码或一个项目的多个任务。

例子:假设你正在设计一个表示“联系人”的消息格式,其中一个人可以有多个电话号码。

message PhoneNumber {  
    string number = 1;  
    enum Type {  
        HOME = 0;  
        WORK = 1;  
        MOBILE = 2;  
    }  
    Type type = 2;  
}  
 
message Person {  
    required string name = 1;  
    repeated PhoneNumber phones = 2; // 一个人可以有多个电话号码  
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值