注:该文用于个人学习记录和知识交流,如有不足,欢迎指点。

一、核心结构概览

一个标准的 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 开始,不能重复
  • 编号 115 编码效率最高(占 1 字节),常用字段尽量用这个区间。
  • 保留编号 1900019999 给 Protobuf 内部使用,不要用。

常用字段类型:

Protobuf 类型C++ 类型说明
stringstd::string字符串
int32int32_t32 位整数
int64int64_t64 位整数
boolbool布尔值
bytesstd::string二进制数据
repeated Tstd::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
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐