0%

游戏协议事件分发路由设计

前言

协议的选型上不多说,用Protobuf,关于如何选型,可以参考下面这个系列文章:

本文探讨的是基于Protobuf上如何做协议事件分发路由设计,预备知识可以参考:

版本选择建议选择: Protobuf3

思路设计

因此通常定义协议为:

两个字节 两个字节 不定长度
消息长度 协议号 消息

游戏中的路由主要作用为: 根据路由序号去解码协议消息, 并且分发到响应方法内处理.

示例

协议号设计

协议号(cmd16), 占用16bit, 分为两段:

  • 模块协议号, 占用7bit: cmd
  • 子模块消息协议号, 占用9bit: c_cmd

两段协议号通过下面的规则组合成协议号(cmd16):

Cmd16 = Cmd bsl 9 + CCmd

注意: 协议号(cmd16), 总共占用16bit, 范围在: 0 - 65535, 因此最大协议数量为: 65536

模块协议号

占用7bit: cmd

模块协议号(cmd)定义在msg.proto文件内:

1
2
3
enum cmd {
msg_base = 0;
}

以枚举(enum)方式定义, 枚举名固定为cmd,枚举的Key(msg_base)必须与[文件名(msg_base.proto)]一致,枚举的Value(0)表示: 模块协议号(cmd).

注意: 模块协议号(cmd), 占用7bit, 范围在: 0 - 127, 因此最大模块数量为: 128

子模块消息协议号

占用9bit: c_cmd

子模块消息协议号(c_cmd)定义在具体的协议(msg_base.proto)文件内:

1
2
3
4
5
6
7
enum c_cmd{
HeartbeatReq = 0;
HeartbeatResp = 1;
JustReq = 2;
}

message HeartbeatReq {}

同样以枚举(enum)方式定义, 枚举名固定为c_cmd,枚举的Key(HeartbeatReq)必须与对应的[协议名(HeartbeatReq)]一致,枚举的Value(0)表示: 子模块消息协议号(c_cmd).

注意: 子模块消息协议号(c_cmd), 占用9bit, 范围在: 0 - 511, 因此最大协议数量为: 512

协议消息设计

协议消息分类:

  • 上行数据: 请求
  • 下行数据: 响应/推送

一般游戏中只存在三种关系:

  • 客户端同步请求返回
  • 客户端异步请求
  • 服务器主推消息

最好是希望这些关系能在协议文件内直观的表示,因此借用了pbrpc来表示:

  • 客户端同步请求返回: 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
syntax = "proto3";
import "google/protobuf/empty.proto";

enum c_cmd{
HeartbeatReq = 0;
HeartbeatResp = 1;
JustReq = 2;
}

service msg_base_service{
rpc heartbeat(HeartbeatReq) returns (HeartbeatResp);
rpc just_req(JustReq) returns (Empty);
rpc heartbeat_push(Empty) returns (HeartbeatResp);
}

message HeartbeatReq{}
message JustReq{
uint64 id = 1;
}
message HeartbeatResp{}

协议消息命名规范

  • 协议名建议都以[驼峰命名法]命名

  • 协议消息分类根据不同的命名后缀来区分:

    • 请求: Req, 例如: HeartbeatReq
    • 响应: Resp, 例如: HeartbeatResp
    • 推送: Push, 例如: TimePush
  • rpc名因为是请以[下划线命名法]命名, 例如: rpc just_req

rpc名的作用

上面定义的三种关系中, 每个关系都有一个名字,在服务端这一方, 除主推的消息的名字无用外,另外两种关系都是作为路由协议回调函数名来使用,这样也跟代码里面的关系对应上了.

使用

通过以上规则,我们只需要将这些规则加以使用,编写一个插件,去生成路由层的代码即可.

今天先说到这里,下次在开文章讲解插件的使用.

参考连接

梦想基金
feng19 微信

微信

feng19 支付宝

支付宝

欢迎关注我的其它发布渠道