Relay message protocol
If you’re using the Relay SDK with UTP or with NGO for your project, the Relay message protocol is already supported. However, if you want to use an alternative engine or networking solution, you’ll need to implement the Relay message protocol before using it. Use the Relay message protocol specifications to implement the Relay message protocol.
The Relay message protocol expects you to express field values in big-endian order, also known as the “network order,” where the most significant byte occurs first.
Messages aren't authenticated except for the BIND
message, which uses an HMAC signature.
Players can only connect and relay messages with other players from the same Unity project and environment. Relay rejects any attempts to communicate across Unity environments.
All messages have a standard header and a message-specific body. Check out Standard header and Message bodies.
Message types
Code | Name | Description |
0 |
| A BIND message indicates a bind request sent from a client to a Relay server. |
1 | BIND_RECEIVED message | A BIND_RECEIVED message indicates a successful response from the Relay server to a client after receiving a BIND request . |
2 | PING message | A PING message indicates a ping message sent between a client and a Relay server (both directions) to keep the connection alive. |
3 | CONNECT_REQUEST message | A CONNECT_REQUEST message indicates a request from a player (the requesting client) to connect to another player (the target client). |
4 | Reserved. | |
5 | Reserved. | |
6 | ACCEPTED message | An ACCEPTED message indicates a confirmation message sent from a Relay server to a requesting client after a successful connection to a target client. |
7 | Reserved. | |
8 | Reserved. | |
9 | DISCONNECT message | A DISCONNECT message indicates a request to disconnect two connected players. |
10 | RELAY message | A RELAY message indicates a request to send a message between two players (clients). |
11 | CLOSE message | A CLOSE message indicates a request from a client to unbind from a Relay server. |
12 | ERROR message | An ERROR message indicates an error that has occurred. |
Accept mode types
The accept mode defines how a Relay server handles requests from clients trying to connect.
Relay only supports the AUTO
accept mode. A connection mode of AUTO
means the Relay server automatically accepts the connection if its capacity allows it (the number of connections must not exceed the maximum number of allowed connections).
Code | Name | Description |
0 | AcceptModeAuto | AcceptModeAuto indicates that connections to clients will be automatically accepted by the Relay server. |
1 | Reserved. |
Standard header
All message types share a standard header that has a signature, the Relay message protocol version, and the message contained in the body of the packet.
Bytes | 1 .. 2 | 3 | 4 |
Purpose | Signature | Version | Type |
The following table describes each field found in the standard header.
Field | Type | Description |
Signature | []byte | The Signature is always 0xDA72 . |
Version | uint8 | The Version is the Relay message protocol version.The value should be 0 for the initial release. |
Type | uint8 | The Type is an integer representing the message type contained in the body. Check out Message types. |
Message bodies
BIND message
BIND
messages are sent from a client to a Relay server to create a mapping from a client’s IP address and port for a specific allocation. This message is authenticated to verify the identity of the client.
The response received from the Allocations service has the information necessary for the client to authenticate its BIND
messages. The response from the Allocations service to the BIND
message shows the binding status.
Clients can send identical BIND
messages as long as the client’s IP address and port haven't changed. For example, if a client sends a BIND
message on a following connection after changing its IP address or port, the Nonce
value must be greater than the previously supplied value.
Bytes | 1 .. 4 | 5 | 6 .. 7 | 8 | 9 .. N | N+1 .. 33 |
Purpose | Header (Type 0) | AcceptMode | Nonce | ConnectionDataLength | ConnectionData | HMAC |
The following table describes each field found in a BIND
message.
Field | Type | Description |
AcceptMode | uint8 | AcceptMode defines how a Relay server should handle requests from clients trying to connect to the owner of this allocation.Currently, only the See Accept mode types. |
Nonce | uint16 | Nonce is a single-use cryptographic nonce. Each following BIND message must have an incremented nonce value. It's conventional to start the nonce value at 0. |
ConnectionDataLength | uint8 | ConnectionDataLength describes the byte length of ConnectionData .The maximum allowed value is 255. |
ConnectionData | []byte | ConnectionData is the encrypted connection data of the connecting client.This field’s length is variable. The maximum length is 255 bytes. |
HMAC | [32]byte | HMAC is a hash-based message authentication code generated using a known key and the preceding bytes in the message, including the header.See Authentication. |
Security
You must sign the HMAC with the secret key returned from the Allocations service. If the HMAC is invalid, the Relay server silently rejects the BIND message
.
The Relay server also validates the nonce value to mitigate message replay attacks by bad actors. If the Relay server determines that a nonce is invalid, it silently rejects it. If it’s the first time the client is binding to a Relay server with a BIND message
, the nonce value can be 0.
BIND_RECEIVED message
Relay servers send BIND_RECEIVED
messages to requesting clients to confirm they have successfully bound to the Relay server through a BIND message
. After receiving the confirmation, the requesting client can initiate communication with a target client using a CONNECT_REQUEST message
.
Note: The Relay server sends a BIND_RECEIVED
message to the client for each successfully processed BIND request
.
Bytes | 1 .. 4 |
Purpose | Header (Type 1) |
PING message
PING
messages are simple messages that keep the binding between a client and a Relay server alive by resetting the client timeout. Relay servers automatically disconnect clients after 10 seconds of inactivity. Any message received by the Relay server involving this client, either as a sender or receiver, resets this timeout. For games with a lower message frequency, the PING
message allows clients to reset the disconnection timer.
Tip: Clients must send PING
messages every second or two in addition to the other messages to ensure the connection doesn’t timeout. If you’re using Relay with NGO, the network manager automatically keeps the connection alive. However, if you’re using Relay with UTP, you must manually keep the connection alive.
Each PING
message has the client’s allocation ID and an arbitrary number that identifies the message.
When a Relay server receives a PING
message from a client, it sends the packet back to the client without altering it. This allows you to use PING
messages to check connectivity and measure round trip time.
The Relay server doesn't send back an error message if the allocation ID has expired.
Warning: Clients must bind with the Relay server through a BIND
message before sending a PING
message. The following scenarios will result in the Relay server returning a ErrClientPlayerMismatch
ERROR
message:
- The client sends a
PING
message before binding to the Relay server through aBIND
message. - The client sends a
PING
message after its IP address has changed and before rebinding to the Relay server with the new IP address.
Bytes | 1 .. 4 | 5 .. 20 | 21 .. 22 |
Purpose | Header (Type 2) | AllocationID | Number |
The following table describes each field found in a PING
message.
Field | Type | Description |
AllocationID | [16]byte | AllocationID is a 16-byte UUID identifying the allocated client sending the ping. |
Number | uint16 | Number is an arbitrary unsigned integer the Relay server echoes back to the client. This can be used by the client to correlate PING messages that are sent and received. |
CONNECT_REQUEST message
CONNECT_REQUEST
messages are sent from a requesting client to a Relay server to establish a connection to a target client. The target player the requesting client wants to connect to (or the target client) is represented by ToConnectionData
, which the Relay server decrypts to determine which player to connect with.
Warning: The target client and the requesting client must be on the same Unity project and environment.
Note: Relay servers have no formal idea of player groups (often referred to as sessions). All players within a logical game session are bound to the same Relay server. By extension of this, Relay doesn’t make any assumptions about the length of a player’s bindings and connections in relation to the game session. It’s up to the game client to determine whether players unbind from the Relay server at the end of a session or use the same binding for multiple sessions.
Bytes | 1 .. 4 | 5 .. 20 | 21 | 22 .. |
Purpose | Header (Type 3) | AllocationID | ToConnectionData Length | ToConnectionData |
The following table describes each field found in a CONNECT_REQUEST
message.
Field | Type | Description |
AllocationID | [16]byte | AllocationID is a 16-byte UUID that identifies the allocated client that's requesting the connection. |
ToConnectionDataLength | uint8 | ToConnectionDataLength is the byte length of ToConnectionData .The maximum allowed value is 255. |
ToConnectionData | []byte | ToConnectionData is an encrypted data blob that describes the client to connect to. See Connection data.This field has a variable length. The maximum byte length is 255. |
ACCEPTED message
A Relay server sends an ACCEPTED
message to a requesting client after a successful connection to a target client.
Note: The Relay server returns the ACCEPTED
message to the requesting client if the target client has fewer than their specified maximum connections.
Bytes | 1 .. 4 | 5 .. 20 | 21 .. 36 |
Purpose | Header (Type 6) | FromAllocationID | ToAllocationID |
The following table describes each field found in an ACCEPTED
message.
Field | Type | Description |
FromAllocationID | [16]byte | FromAllocationID is a 16-byte UUID that identifies the target client. |
ToAllocationID | [16]byte | ToAllocationID is a 16-byte UUID that identifies the allocated client that requested the connection (the requesting client). |
DISCONNECT message
The DISCONNECT
message is the inverse of the CONNECT_REQUEST
handshake and allows a client to disconnect from another client. A client can disconnect from any other client it has connected with by sending a DISCONNECT
message to the Relay server.
When the Relay server receives the message, it removes the specified allocation ID from the requesting client’s list of connected players. The Relay server also forwards the DISCONNECT
request to the host client so the client can update its map of connected players to remove the requesting client's Allocation ID.
The Relay server then sends the DISCONNECT
message back to the client as a confirmation. If either of the allocation IDs in the message body is invalid, the Relay server sends an ERROR message
instead.
Once a client has disconnected, the Relay server rejects all RELAY
messages sent to that client’s allocation ID. A disconnected client can re-establish a connection with a CONNECT_REQUEST message
.
Note: The host and connecting clients can both send a DISCONNECT
messages. However, if a host client sends a DISCONNECT
message, the client must migrate the joined players to a new host or disconnect them all. Relay doesn't handle host migration.
Bytes | 1 .. 4 | 5 .. 20 | 21 .. 36 |
Purpose | Header (Type 9) | FromAllocationID | ToAllocationID |
The following table describes each field found in a DISCONNECT
message.
Field | Type | Description |
FromAllocationID | [16]byte | FromAllocationID is a 16-byte UUID that identifies the allocated client that's requesting to close the connection (the requesting client). |
ToAllocationID | [16]byte | ToAllocationID is a 16-byte UUID that identifies the allocated client whose connection is being closed (the target client). |
RELAY message
RELAY
messages allow clients to send messages containing any arbitrary payload of bytes between each other without awareness of each other’s IP addresses and ports.
Before sending a RELAY
message between clients, the Relay server ensures the client sending the packet has previously been authenticated as the FromAllocationID
through a BIND message
. If the client hasn't previously bound, the Relay server returns an ErrClientPlayerMismatch
ERROR
message.
The Relay server also ensures the two clients have an established connection through a earlier CONNECT_REQUEST
exchange. The Relay server returns an ErrNotConnected
ERROR message
if the clients aren't connected.
If all validations pass, the Relay server sends the entire message to the ToAllocationID
as-is. The Relay server doesn't send any confirmation message to the FromAllocationID
.
Bytes | 1 .. 4 | 5 .. 20 | 21 .. 36 | 37 .. 38 | 39 .. |
Purpose | Header (Type 10) | FromAllocationID | ToAllocationID | Length | Content |
Note: The maximum length of a RELAY
message isn't guaranteed at the protocol level. It's defined by the configuration of the Relay server itself, which has a default maximum content length of **1400 **bytes and results in a RELAY
message maximum size of **1438 **bytes.
The following table describes each field found in a RELAY
message.
Field | Type | Description |
FromAllocationID | [16]byte | FromAllocationID is a 16-byte UUID that identifies the allocated client sending the message (the requesting client). |
ToAllocationID | [16]byte | ToAllocateID is a 16-byte UUID that identifies the allocated client receiving the message (the target client). |
Length | uint16 | Length specifies the length of the message content in bytes.The maximum value is 1400. |
Content | []byte | Content has the content of the message. |
CLOSE message
CLOSE
messages are idempotent messages that allow a client to unbind and deallocate from a Relay server. Clients should send a CLOSE
message to the Relay server when leaving their game session or when closing the game client. Clients can only close their own bindings to the Relay server; they can't close another client’s binding.
Clients should send CLOSE
messages multiple times to increase the chance of successful delivery. Because CLOSE
messages are idempotent, there’s no risk of the Relay server sending an ERROR
message if the client has already been disconnected.
The CLOSE
message is a best effort to gracefully terminate an allocation, and there is no guarantee it will succeed. Because Relay servers can deallocate clients with a timeout, Relay servers don't rely on a CLOSE
message to remove a client allocation.
The client sending the CLOSE
message must be bound to the Relay server through a earlier BIND
request. If the client isn't bound to the Relay server (or the client’s IP address has changed), the Relay server will silently reject the message. Use PING
messages to prevent unintended allocation timeouts.
Note: Unbound clients still expire through timeout. See Client timeouts.
Bytes | 1 .. 4 | 5 .. 20 |
Purpose | Header (Type 11) | AllocationID |
The following table describes each field found in a CLOSE
message.
Field | Type | Description |
AllocationID | [16]byte | AllocationID is a 16-byte UUID that identifies the client's allocation. |
ERROR message
Relay servers use ERROR
messages strictly to inform the client that an error has occurred.
Bytes | 1 .. 4 | 5 .. 20 | 21 |
Purpose | Header (Type 12) | AllocationID | ErrorCode |
The following table describes each field found in an ERROR
message.
Field | Type | Description |
AllocationID | [16]byte | AllocationID is a 16-byte UUID that identifies the client's allocation. |
ErrorCode | uint8 | ErrorCode represents an error code. See Error codes below. |
Error codes
The following table describes each of the possible error messages a Relay server can send to a client.
Code | Name | Description |
0 | Invalid protocol version (ErrInvalidProtocolVersion ) | ErrInvalidProtocolVersion is returned when a parsed message has an unexpected protocol version. |
1 | Player timed out due to inactivity (ErrTimeout ) | ErrTimeout is returned when a client’s binding to a Relay server has timed out. See Client timeouts.Clients should use |
2 | Unauthorized (ErrUnauthorized ) | ErrUnauthorized is returned when a client attempts to perform an operation that it's not authorized to perform. |
3 | Allocation ID client mismatch (ErrClientPlayerMismatch ) | ErrClientPlayerMismatch is returned when a client has sent a RELAY message to the server using an unknown network connection.It's intended to inform the client that the IP address they're using has changed from the one the Relay server is aware of. This error message indicates the client should re-bind with a new |
4 | Allocation ID not found (ErrAllocationNotFound ) | ErrAllocationNotFound is returned when the Relay server can't find an allocation for the given allocation ID. |
5 | Not connected (ErrNotConnected ) | ErrNotConnected is returned when a client attempts to communicate with a client to whom it's not connected. |
6 | Self-connect not allowed (ErrSelfConnectNotAllowed ) | ErrSelfConnectNotAllowed is returned when a client sends a CONNECT_REQUEST message to a client with the same allocation ID. |