前言
协议的选型上不多说,用Protobuf
,关于如何选型,可以参考下面这个系列文章:
本文探讨的是基于Protobuf
上如何做协议事件分发路由设计,预备知识可以参考:
版本选择建议选择: Protobuf3
思路设计
因此通常定义协议为:
两个字节 | 两个字节 | 不定长度 |
---|---|---|
消息长度 | 协议号 | 消息 |
游戏中的路由主要作用为: 根据路由序号去解码协议消息, 并且分发到响应方法内处理.
协议号设计
协议号(cmd16
), 占用16
个bit
, 分为两段:
- 模块协议号, 占用
7
个bit
:cmd
- 子模块消息协议号, 占用
9
个bit
:c_cmd
两段协议号通过下面的规则组合成协议号(cmd16
):
Cmd16 = Cmd bsl 9 + CCmd
注意: 协议号(cmd16
), 总共占用16
个bit
, 范围在: 0
- 65535
, 因此最大协议数量为: 65536
个
模块协议号
占用7
个bit
: cmd
模块协议号(cmd
)定义在msg.proto文件内:
1 | enum cmd { |
以枚举(enum
)方式定义, 枚举名固定为cmd
,枚举的Key
(msg_base
)必须与[文件名(msg_base.proto
)]一致,枚举的Value
(0
)表示: 模块协议号(cmd
).
注意: 模块协议号(cmd
), 占用7
个bit
, 范围在: 0
- 127
, 因此最大模块数量为: 128
个
子模块消息协议号
占用9
个bit
: c_cmd
子模块消息协议号(c_cmd
)定义在具体的协议(msg_base.proto)文件内:
1 | enum c_cmd{ |
同样以枚举(enum
)方式定义, 枚举名固定为c_cmd
,枚举的Key
(HeartbeatReq
)必须与对应的[协议名(HeartbeatReq
)]一致,枚举的Value
(0
)表示: 子模块消息协议号(c_cmd
).
注意: 子模块消息协议号(c_cmd
), 占用9
个bit
, 范围在: 0
- 511
, 因此最大协议数量为: 512
个
协议消息设计
协议消息分类:
- 上行数据: 请求
- 下行数据: 响应/推送
一般游戏中只存在三种关系:
- 客户端同步请求返回
- 客户端异步请求
- 服务器主推消息
最好是希望这些关系能在协议文件内直观的表示,因此借用了pb
的rpc
来表示:
- 客户端同步请求返回:
rpc heartbeat(HeartbeatReq) returns (HeartbeatResp);
- 客户端异步请求:
rpc just_req(JustReq) returns (Empty);
- 服务器主推消息:
rpc heartbeat_push(Empty) returns (HeartbeatResp);
这些rpc
都定义在service
内,service
名的定义必须以当前文件名为前缀, 以_service
为后缀命名.
请参照 msg_base.proto:
1 | syntax = "proto3"; |
协议消息命名规范
协议名建议都以[驼峰命名法]命名
协议消息分类根据不同的命名后缀来区分:
- 请求:
Req
, 例如:HeartbeatReq
- 响应:
Resp
, 例如:HeartbeatResp
- 推送:
Push
, 例如:TimePush
- 请求:
rpc
名因为是请以[下划线命名法]命名, 例如:rpc just_req
rpc名的作用
上面定义的三种关系中, 每个关系都有一个名字,在服务端这一方, 除主推的消息的名字无用外,另外两种关系都是作为路由协议回调函数名来使用,这样也跟代码里面的关系对应上了.
使用
通过以上规则,我们只需要将这些规则加以使用,编写一个插件,去生成路由层的代码即可.
今天先说到这里,下次在开文章讲解插件的使用.