Skip to content

ProtoBuf协议入门指南

ProtoBuf协议入门指南

1 简介

服务器与客户端通信,必须有协议,否则双方无法理解对方的码流。在protobuf中,协议是由一系列的消息组成的。因此最重要的就是定义通信时使用到的消息格式。 在制定协议时,不是一气呵成的,是一个完善和优化的过程。首先,需要了解定义的格式与命名的规则,接下来根据自己所做的小模块(如登录模块)所需要的数据进行消息的定义,在程序的完成过程中可能会需要其他通信数据,此时可以添加字段。因此在项目启动的初期不必拘泥于协议的完整,只需大致制定出一个协议,在项目进行的过程中不断完善。

2 协议的定义格式

来看一个简单的例子。定义一个“下注请求”的消息格式,客户端请求有用户ID、下注详情、下注类型,服务端回复有返回码、描述、剩余筹码、一组牌型。可采用如下的方式来定义消息类型的.proto文件了: //注释采用双斜杠

//自定义下注信息 
message BetItem
{
    optional uint32    lineNumber      = 1; // 线条数
    optional uint32    lineBet         = 2; // 每条线的赌注
}
//客户端发送的请求内容
message BetRoomCmd_C
{
    required uint32    uid             = 1; // 用户ID
    optional BetItem   betReq          = 2; // 下注详情
    optional uint32    betType         = 3; // 下注类型 
}

//服务器端回复的内容
message BetRoomCmd_S
{
    required uint32    resultCode      = 1; // 返回码
    optional string    desc            = 2; // 描述
    optional uint32    remainderChips  = 3; // 剩余筹码
    repeated uint32    resultArray     = 5; // 一组牌型
}

protobuf消息定义格式: 至少一个字段组合而成,类似于C语言中的结构。 字段格式:限定修饰符① | 数据类型② | 字段名称③ | = | 字段编码值④ | [字段默认值⑤]
required uint32 uid = 1;
限定修饰符 数据类型 字段名称 编码值

2.1 限定修饰符 required \ optional \ repeated

required: 表示是一个必须字段,如上例,下注请求时必须发送用户ID。对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段。一个格式良好的消息一定要含有1个这种字段; optional:表示是一个可选字段,对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段; repeated:表示该字段可以包含0~N个元素。其特性和optional一样,但每一次可包含多个值,可看作是在传递一个数组,如上例中获取一组牌型。

2.2 数据类型

数据类型 描述
bool 布尔类型
double 64位浮点数
float 32为浮点数
int32 32位整数
uin32 无符号32位整数
int64 64位整数
uint64 64为无符号整
sint32 32位整数,处理负数效率更高
sing64 64位整数 处理负数效率更高
fixed32 32位无符号整数
fixed64 64位无符号整数
sfixed32 32位整数、以更高的效率处理负数
sfixed64 64为整数
string 只能处理 ASCII字符
bytes 用于处理多字节的语言字符、如中文
enum 可以包含一个用户自定义的枚举类型uint32
message 可以包含一个用户自定义的消息类型

message表示支持嵌套消息,消息可以包含另一个消息作为其字段。如上例:BetRoomCmd_C中optional BetItem betReq = 2;, BetItem为自定义的消息类型。

2.3 字段名称

协议字段命名需要遵循如下规则:
①平台服务器协议中的协议字段名称统一小写;
②游戏业务协议字段名称首单词小写,后续单词首字母大写;
③每个协议字段定义后,需要有精简的注释;
④尽可能让required类协议写在前面,同类描述字段相邻靠拢表示;(同类描述字段表示相同类型的描述字段,如:玩家的昵称、头像为一类;玩家的金币、积分为一类等)
⑤各字段之间,尽可能保证格式整齐。

2.4 字段编码值

有了该值,通信双方才能互相识别对方的字段。当然相同的编码值,其限定修饰符和数据类型必须相同。

2.5 默认值

当在传递数据时,对于required数据类型,如果用户没有设置值,则使用默认值传递到对端。当接受数据是,对于optional字段,如果没有接收到optional字段,则设置为默认值。

3 协议命名规则

3.1 规则

注:以下的规则针对的是本公司。 协议命名需要遵循如下规则: • 每个协议定义前,需要有精简的注释; • 协议命名中,每个单词或缩写单词,首字母需要大写; • 协议命名从左到右为:Proto关键字+业务描述+一级协议业务描述+平台通讯方式+上行/下行协议方式。 举例如下:

①Proto关键字:Proto对应的关键字,message。
②业务描述:业务的具体描述。
③一级协议业务描述:一级协议的业务描述。
④平台通讯方式类型:
- 平台服务器与客户端之间通讯,对应值为"Pmd";
- 区服务器服与平台服务器之间通讯,对应值为"Smd";
- 客户端与服务器之间通讯,对应值为"Cmd";
⑤上行/下行协议方式:协议分为上行协议与下行协议。
- 上行协议即请求协议,对应值为"_C"。
- 下行协议即回复协议,对应值为"_S"。
- 上行协议与下行协议,对应值为"_CS"。

3.2 实例

// 请求注册帐号

message RequestAccountRegisterLoginUserCmd_C 
{
     required string account      = 1; // 账号
     required string password     = 2; // 账号ID 
     optional string code            = 3; // 验证码
} 

// 返回注册帐号

 message ReturnAccountRegisterLoginUserCmd_S 
{ 
required string account    = 1; // 账号 
r  equired uint64 accountid   = 2; // 账号ID 
}