Networking API intended to make it easy to port non-connection-oriented code to take advantage of P2P connectivity and Steam Datagram Relay
is connection-oriented (like TCP), meaning you need to listen and connect, and then you send messages using a connection handle. ISteamNetworkingMessages, on the other hand, is more like UDP, in that you can just send messages to arbitrary peers at any time. The underlying connections are established implicitly.
Under the hood ISteamNetworkingMessages works on top of the ISteamNetworkingSockets
code, so you get the same routing and messaging efficiency. The difference is mainly in your responsibility to explicitly establish a connection and the type of feedback you get about the state of the connection. Both interfaces can do "P2P" communications, both support both unreliable and reliable messages, fragmentation and reassembly, and both can be used to take advantage of Steam Datagram Relay
to talk to dedicated servers.
The primary purpose of this interface is to be "like UDP", so that UDP-based code can be ported easily to take advantage of relayed connections. If you find yourself needing more low level information or control, or to be able to better handle failure, then you probably need to use ISteamNetworkingSockets
directly. Also, note that if your main goal is to obtain a connection between two peers without concerning yourself with assigning roles of "client" and "server", you may find the symmetric connection mode of ISteamNetworkingSockets useful.
Member functions for ISteamNetworkingMessages are called through the global accessor function SteamNetworkingMessages().
Sending and receiving messages
EResult SendMessageToUser( const SteamNetworkingIdentity &identityRemote, const void *pubData, uint32 cubData, int nSendFlags, int nRemoteChannel );
Sends a message to the specified host. If we don't already have a session with that user, a session is implicitly created. There might be some handshaking that needs to happen before we can actually begin sending message data. If this handshaking fails and we can't get through, an error will be posted via the callback SteamNetworkingMessagesSessionFailed_t
There is no notification when the operation succeeds. (You should have the peer send a reply for this purpose.)
Sending a message to a host will also implicitly accept any incoming connection from that host.
nSendFlags is a bitmask of k_nSteamNetworkingSend_xxx
nRemoteChannel is a routing number you can use to help route message to different systems.
You'll have to call ReceiveMessagesOnChannel
with the same channel number in order to retrieve the data on the other end.
Using different channels to talk to the same user will still use the same underlying connection, saving on resources. If you don't need this feature, use 0. Otherwise, small integers are the most efficient.
It is guaranteed that reliable messages to the same host on the same channel will be be received by the remote host (if they are received at all) exactly once, and in the same order that they were sent.
NO other order guarantees exist! In particular, unreliable messages may be dropped, received out of order with respect to each other and with respect to reliable data, or may be received multiple times. Messages on different channels are *not* guaranteed to be received in the order they were sent.
A note for those familiar with TCP/IP ports, or converting an existing codebase that opened multiple sockets: You might notice that there is only one channel, and with TCP/IP each endpoint has a port number. You can think of the channel number as the [/]destination[/i] port. If you need each message to also include a source port
(so the recipient can route the reply), then just put that in your message. That is essentially how UDP works!
int ReceiveMessagesOnChannel( int nLocalChannel, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages );
Reads the next message that has been sent from another user via SendMessageToUser() on the given channel. Returns number of messages returned into your list. (0 if no message are available on that channel.)
When you're done with the message object(s), make sure and call SteamNetworkingMessage_t::Release!
Managing sessions with peers
Although this API allows for sending messages to peers ad-hoc, under the hood ISteamNetworkingSockets connections are used. We refer to these connections as "sessions" in this context. However, when a remote peer initiates contact, no messages can be received from the peer until the session is accepted, either by sending outbound communications to the peer, or with AcceptSessionWithUser
. Sessions automatically time out when communications are idle, but you can terminate them early to free up resources, when you know you are finished.
bool AcceptSessionWithUser( const SteamNetworkingIdentity &identityRemote );
Call this in response to a SteamNetworkingMessagesSessionRequest_t
SteamNetworkingMessagesSessionRequest_t are posted when a user tries to send you a message, and you haven't tried to talk to them first. If you don't want to talk to them, just ignore the request. If the user continues to send you messages, SteamNetworkingMessagesSessionRequest_t callbacks will continue to be posted periodically.
Returns false if there is no session with the user pending or otherwise. If there is an existing active session, this function will return true, even if it is not pending.
will implicitly accepts any pending session request to that user.
bool CloseSessionWithUser( const SteamNetworkingIdentity &identityRemote );
Call this when you're done talking to a user to immediately free up resources under-the-hood. If the remote user tries to send data to you again, another SteamNetworkingMessagesSessionRequest_t
callback will be posted.
Note that sessions that go unused for a few minutes are automatically timed out.
bool CloseChannelWithUser( const SteamNetworkingIdentity &identityRemote, int nLocalChannel );
Call this when you're done talking to a user on a specific channel. Once all open channels to a user have been closed, the open session to the user will be closed, and any new data from this user will trigger a SteamNetworkingMessagesSessionRequest_t
ESteamNetworkingConnectionState GetSessionConnectionInfo( const SteamNetworkingIdentity &identityRemote, SteamNetConnectionInfo_t *pConnectionInfo, SteamNetworkingQuickConnectionStatus *pQuickStatus );
Returns information about the latest state of a connection, if any, with the given peer. Primarily intended for debugging purposes, but can also be used to get more detailed failure information.
Returns the value of SteamNetConnectionInfo_t
::m_eState, or k_ESteamNetworkingConnectionState_None if no connection exists with specified peer. You may pass nullptr for either parameter if you do not need the corresponding details. Note that sessions time out after a while, so if a connection fails, or SendMessageToUser
returns k_EResultNoConnection, you cannot wait indefinitely to obtain the reason for failure.
Posted when a remote host is sending us a message, and we do not already have a session with them
/// User who wants to talk to us
Posted when we fail to establish a connection, or we detect that communications have been disrupted it an unusual way. There is no notification when a peer proactively closes the session. ("Closed by peer" is not a concept of UDP-style communications, and ISteamNetworkingMessages is primarily intended to make porting UDP code easy.)
Remember: callbacks are asynchronous. See notes on SendMessageToUser
, and k_nSteamNetworkingSend_AutoRestartBrokenSession in particular.
Also, if a session times out due to inactivity, no callbacks will be posted. The only way to detect that this is happening is that querying the session state may return none, connecting, and findingroute again.
/// Detailed info about the session that failed.
/// SteamNetConnectionInfo_t::m_identityRemote indicates who this session
/// was with.