Relay 消息协议
如果您在项目中将 Relay SDK 搭配 UTP 或者搭配 NGO 使用,则支持 Relay 消息协议。不过,如果您使用的是其他引擎或网络解决方案,则需要在使用前实施 Relay 消息协议。请使用 Relay 消息协议规范来实施 Relay 消息协议。
Relay 消息协议要求以“大端序”(也称为“网络序”)表示字段值,即高位先行。
除使用 HMAC 签名的 BIND
消息外,其他消息均不进行验证。
玩家仅可与属于相同 Unity 项目和环境的其他玩家进行连接并转发消息。Relay 拒绝跨 Unity 环境进行的通信尝试。
所有消息都具有标准标头和消息特定的消息体。请参阅标准标头和消息体。
消息类型
代码 | 名称 | 描述 |
0 |
| BIND 消息表示由客户端发送给 Relay 服务器的绑定请求。 |
1 | BIND_RECEIVED 消息 | BIND_RECEIVED 消息表示 Relay 服务器在收到 BIND 请求 后对客户端的成功响应。 |
2 | PING 消息 | PING 消息表示客户端和 Relay 服务器之间(双向)发送的 Ping 消息,用于保持连接活动状态。 |
3 | CONNECT_REQUEST 消息 | CONNECT_REQUEST 消息表示一个玩家(请求客户端)发送给另一个玩家(目标客户端)的连接请求。 |
4 | 预留。 | |
5 | 预留。 | |
6 | ACCEPTED 消息 | ACCEPTED 消息表示在请求客户端成功连接到目标客户端后,由 Relay 服务器发送给请求客户端的确认消息。 |
7 | 预留。 | |
8 | 预留。 | |
9 | DISCONNECT 消息 | DISCONNECT 消息表示两个玩家之间的连接断开请求。 |
10 | RELAY 消息 | RELAY 消息表示两个玩家(客户端)之间的消息发送请求。 |
11 | CLOSE 消息 | CLOSE 消息表示由客户端发送的从 Relay 服务器解绑的请求。 |
12 | ERROR 消息 | ERROR 消息表示发生了错误。 |
接受模式类型
“接受模式”定义了 Relay 服务器处理客户端连接请求的方式。
Relay 仅支持 AUTO
接受模式。AUTO
连接模式表示 Relay 服务器在容量允许(即连接数不得超过允许的最大连接数)情况下自动接受连接。
代码 | 名称 | 描述 |
0 | AcceptModeAuto | AcceptModeAuto 表示 Relay 服务器自动接受与客户端的连接。 |
1 | 预留。 |
标准标头
所有消息类型均使用相同的标准标头,其中包含签名、Relay 消息协议版本以及报文主体中包含的消息。
字节 | 1 ..2 | 3 | 4 |
用途 | Signature | Version | Type |
下表对标准标头中的每个字段进行了介绍。
字段 | 类型 | 描述 |
Signature | []byte | Signature 始终为 0xDA72 。 |
Version | uint8 | Version 是 Relay 消息协议版本。初始版本对应的值为 0。 |
Type | uint8 | Type 是表示消息体中所包含消息类型的整数。请参阅消息类型。 |
消息主体
BIND 消息
BIND
消息由客户端发送到 Relay 服务器,用于针对特定分配创建客户端 IP 地址和端口的映射。通过验证该消息可验证客户端的身份。
从分配服务收到的响应包含用于验证客户端 BIND
消息所需的信息。分配服务对 BIND
消息的响应即表示当前绑定状态。
在客户端 IP 地址和端口不变的情况下,客户端可发送相同的 BIND
消息。例如,如果客户端 IP 地址或端口发生更改,则当为后续连接发送 BIND
消息时,Nonce
值必须要大于之前提供的值。
字节 | 1 ..4 | 5 | 6 ..7 | 8 | 9 ..N | N+1 ..33 |
用途 | 标头(类型 0) | AcceptMode | Nonce | ConnectionDataLength | ConnectionData | HMAC |
下表对 BIND
消息中的每个字段进行了介绍。
字段 | 类型 | 描述 |
AcceptMode | uint8 | AcceptMode 定义了 Relay 服务器针对客户端连接到分配所有者的请求的处理方式。目前仅支持 请参阅接受模式类型。 |
Nonce | uint16 | Nonce 是一次性密码随机数。后续发送的 BIND 消息必须使用递增的随机数值。通常,随机数值从 0 开始。 |
ConnectionDataLength | uint8 | ConnectionDataLength 描述 ConnectionData 的字符长度。允许的最大值为 255。 |
ConnectionData | []byte | ConnectionData 是发起连接的客户端的加密连接数据。该字段的长度是可变的。最大长度为 255 字节。 |
HMAC | [32]byte | HMAC 是使用已知密钥和消息中位于前面的字节(包括标头)生成的基于哈希的消息验证代码。请参阅身份验证。 |
安全
必须使用从分配服务返回的密钥对 HMAC 进行签名。如果 HMAC 无效,则 Relay 服务器会静默拒绝 BIND message
。
Relay 服务器还会验证随机数值,以便缓解攻击者的消息重放攻击。Relay 服务器认定随机数无效,则会静默拒绝。如果客户端第一次使用 BIND message
绑定到 Relay 服务器,随机数值可以是 0。
BIND_RECEIVED 消息
Relay 服务器向请求客户端发送 BIND_RECEIVED
消息,用于确认客户端已成功通过 BIND message
绑定到 Relay 服务器。收到确认消息后,请求客户端即可使用 CONNECT_REQUEST message
向目标客户端发起通信。
注意:Relay 服务器会针对每条成功处理的 BIND request
向客户端发送一条 BIND_RECEIVED
消息。
字节 | 1 ..4 |
用途 | 标头(类型 1) |
PING 消息
PING
消息是简单消息,通过重置客户端超时时间来保持客户端和 Relay 服务器之间的绑定。Relay 服务器会在客户端处于无活动状态 10 秒钟后自动断开与客户端的连接。Relay 服务器收到的关于客户端(作为发送方或接收方)的任何消息均会引发超时时间重置。对于消息收发频率较低的游戏,PING
消息允许客户端重置断开连接计时器。
提示:除其他消息外,客户端还必须每一秒或两秒发送 PING
消息,以防止连接超时。如果搭配 NGO 使用 Relay,Network Manager 会自动保持连接活动状态。但是,如果搭配 UTP 使用 Relay,则必须手动保持连接活动状态。
每条 PING
消息均包含客户端的分配 ID 和用于标识该消息的任意数字。
Relay 服务器收到来自客户端的 PING
消息后,会将数据包原样发送回客户端。鉴于此,您可以使用 PING
消息检查连接性并测量往返时间。
Relay 服务器在分配 ID 过期的情况下不会发回错误消息。
警告:客户端必须先通过 BIND
消息与 Relay 服务器绑定,然后再发送 PING
消息。以下情况会导致 Relay 服务器返回 ErrClientPlayerMismatch
ERROR
消息:
- 客户端在通过
BIND
消息绑定到 Relay 服务器之前发送PING
消息。 - 客户端在 IP 地址更改后、未使用新 IP 地址与 Relay 服务器重新绑定之前发送
PING
消息。
字节 | 1 ..4 | 5 ..20 | 21 ..22 |
用途 | 标头(类型 2) | AllocationID | Number |
下表对 PING
消息中的每个字段进行了介绍。
字段 | 类型 | 描述 |
AllocationID | [16]byte | AllocationID 是一个 16 字节 UUID,用于标识发送 ping 消息的已分配客户端。 |
Number | uint16 | Number 是 Relay 服务器响应客户端的任意无符号整数,客户端使用该整数将发送和接收的 PING 消息进行关联。 |
CONNECT_REQUEST 消息
CONNECT_REQUEST
消息由请求客户端发送到 Relay 服务器,用于与目标客户端建立连接。请求客户端希望连接的目标玩家(或目标客户端)由 ToConnectionData
表示,Relay 服务器在对其解密后可以确定要连接的玩家。
警告:目标客户端和请求客户端必须属于相同的 Unity 项目和环境。
注意:Relay 服务器没有正式的玩家分组概念(通常使用会话)。处于同一逻辑游戏会话中的所有玩家绑定到同一 Relay 服务器,由此推论,Relay 不会对与游戏会话相关的玩家的绑定和连接时长做任何假设。玩家是否在会话结束时从 Relay 服务器解除绑定或将同一绑定用于多个会话,均取决于游戏客户端。
字节 | 1 ..4 | 5 ..20 | 21 | 22 .. |
用途 | 标头(类型 3) | AllocationID | ToConnectionData Length | ToConnectionData |
下表对 CONNECT_REQUEST
消息中的每个字段进行了介绍。
字段 | 类型 | 描述 |
AllocationID | [16]byte | AllocationID 是一个 16 字节 UUID,用于标识请求连接的已分配客户端。 |
ToConnectionDataLength | uint8 | ToConnectionDataLength 描述 ToConnectionData 的字符长度。允许的最大值为 255。 |
ToConnectionData | []byte | ToConnectionData 是描述要连接到的客户端的加密数据块。请参阅连接数据。该字段的长度是可变的。最大长度为 255。 |
ACCEPTED 消息
Relay 服务器会在请求客户端成功连接到目标客户端后,向请求客户端发送一条 ACCEPTED
消息。
注意:如果目标客户端的连接数少于指定的最大连接数,Relay 服务器会向请求客户端返回 ACCEPTED
消息。
字节 | 1 ..4 | 5 ..20 | 21 ..36 |
用途 | 标头(类型 6) | FromAllocationID | ToAllocationID |
下表对 ACCEPTED
消息中的每个字段进行了介绍。
字段 | 类型 | 描述 |
FromAllocationID | [16]byte | FromAllocationID 是一个 16 字节 UUID,用于标识目标客户端。 |
ToAllocationID | [16]byte | ToAllocationID 是一个 16 字节 UUID,用于标识请求连接的已分配客户端(请求客户端)。 |
DISCONNECT 消息
DISCONNECT
消息与 CONNECT_REQUEST
握手相反,用于断开一个客户端与其他客户端的连接。客户端可以通过向 Relay 服务器发送 DISCONNECT
消息来断开与其他客户端的连接。
Relay 服务器收到该消息后,会从请求客户端的已连接玩家列表中删除指定的分配 ID,并将 DISCONNECT
请求转发到主机客户端,以便主机客户端更新已连接玩家的地图并删除请求客户端的分配 ID。
之后,Relay 服务器将 DISCONNECT
消息发回客户端进行确认。如果消息主体中的任一分配 ID 无效,Relay 服务器会发送 ERROR message
。
客户端断开连接后,Relay 服务器会拒绝所有发送至该客户端分配 ID 的 RELAY
消息。客户端断开连接后,可使用 CONNECT_REQUEST message
重新建立连接。
注意:主机客户端和连接客户端均可发送 DISCONNECT
消息。不过,如果主机客户端发送 DISCONNECT
消息,客户端必须将已加入的玩家迁移到新的主机或与所有玩家断开连接。Relay 不负责处理主机迁移。
字节 | 1 ..4 | 5 ..20 | 21 ..36 |
用途 | 标头(类型 9) | FromAllocationID | ToAllocationID |
下表对 DISCONNECT
消息中的每个字段进行了介绍。
字段 | 类型 | 描述 |
FromAllocationID | [16]byte | FromAllocationID 是一个 16 字节 UUID,用于标识请求关闭连接的已分配客户端(请求客户端)。 |
ToAllocationID | [16]byte | ToAllocationID 是一个 16 字节 UUID,用于标识即将被关闭连接的已分配客户端(目标客户端)。 |
RELAY 消息
RELAY
消息允许客户端在不知道彼此 IP 地址和端口的情况下,相互发送包含任意字节有效负载的消息。
在客户端之间发送 RELAY
消息之前,Relay 服务器需要确认发送数据包的客户端之前已通过 BIND message
验证为 FromAllocationID
。如果该客户端之前未绑定,Relay 服务器会返回一条 ErrClientPlayerMismatch
ERROR
消息。
此外,Relay 服务器还会确认两个客户端之前已通过 CONNECT_REQUEST
交换建立连接。如果两个客户端未进行连接,Relay 服务器会返回一条 ErrNotConnected
ERROR message
。
如果所有验证均通过,Relay 服务器会将整个消息原样发送到 ToAllocationID
。Relay 服务器不会向 FromAllocationID
发送任何确认消息。
字节 | 1 ..4 | 5 ..20 | 21 ..36 | 37 ..38 | 39 .. |
用途 | 标头(类型 10) | FromAllocationID | ToAllocationID | Length | Content |
注意:RELAY
消息的最大长度未在协议级别进行保证,而是由 Relay 服务器本身的配置定义。Relay 服务器的默认最大内容长度为 **1400** 字节,因此 RELAY
消息的最大大小为 1438 字节。
下表对 RELAY
消息中的每个字段进行了介绍。
字段 | 类型 | 描述 |
FromAllocationID | [16]byte | FromAllocationID 是一个 16 字节 UUID,用于标识发送消息的已分配客户端(请求客户端)。 |
ToAllocationID | [16]byte | ToAllocateID 是一个 16 字节 UUID,用于标识接收消息的已分配客户端(目标客户端)。 |
Length | uint16 | Length 指定了消息内容的长度,以字节为单位。最大长度为 1400。 |
Content | []byte | Content 包含消息的内容。 |
CLOSE 消息
CLOSE
消息为幂等消息,允许客户端从 Relay 服务器解绑并取消分配。客户端应在离开游戏会话或关闭游戏客户端时向 Relay 服务器发送 CLOSE
消息。客户端仅可关闭自身与 Relay 服务器的绑定,但无法关闭其他客户端的绑定。
为增加成功发送的机会,客户端应多次发送 CLOSE
消息。由于 CLOSE
消息是幂等的,Relay 服务器在客户端已断开连接的情况下发送 ERROR
消息不会产生风险。
CLOSE
消息是用于正常终止分配的最佳方式,但并不能保证一定成功。由于可以通过超时时间为客户端取消分配,Relay 服务器不依赖 CLOSE
消息来删除客户端分配。
发送 CLOSE
消息的客户端必须之前通过 BIND
请求绑定到 Relay 服务器。如果客户端未绑定到 Relay 服务器(或客户端 IP 地址已更改),Relay 服务器会静默拒绝该消息。使用 PING
消息可防止分配意外超时。
注意:未绑定的客户端仍然会因超时而过期。请参阅客户端超时。
字节 | 1 ..4 | 5 ..20 |
用途 | 标头(类型 11) | AllocationID |
下表对 CLOSE
消息中的每个字段进行了介绍。
字段 | 类型 | 描述 |
AllocationID | [16]byte | AllocationID 是一个 16 字节 UUID,用于标识客户端分配。 |
ERROR 消息
Relay 服务器严格使用 ERROR
消息来通知客户端发生了错误。
字节 | 1 ..4 | 5 ..20 | 21 |
用途 | 标头(类型 12) | AllocationID | ErrorCode |
下表对 ERROR
消息中的每个字段进行了介绍。
字段 | 类型 | 描述 |
AllocationID | [16]byte | AllocationID 是一个 16 字节 UUID,用于标识客户端分配。 |
ErrorCode | uint8 | ErrorCode 表示错误代码。请参阅错误代码。 |
错误代码
下表对 Relay 服务器可能发送到客户端的每个错误消息进行了介绍。
代码 | 名称 | 描述 |
0 | 无效协议版本 (ErrInvalidProtocolVersion ) | 当解析后的消息中包含无效的协议版本时,会返回 ErrInvalidProtocolVersion 。 |
1 | 玩家因无活动而超时 (ErrTimeout ) | 当客户端与 Relay 服务器的绑定超时后,会返回 ErrTimeout 。请参阅客户端超时。客户端应使用 |
2 | 未授权 (ErrUnauthorized ) | 当客户端尝试执行未经授权的操作时,会返回 ErrUnauthorized 。 |
3 | 分配 ID 客户端不匹配 (ErrClientPlayerMismatch ) | 客户端使用未知的网络连接向服务器发送 RELAY 消息后,会返回 ErrClientPlayerMismatch 。该消息用于通知客户端其使用的 IP 地址不同于 Relay 服务器上绑定的地址。 该错误消息指示客户端应使用新的 |
4 | 未找到分配 ID (ErrAllocationNotFound ) | 当 Relay 服务器无法找到给定分配 ID 对应的分配时,会返回 ErrAllocationNotFound 。 |
5 | 未连接 (ErrNotConnected ) | 当客户端尝试与未连接的客户端通信时,会返回 ErrNotConnected 。 |
6 | 不允许自连接 (ErrSelfConnectNotAllowed ) | 当客户端向具有相同分配 ID 的客户端发送 CONNECT_REQUEST 消息 时,会返回 ErrSelfConnectNotAllowed 。 |