一、前言
在配置好protobuffer之后,简单学习protobuffer的基础使用
二、c#版基础使用
1、定义.proto
文件
syntax = "proto3";
package SocketGameProtocol;
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
字段 | 描述 |
---|---|
syntax | protobuf版本,目前已更新到proto3 |
package | 指定包名:默认包名 |
option java_package | 指定包名,实际包名。如果该字段有定义,那么就使用 java_package,没有的话就使用package作为包名,为了避免碰撞即使有该字段,也要定义package |
option java_outer_classname | 指定类名,编译后就会生成com.example.SocketGameProtocol.AddressBookProtos 类,如果没有定义该字段,默认使用文件名作为class名称,比如my_proto.proto 会使用MyProto 作为文件名 |
required string name | required 表示字段为必要字段。 |
optinal | 可选,没有该属性时,会被赋成默认值:string为"",int 为0; |
repeated | 数组,编译后的对象为phones=[187***,182**,…] |
enum | 枚举类型 |
=1、=2 | 标记在每一个属性上,作为唯一的tag,被用于二进制编码。范围是1~(2^29)-1,使用1-15时会使用少于一个byte标识,最常用的 |
注意:枚举初始化常量是0,每个枚举都必须包含一个常量,定义为0位第一个元素
必须有一个零值,所以用0作为数字默认值
2.编译
在bin文件夹中创建一个bat文件,内容如下:
@echo off
for %%i in (*.proto) do (
protoc --csharp_out=./ %%i
rem 从这里往下都是注释,可忽略
echo From %%i To %%~ni.cs Successfully!
)
pause
将proto文件也导入bin文件夹中,点击bat文件,即可自动生成SocketGameProtocol.cs
文件
3…proto与c#格式映射
.proto不同的字段定义会生成对应的api
// required string name = 1;
public boolean hasName();
public String getName();
// required int32 id = 2;
public boolean hasId();
public int getId();
// optional string email = 3;
public boolean hasEmail();
public String getEmail();
// repeated .tutorial.Person.PhoneNumber phones = 4; repeated生成list
public List<PhoneNumber> getPhonesList();
public int getPhonesCount();
public PhoneNumber getPhones(int index);
枚举类型的编译
public static enum PhoneType {
MOBILE(0, 0),
HOME(1, 1),
WORK(2, 2),
;
...
}
三、protobuffer的消息定义
消息由至少一个字段组合而成,相似于C语言中的结构。每一个字段都有必定的格式。编码字段格式:限定修饰符① | 数据类型② | 字段名称③ | = | 字段编码值④ | [字段默认值⑤]sp
- 限定修饰符包括 required\optional\repeated
- Required: 表示是一个必须字段,必须相对于发送方,在发送消息以前必须设置该字段的值,对于接收方,必须可以识别该字段的意思。发送以前没有设置required字段或者没法识别required字段都会引起编解码异常,致使消息被丢弃。htm
- Optional:表示是一个可选字段,可选对于发送方,在发送消息时,能够有选择性的设置或者不设置该字段的值。对于接收方,若是可以识别可选字段就进行相应的处理,若是没法识别,则忽略该字段,消息中的其它字段正常处理。—由于optional字段的特性,不少接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也能够正常的与新的软件进行通讯,只不过新的字段没法识别而已,由于并非每一个节点都须要新的功能,所以能够作到按需升级和平滑过渡。blog
- Repeated:表示该字段能够包含0~N个元素。其特性和optional同样,可是每一次能够包含多个值。能够看做是在传递一个数组的值。
- 数据类型
protobuf定义了一套基本数据类型,几乎均可以映射到c++等语言的基础数据类型
protobuf数据类型 | 描述 | 打包 |
---|---|---|
bool | 布尔类型 | 1字节 |
double | 64位浮点数 | N |
float | 32位浮点数 | N |
int32 | 32位整数 | N |
uin32 | 无符号32位整数 | N |
int64 | 64位无符号整数 | N |
string | 只能处理ASCII字符 | N |
bytes | 用于处理多字节的语言字符,如中文 | N |
enum | 能够包含一个用户自定义的枚举类型uint32 | N |
message | 能够包含一个用户自定义的消息类型 | N |
N表示打包的字节并非固定,而是根据数据的大小或者长度
默认值
当消息被解析时,如果编码的消息不包含特定的单个元素,则解析对象中的相应字段将被设置为该字段的默认值:
- 对于字符串,默认值为空字符串
- 对于字节,默认值为空字节
- 对于bools,默认值为flase
- 对于数值类型,默认值为0
- 对于枚举,默认值是第一个定义的枚举值,它必须为0
- 对于消息字段,该字段未设置。其确切的值取决于语言
- 重复字段的默认值为空
注意:当message属性被解析,就没办法明确设置值为默认值(例如布尔值是否设置为false,或者根本不设置)
另外,如果当message属性被解析设置为默认值,那么这个值就不会被序列化
四、proto3中使用proto2
可以导入proto2消息类型并在proto3消息中使用他们,反之亦然。但是proto2枚举不能直接在proto3语法中使用
更新message
- 不要更改任何现有字段的数字标签
- 如果添加新字段,则使用“旧”消息格式的代码序列化的任何消息仍然可以通过新生成的代码进行解析。
- 只要在更新的消息类型中不再使用标签号,就可以删除字段。需要重命名字段,也可以添加前缀"OBSOLETE_",或者使保留标记,以便.proto的未来用户不会意外重用该数字
五、游戏内使用
1、枚举四个RequestCode
,分别为RequestNode、UserRequest、NoticeRequest、GameRequest
,请求发送用户、公告、游戏战绩的相关内容。在游戏中前端发送请求,若返回ReturnCode
为.succeed
时,即请求成功后,会对应发送相关pack
2、枚举八个ActionCode
,分别为ActionNone、Register、Login、ReceiveNotice、SaveRecord、CheckMyInfo、CheckRank、CheckMyGameInfo
,对应着注册、登录、获取公告、纪录战绩、查询个人信息、查询排行榜、查询游戏信息,前端进行相关操作请求后端发送时,则请求相对应的ActionCode进行相关请求
3、枚举三个ReturnCode
,分别为ReturnNode,Succeed、Fail
,分别对应成功、失败,当前端请求成功后,会对应返回
4、枚举五个GameId
,分别对应着五个游戏
5、游戏需要登录注册包、公告包、战绩包、排行榜包、分数包,可以实现在前端发送相关请求时,后端可以根据前端请求发送过来对应的含有所需内容的包
注:这里将玩家昵称username
和分数score
封装成ScoreInfo
数组,RankingPack
排行榜包中包含gameId
和ScoreInfo
数组