Linux C/C++ 学习日记(69):grpc(二):.proto文件的书写
注:该文用于个人学习记录和知识交流,如有不足,欢迎指点。
一、核心结构概览
一个标准的 gRPC .proto 文件通常包含以下部分:
// 1. 版本声明(必须放在第一行)
syntax = "proto3";
// 2. 包名(可选,用于避免命名冲突,对应 C++ 的 namespace)
package im.login;
// 3. 选项配置(可选,控制不同语言的生成代码)
option cc_generic_services = false; // C++ 专用:关闭旧版服务生成,推荐 false
option go_package = "./imlogin"; // Go 专用:生成路径
option java_package = "com.im.login"; // Java 专用:包名
// 4. 消息定义(Request/Response 数据结构)
message LoginRequest { ... }
message LoginResponse { ... }
// 5. 服务定义(gRPC 特有:定义 RPC 接口)
service LoginService {
rpc Login(LoginRequest) returns (LoginResponse);
}
二、详细语法说明
1. 版本与包名
syntax = "proto3";:必须写在文件第一行。指定使用 proto3 语法(gRPC 推荐,比 proto2 更简洁)。package xxx;:对应 C++ 中的命名空间(namespace)。例如package im.login;生成的 C++ 类会在im::login命名空间下。(一般取文件名)
2. 消息定义 (Message)
用于定义请求和响应的数据结构。
基本规则:
- 字段编号从
1开始,不能重复。 - 编号
1到15编码效率最高(占 1 字节),常用字段尽量用这个区间。 - 保留编号
19000到19999给 Protobuf 内部使用,不要用。
常用字段类型:
| Protobuf 类型 | C++ 类型 | 说明 |
|---|---|---|
string | std::string | 字符串 |
int32 | int32_t | 32 位整数 |
int64 | int64_t | 64 位整数 |
bool | bool | 布尔值 |
bytes | std::string | 二进制数据 |
repeated T | std::vector<T> | 数组 / 列表 |
示例:
// 登录请求
message LoginRequest {
string username = 1; // 用户名
string password = 2; // 密码
int32 platform = 3; // 平台:1-IOS, 2-Android, 3-WEB
}
// 登录响应
message LoginResponse {
int32 code = 1; // 错误码:0-成功
string msg = 2; // 错误信息
string token = 3; // 登录成功后的 token
repeated string permissions = 4; // 权限列表(数组)
}
3. 服务定义 (Service)
这是 gRPC 特有的部分,用于定义 RPC 接口。
四种 RPC 类型:
1. Unary RPC (一元 RPC):
最简单的模式,客户端发一个请求,服务端回一个响应。
一元 RPC 模式也被称为简单 RPC 模式。在该模式中,当客户端调用服务器端的远程方法时,客户端发送请求至服务器端并获得一个响应,与响应一起发送的还有状态细节以及 trailer 元数据。

rpc Login(LoginRequest) returns (LoginResponse);
2. Server Streaming (服务端流RPC模式):
客户端发一个请求,服务端返回一连串响应(比如服务器主动推送消息)。
在一元 RPC 模式中,gRPC 服务器端和 gRPC 客户端在通信时始终只有一个请求和一个响应。在服务器端流 RPC 模式中,服务器端在接收到客户端的请求消息后,会发回一个响应的序列。这种多个响应所组成的序列也被称为“流”。在将所有的服务器端响应发送完毕之后,服务器端会以 trailer 元数据的形式将其状态发送给客户端,从而标记流的结束

rpc SubscribeMessage(SubscribeReq) returns (stream Message);
3. Client Streaming (客户端流RPC模式):
客户端发一连串请求,服务端最后回一个响应。
在客户端流 RPC 模式中,客户端会发送多个请求给服务器端,而不再是单个请求。服务器端则会发送一个响应给客户端。但是,服务器端不一定要等到从客户端接收到所有消息后才发送响应。基于这样的逻辑,我们可以在接收到流中的一条消息或几条消息之后就发送响应,也可以在读取完流中的所有消息之后再发送响应

rpc UploadLog(stream LogEntry) returns (UploadResp);
4. Bidirectional Streaming (双向流RPC模式):
双方都可以自由收发消息(比如聊天室)。
在双向流 RPC 模式中,客户端以消息流的形式发送请求到服务器端,服务器端也以消息流的形式进行响应。调用必须由客户端发起,但在此之后,通信完全基于 gRPC 客户端和服务器端的应用程序逻辑。

rpc Chat(stream ChatMsg) returns (stream ChatMsg)
三、proto中引入其它proto文件
1. 编写被导入的基础文件 (im_common.proto)
这个文件定义通用的枚举、消息,供其他模块复用。
// proto/im_common.proto
syntax = "proto3";
// 【核心】定义 proto 包名,直接对应 C++ 的 namespace
package im.common;
// 关闭旧版服务生成(gRPC 推荐)
option cc_generic_services = false;
// 通用错误码枚举
enum ErrorCode {
SUCCESS = 0;
UNKNOWN = 1;
AUTH_FAILED = 2;
}
// 通用用户信息(会被 login/chat 等模块复用)
message UserInfo {
string user_id = 1;
string nickname = 2;
string avatar_url = 3;
}
2. 编写主文件并 import (im_login.proto)
在主文件中导入基础文件,并使用其定义的类型。
// proto/im_login.proto
syntax = "proto3";
// 【核心】主文件的包名,对应独立的 C++ namespace
package im.login;
option cc_generic_services = false;
// 【核心】导入基础文件
// 注意:路径是相对于 CMakeLists.txt 中 -I 参数的路径
import "im_common.proto";
// ---------------- 登录模块的请求/响应 ----------------
message LoginReq {
string username = 1;
string password = 2;
}
message LoginResp {
// 【核心】使用被导入文件中的类型:包名.类型名
im.common.ErrorCode code = 1;
string msg = 2;
string token = 3;
// 使用被导入的 UserInfo
im.common.UserInfo user_info = 4;
}
// ---------------- 服务定义 ----------------
service LoginService {
rpc Login(LoginReq) returns (LoginResp);
}
3. 核心对应关系:Proto Package ↔ C++ Namespace
这是最关键的映射规则:.proto 中的 package 直接对应 C++ 中的命名空间(namespace),层级用 :: 分隔。
| 位置 | Proto 代码 (package) | 对应 C++ 代码 (namespace) |
|---|---|---|
| 基础文件 | package im.common; | namespace im { namespace common { ... } } 或简写 im::common |
| 主文件 | package im.login; | namespace im { namespace login { ... } } 或简写 im::login |
更多推荐




所有评论(0)