Relay 消息协议

如果您在项目中将 Relay SDK 搭配 UTP 或者搭配 NGO 使用,则支持 Relay 消息协议。不过,如果您使用的是其他引擎或网络解决方案,则需要在使用前实施 Relay 消息协议。请使用 Relay 消息协议规范来实施 Relay 消息协议。

Relay 消息协议要求以“大端序”(也称为“网络序”)表示字段值,即高位先行。

除使用 HMAC 签名的 BIND 消息外,其他消息均不进行验证。

玩家仅可与属于相同 Unity 项目和环境的其他玩家进行连接并转发消息。Relay 拒绝跨 Unity 环境进行的通信尝试。

所有消息都具有标准标头和消息特定的消息体。请参阅标准标头消息体

消息类型

代码名称描述
0BIND 消息BIND 消息表示由客户端发送给 Relay 服务器的绑定请求。
1BIND_RECEIVED 消息BIND_RECEIVED 消息表示 Relay 服务器在收到 BIND 请求后对客户端的成功响应。
2PING 消息PING 消息表示客户端和 Relay 服务器之间(双向)发送的 Ping 消息,用于保持连接活动状态。
3CONNECT_REQUEST 消息CONNECT_REQUEST 消息表示一个玩家(请求客户端)发送给另一个玩家(目标客户端)的连接请求。
4预留。
5预留。
6ACCEPTED 消息ACCEPTED 消息表示在请求客户端成功连接到目标客户端后,由 Relay 服务器发送给请求客户端的确认消息。
7预留。
8预留。
9DISCONNECT 消息DISCONNECT 消息表示两个玩家之间的连接断开请求。
10RELAY 消息RELAY 消息表示两个玩家(客户端)之间的消息发送请求。
11CLOSE 消息CLOSE 消息表示由客户端发送的从 Relay 服务器解绑的请求。
12ERROR 消息ERROR 消息表示发生了错误。

接受模式类型

“接受模式”定义了 Relay 服务器处理客户端连接请求的方式。

Relay 仅支持 AUTO 接受模式。AUTO 连接模式表示 Relay 服务器在容量允许(即连接数不得超过允许的最大连接数)情况下自动接受连接。

代码名称描述
0AcceptModeAutoAcceptModeAuto 表示 Relay 服务器自动接受与客户端的连接。
1 预留。

标准标头

所有消息类型均使用相同的标准标头,其中包含签名、Relay 消息协议版本以及报文主体中包含的消息。

字节1 ..234
用途SignatureVersionType

下表对标准标头中的每个字段进行了介绍。

字段类型描述
Signature[]byteSignature 始终为 0xDA72
Versionuint8Version 是 Relay 消息协议版本。

初始版本对应的值为 0

Typeuint8Type 是表示消息体中所包含消息类型的整数。请参阅消息类型

消息主体

BIND 消息

BIND 消息由客户端发送到 Relay 服务器,用于针对特定分配创建客户端 IP 地址和端口的映射。通过验证该消息可验证客户端的身份。

分配服务收到的响应包含用于验证客户端 BIND 消息所需的信息。分配服务对 BIND 消息的响应即表示当前绑定状态。

在客户端 IP 地址和端口不变的情况下,客户端可发送相同的 BIND 消息。例如,如果客户端 IP 地址或端口发生更改,则当为后续连接发送 BIND 消息时,Nonce 值必须要大于之前提供的值。

字节1 ..456 ..789 ..NN+1 ..33
用途标头(类型 0)AcceptModeNonceConnectionDataLengthConnectionDataHMAC

下表对 BIND 消息中的每个字段进行了介绍。

字段类型描述
AcceptModeuint8AcceptMode 定义了 Relay 服务器针对客户端连接到分配所有者的请求的处理方式。

目前仅支持 AUTO 接受模式。

请参阅接受模式类型

Nonceuint16Nonce 是一次性密码随机数。后续发送的 BIND 消息必须使用递增的随机数值。通常,随机数值从 0 开始。
ConnectionDataLengthuint8ConnectionDataLength 描述 ConnectionData 的字符长度。

允许的最大值为 255

ConnectionData[]byteConnectionData发起连接的客户端的加密连接数据

该字段的长度是可变的。最大长度为 255 字节。

HMAC[32]byteHMAC 是使用已知密钥和消息中位于前面的字节(包括标头)生成的基于哈希的消息验证代码。

请参阅身份验证

安全

必须使用从分配服务返回的密钥对 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 服务器返回 ErrClientPlayerMismatchERROR 消息

  • 客户端在通过 BIND 消息绑定到 Relay 服务器之前发送 PING 消息。
  • 客户端在 IP 地址更改后、未使用新 IP 地址与 Relay 服务器重新绑定之前发送 PING 消息。
字节1 ..45 ..2021 ..22
用途标头(类型 2)AllocationIDNumber

下表对 PING 消息中的每个字段进行了介绍。

字段类型描述
AllocationID[16]byteAllocationID 是一个 16 字节 UUID,用于标识发送 ping 消息的已分配客户端。
Numberuint16Number 是 Relay 服务器响应客户端的任意无符号整数,客户端使用该整数将发送和接收的 PING 消息进行关联。

CONNECT_REQUEST 消息

CONNECT_REQUEST 消息由请求客户端发送到 Relay 服务器,用于与目标客户端建立连接。请求客户端希望连接的目标玩家(或目标客户端)由 ToConnectionData 表示,Relay 服务器在对其解密后可以确定要连接的玩家。

警告:目标客户端和请求客户端必须属于相同的 Unity 项目和环境。

注意:Relay 服务器没有正式的玩家分组概念(通常使用会话)。处于同一逻辑游戏会话中的所有玩家绑定到同一 Relay 服务器,由此推论,Relay 不会对与游戏会话相关的玩家的绑定和连接时长做任何假设。玩家是否在会话结束时从 Relay 服务器解除绑定或将同一绑定用于多个会话,均取决于游戏客户端。

字节1 ..45 ..202122 ..
用途标头(类型 3)AllocationIDToConnectionData LengthToConnectionData

下表对 CONNECT_REQUEST 消息中的每个字段进行了介绍。

字段类型描述
AllocationID[16]byteAllocationID 是一个 16 字节 UUID,用于标识请求连接的已分配客户端。
ToConnectionDataLengthuint8ToConnectionDataLength 描述 ToConnectionData 的字符长度。

允许的最大值为 255

ToConnectionData[]byteToConnectionData 是描述要连接到的客户端的加密数据块。请参阅连接数据

该字段的长度是可变的。最大长度为 255

ACCEPTED 消息

Relay 服务器会在请求客户端成功连接到目标客户端后,向请求客户端发送一条 ACCEPTED 消息。

注意:如果目标客户端的连接数少于指定的最大连接数,Relay 服务器会向请求客户端返回 ACCEPTED 消息。

字节1 ..45 ..2021 ..36
用途标头(类型 6)FromAllocationIDToAllocationID

下表对 ACCEPTED 消息中的每个字段进行了介绍。

字段类型描述
FromAllocationID[16]byteFromAllocationID 是一个 16 字节 UUID,用于标识目标客户端。
ToAllocationID[16]byteToAllocationID 是一个 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 ..45 ..2021 ..36
用途标头(类型 9)FromAllocationIDToAllocationID

下表对 DISCONNECT 消息中的每个字段进行了介绍。

字段类型描述
FromAllocationID[16]byteFromAllocationID 是一个 16 字节 UUID,用于标识请求关闭连接的已分配客户端(请求客户端)。
ToAllocationID[16]byteToAllocationID 是一个 16 字节 UUID,用于标识即将被关闭连接的已分配客户端(目标客户端)。

RELAY 消息

RELAY 消息允许客户端在不知道彼此 IP 地址和端口的情况下,相互发送包含任意字节有效负载的消息。

在客户端之间发送 RELAY 消息之前,Relay 服务器需要确认发送数据包的客户端之前已通过 BIND message 验证为 FromAllocationID。如果该客户端之前未绑定,Relay 服务器会返回一条 ErrClientPlayerMismatch ERROR 消息

此外,Relay 服务器还会确认两个客户端之前已通过 CONNECT_REQUEST 交换建立连接。如果两个客户端未进行连接,Relay 服务器会返回一条 ErrNotConnectedERROR message

如果所有验证均通过,Relay 服务器会将整个消息原样发送到 ToAllocationID。Relay 服务器不会向 FromAllocationID 发送任何确认消息。

字节1 ..45 ..2021 ..3637 ..3839 ..
用途标头(类型 10)FromAllocationIDToAllocationIDLengthContent

注意RELAY 消息的最大长度未在协议级别进行保证,而是由 Relay 服务器本身的配置定义。Relay 服务器的默认最大内容长度为 **1400** 字节,因此 RELAY 消息的最大大小为 1438 字节。

下表对 RELAY 消息中的每个字段进行了介绍。

字段类型描述
FromAllocationID[16]byteFromAllocationID 是一个 16 字节 UUID,用于标识发送消息的已分配客户端(请求客户端)。
ToAllocationID[16]byteToAllocateID 是一个 16 字节 UUID,用于标识接收消息的已分配客户端(目标客户端)。
Lengthuint16Length 指定了消息内容的长度,以字节为单位。

最大长度为 1400

Content[]byteContent 包含消息的内容。

CLOSE 消息

CLOSE 消息为幂等消息,允许客户端从 Relay 服务器解绑并取消分配。客户端应在离开游戏会话或关闭游戏客户端时向 Relay 服务器发送 CLOSE 消息。客户端仅可关闭自身与 Relay 服务器的绑定,但无法关闭其他客户端的绑定。

为增加成功发送的机会,客户端应多次发送 CLOSE 消息。由于 CLOSE 消息是幂等的,Relay 服务器在客户端已断开连接的情况下发送 ERROR 消息不会产生风险。

CLOSE 消息是用于正常终止分配的最佳方式,但并不能保证一定成功。由于可以通过超时时间为客户端取消分配,Relay 服务器不依赖 CLOSE 消息来删除客户端分配。

发送 CLOSE 消息的客户端必须之前通过 BIND 请求绑定到 Relay 服务器。如果客户端未绑定到 Relay 服务器(或客户端 IP 地址已更改),Relay 服务器会静默拒绝该消息。使用 PING 消息可防止分配意外超时。

注意:未绑定的客户端仍然会因超时而过期。请参阅客户端超时

字节1 ..45 ..20
用途标头(类型 11)AllocationID

下表对 CLOSE 消息中的每个字段进行了介绍。

字段类型描述
AllocationID[16]byteAllocationID 是一个 16 字节 UUID,用于标识客户端分配。

ERROR 消息

Relay 服务器严格使用 ERROR 消息来通知客户端发生了错误。

字节1 ..45 ..2021
用途标头(类型 12)AllocationIDErrorCode

下表对 ERROR 消息中的每个字段进行了介绍。

字段类型描述
AllocationID[16]byteAllocationID 是一个 16 字节 UUID,用于标识客户端分配。
ErrorCodeuint8ErrorCode 表示错误代码。请参阅错误代码

错误代码

下表对 Relay 服务器可能发送到客户端的每个错误消息进行了介绍。

代码名称描述
0无效协议版本 (ErrInvalidProtocolVersion)当解析后的消息中包含无效的协议版本时,会返回 ErrInvalidProtocolVersion
1玩家因无活动而超时 (ErrTimeout)当客户端与 Relay 服务器的绑定超时后,会返回 ErrTimeout。请参阅客户端超时

客户端应使用 PING 消息来防止产生此错误。

2未授权 (ErrUnauthorized)当客户端尝试执行未经授权的操作时,会返回 ErrUnauthorized
3分配 ID 客户端不匹配 (ErrClientPlayerMismatch)客户端使用未知的网络连接向服务器发送 RELAY 消息后,会返回 ErrClientPlayerMismatch

该消息用于通知客户端其使用的 IP 地址不同于 Relay 服务器上绑定的地址。

该错误消息指示客户端应使用新的 BIND 消息重新进行绑定。

4未找到分配 ID (ErrAllocationNotFound)当 Relay 服务器无法找到给定分配 ID 对应的分配时,会返回 ErrAllocationNotFound
5未连接 (ErrNotConnected)当客户端尝试与未连接的客户端通信时,会返回 ErrNotConnected
6不允许自连接 (ErrSelfConnectNotAllowed)当客户端向具有相同分配 ID 的客户端发送 CONNECT_REQUEST 消息时,会返回 ErrSelfConnectNotAllowed