Steamworks Documentation
ISteamNetworkingSockets Interface
Networking API similar to Berkeley sockets, but for games.

  • It's a connection-oriented API (like TCP, not UDP). When sending and receiving messages, the peer is identified using a connection handle.
  • But unlike TCP, it's message-oriented, not stream-oriented. (The boundaries between the messages are maintained by the API.)
  • Both reliable and unreliable messages are supported.
  • Large messages are split into multiple packets, small messages are combined into fewer packets.
  • A robust ACK / reassembly / retransmission strategy.
  • Strong encryption and authentication. When a player connects, you can be sure that if a certain SteamID is authenticated, that someone who has access to that person's account has authorized the connection. Eavesdropping / tampering requires hacking into the VAC-secured process. Impersonation requires access to the target's computer.
  • Supports relayed connections through the Valve network. This prevents IP addresses from being revealed, protecting your players and gameservers from attack. NAT punching is not attempted.
  • Also supports standard connectivity over plain UDP using IPv4 or IPv6.
  • To participate in Valve's dedicated server hosting program, use of this API is required. (This program is in beta -- contact us if you'd like to participate.)

Related documents:
  • Steam Networking overview of the different networking APIs.
  • ISteamNetworkingUtils Utilities for measuring ping and estimating ping time between peers.
  • steamnetworkingtypes Miscellaneous types and utilities.
  • ISteamNetworking Previous version of this interface.
  • Coming soon: ISteamNetworkingMessages is a UDP-style interface. Send and receive messages using peer address/identity, instead of connection handles.

An opensource version of this API is available on github. You can use it for whatever purpose you want. To use the Valve network you need to be a Steam partner and use the version in the Steamworks SDK.

Member functions for ISteamNetworkingSockets are called through the global accessor function SteamNetworkingSockets().

Managing connections

CreateListenSocketIP

HSteamListenSocket CreateListenSocketIP( const SteamNetworkingIPAddr &localAddress );

Creates a "server" socket that listens for clients to connect to by calling ConnectByIPAddress, over ordinary UDP (IPv4 or IPv6)

You must select a specific local port to listen on and set it the port field of the local address.

Usually you will set the IP portion of the address to zero, (SteamNetworkingIPAddr::Clear()). This means that you will not bind to any particular local interface. In addition, if possible the socket will be bound in "dual stack" mode, which means that it can accept both IPv4 and IPv6 clients. If you wish to bind a particular interface, then set the local address to the appropriate IPv4 or IPv6 IP.

When a client attempts to connect, a SteamNetConnectionStatusChangedCallback_t will be posted. The connection will be in the k_ESteamNetworkingConnectionState_Connecting state.

ConnectByIPAddress

HSteamNetConnection ConnectByIPAddress( const SteamNetworkingIPAddr &address );

Creates a connection and begins talking to a "server" over UDP at the given IPv4 or IPv6 address. The remote host must be listening with a matching call to ISteamnetworkingSockets::CreateListenSocketIP on the specified port.

A SteamNetConnectionStatusChangedCallback_t callback will be triggered when we start connecting, and then another one on either timeout or successful connection.

If the server does not have any identity configured, then their network address will be the only identity in use. Or, the network host may provide a platform-specific identity with or without a valid certificate to authenticate that identity. (These details will be contained in the SteamNetConnectionStatusChangedCallback_t.) It's up to your application to decide whether to allow the connection.

CreateListenSocketP2P

HSteamListenSocket CreateListenSocketP2P( int nVirtualPort );

Like CreateListenSocketIP, but clients will connect using ConnectP2P. The connection will be relayed through the Valve network.

nVirtualPort specifies how clients can connect to this socket using ConnectP2P. It's very common for applications to only have one listening socket; in that case, use zero. If you need to open multiple listen sockets and have clients be able to connect to one or the other, then nVirtualPort should be a small integer (<1000) unique to each listen socket you create.

If you use this, you probably want to call ISteamNetworkingUtils::InitializeRelayNetworkAccess when your app initializes.

ConnectP2P

HSteamNetConnection ConnectP2P( const SteamNetworkingIdentity &identityRemote, int nVirtualPort );
Begin connecting to a server that is identified using a platform-specific identifier. For example, if the other player is playing on Steam, identityRemote is the CSteamID of the other player.

The connection will be relayed through the Valve network.

If you use this, you probably want to call ISteamNetworkingUtils::InitializeRelayNetworkAccess when your app initializes.

AcceptConnection

EResult AcceptConnection( HSteamNetConnection hConn );

Accept an incoming connection that has been received on a listen socket.

When a connection attempt is received (perhaps after a few basic handshake packets have been exchanged to prevent trivial spoofing), a connection interface object is created in the k_ESteamNetworkingConnectionState_Connecting state and a SteamNetConnectionStatusChangedCallback_t is posted. At this point, your application MUST either accept or close the connection. (It may not ignore it.) Accepting the connection will transition it either into the connected state, or the finding route state, depending on the connection type.

You should take action within a second or two, because accepting the connection is what actually sends the reply notifying the client that they are connected. If you delay taking action, from the client's perspective it is the same as the network being unresponsive, and the client may timeout the connection attempt. In other words, the client cannot distinguish between a delay caused by network problems and a delay caused by the application.

This means that if your application goes for more than a few seconds without processing callbacks (for example, while loading a map), then there is a chance that a client may attempt to connect in that interval and fail due to timeout.

If the application does not respond to the connection attempt in a timely manner, and we stop receiving communication from the client, the connection attempt will
be timed out locally, transitioning the connection to the k_ESteamNetworkingConnectionState_ProblemDetectedLocally state. The client may also close the connection before it is accepted, and a transition to the k_ESteamNetworkingConnectionState_ClosedByPeer is also possible depending the exact sequence of events.

Returns:
  • k_EResultInvalidParam if the handle is invalid.
  • k_EResultInvalidState if the connection is not in the appropriate state. (Remember that the connection state could change in between the time that the notification being posted to the queue and when it is received by the application.)

CloseConnection

bool CloseConnection( HSteamNetConnection hPeer, int nReason, const char *pszDebug, bool bEnableLinger );

Disconnects from the remote host and invalidates the connection handle. Any unread data on the connection is discarded.

nReason is an application defined code that will be received on the other end and recorded (when possible) in backend analytics. The value should come from a restricted range. (See ESteamNetConnectionEnd.) If you don't need to communicate any information to the remote host, and do not want analytics to be able to distinguish "normal" connection terminations from "exceptional" ones, you may pass zero, in which case the generic value of k_ESteamNetConnectionEnd_App_Generic will be used.

pszDebug is an optional human-readable diagnostic string that will be received by the remote host and recorded (when possible) in backend analytics.

Optionally, you may put the socket into a "linger" state, which causes the connection to attempt is deliver any remaining outbound reliable messages before actually closing the connection. Otherwise any unsent reliable data is discarded. In any case, once you close the connection you will have no further visibility into the status of the connection or any messages. (In some uses cases, this is acceptable. If not, then you need to wait to close the connection.) Remember that this is an application protocol, not a kernel protocol like TCP. So you need to make sure the application stays running long enough for the data to be flushed out.

If the connection has already ended (k_ESteamNetworkingConnectionState_ClosedByPeer or k_ESteamNetworkingConnectionState_ProblemDetectedLocally) and you are just freeing up the connection object, then nReason, pszDebug , and bEnableLinger are ignored.

CloseListenSocket

bool CloseListenSocket( HSteamListenSocket hSocket );

Destroy a listen socket. All the connections that were accepted on the listen socket are closed ungracefully.

CreateSocketPair

bool CreateSocketPair( HSteamNetConnection *pOutConnection1, HSteamNetConnection *pOutConnection2, bool bUseNetworkLoopback, const SteamNetworkingIdentity *pIdentity1, const SteamNetworkingIdentity *pIdentity2 );

Create a pair of connections that are talking to each other, e.g. a loopback connection. This is very useful for testing, or so that your client/server code can work the same even when you are running a local "server".

The two connections will immediately be placed into the connected state, and no callbacks will be posted immediately. After this, if you close either connection, the other connection will receive a callback, exactly as if they were communicating over the network. You must close *both* sides in order to fully clean up the resources!

By default, internal buffers are used, completely bypassing the network, the chopping up of messages into packets, encryption, copying the payload, etc. This means that loopback packets, by default, will not simulate lag or loss. Passing true for bUseNetworkLoopback will cause the socket pair to send packets through the local network loopback device (127.0.0.1) on ephemeral ports. Fake lag and loss are supported in this case, and CPU time is expended to encrypt and decrypt.

If you wish to assign a specific identity to either connection, you may pass a particular identity. Otherwise, if you pass nullptr, the respective connection will assume a generic "localhost" identity. If you use real network loopback, this might be translated to the actual bound loopback port. Otherwise, the port will be zero.

Sending and receiving messages

SendMessageToConnection

EResult SendMessageToConnection( HSteamNetConnection hConn, const void *pData, uint32 cbData, int nSendFlags );
Send a message to the remote host on the specified connection.

nSendFlags determines the delivery guarantees that will be provided, when data should be buffered, etc. E.g.:
  • k_nSteamNetworkingSend_Unreliable
  • k_nSteamNetworkingSend_Reliable
  • k_nSteamNetworkingSend_NoNagle
  • k_nSteamNetworkingSend_NoDelay

Note that the semantics we use for messages are not precisely the same as the semantics of a standard "stream" socket. (SOCK_STREAM) For an ordinary stream socket, the boundaries between chunks are not considered relevant, and the sizes of the chunks of data written will not necessarily match up to the sizes of the chunks that are returned by the reads on the other end. The remote host might read a partial chunk, or chunks might be coalesced. For the message semantics used here, however, the sizes WILL match. Each send call will match a successful read call on the remote host one-for-one. If you are porting existing stream-oriented code to the semantics of reliable messages, your code should work the same, since reliable message semantics are more strict than stream semantics. The only caveat is related to performance: there is per-message overhead to retain the message sizes, and so if your code sends many small chunks of data, performance will suffer. Any code based on stream sockets that does not write excessively small chunks will work without any changes.

Returns:
  • k_EResultInvalidParam: invalid connection handle, or the individual message is too big. (See k_cbMaxSteamNetworkingSocketsMessageSizeSend)
  • k_EResultInvalidState: connection is in an invalid state
  • k_EResultNoConnection: connection has ended
  • k_EResultIgnored: You used k_nSteamNetworkingSend_NoDelay, and the message was dropped because we were not ready to send it.
  • k_EResultLimitExceeded: there was already too much data queued to be sent. (See k_ESteamNetworkingConfig_SendBufferSize)

FlushMessagesOnConnection

EResult FlushMessagesOnConnection( HSteamNetConnection hConn );
Flush any messages waiting on the Nagle timer and send them at the next transmission opportunity (often that means right now).

If Nagle is enabled (it's on by default) then when calling SendMessageToConnection the message will be buffered, up to the Nagle time before being sent, to merge small messages into the same packet. (See k_ESteamNetworkingConfig_NagleTime)

Returns:
  • k_EResultInvalidParam: invalid connection handle
  • k_EResultInvalidState: connection is in an invalid state
  • k_EResultNoConnection: connection has ended
  • k_EResultIgnored: We weren't (yet) connected, so this operation has no effect.

ReceiveMessagesOnConnection

int ReceiveMessagesOnConnection( HSteamNetConnection hConn, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages );
Fetch the next available message(s) from the connection, if any. Returns the number of messages returned into your array, up to nMaxMessages.
If the connection handle is invalid, -1 is returned. If no data is available, 0, is returned.

The order of the messages returned in the array is relevant. Reliable messages will be received in the order they were sent (and with the same sizes --- see SendMessageToConnection for on this subtle difference from a stream socket).

Unreliable messages may be dropped, or delivered out of order with respect to each other or with respect to reliable messages.

If any messages are returned, you MUST call SteamNetworkingMessage_t::Release() to each of them free up resources after you are done. It is safe to keep the object alive for a little while (put it into some queue, etc), and you may call Release() from any thread.

ReceiveMessagesOnListenSocket

int ReceiveMessagesOnListenSocket( HSteamListenSocket hSocket, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages );

Same as ReceiveMessagesOnConnection, but will return the next message available on any connection that was accepted through the specified listen socket. Examine SteamNetworkingMessage_t::m_conn to know which client connection.

Delivery order of messages among different clients is not defined. They may be returned in an order different from what they were actually received. (Delivery
order of messages from the same client is well defined, and thus the order of the messages is relevant!)

Connection info

GetConnectionInfo

bool GetConnectionInfo( HSteamNetConnection hConn, SteamNetConnectionInfo_t *pInfo );
Returns basic information about the high-level state of the connection.
Returns false if the connection handle is invalid.

GetQuickConnectionStatus

bool GetQuickConnectionStatus( HSteamNetConnection hConn, SteamNetworkingQuickConnectionStatus *pStats );
Returns a small set of information about the real-time state of the connection.
Returns false if the connection handle is invalid, or the connection has ended.

GetDetailedConnectionStatus

int GetDetailedConnectionStatus( HSteamNetConnection hConn, char *pszBuf, int cbBuf );
Returns very detailed connection stats in diagnostic text format. Useful for dumping to a log, etc. The format of this information is subject to change.

Returns:
  • -1: failure (bad connection handle)
  • 0: OK, your buffer was filled in and '\0'-terminated
  • >0: Your buffer was either nullptr, or it was too small and the text got truncated. Try again with a buffer of at least N bytes.

SetConnectionUserData

bool SetConnectionUserData( HSteamNetConnection hPeer, int64 nUserData );

Set connection user data. The data is used in the following places:

Returns false if the handle is invalid.

GetConnectionUserData

int64 GetConnectionUserData( HSteamNetConnection hPeer );
Fetch connection user data. Returns -1 if handle is invalid or if you haven't set any userdata on the connection.

SetConnectionName

void SetConnectionName( HSteamNetConnection hPeer, const char *pszName );
Set a name for the connection, used mostly for debugging

GetConnectionName

bool GetConnectionName( HSteamNetConnection hPeer, char *pszName, int nMaxLen );
Fetch connection name into your buffer, which is at least nMaxLen bytes. Returns false if handle is invalid.

Misc

GetListenSocketAddress

bool GetListenSocketAddress( HSteamListenSocket hSocket, SteamNetworkingIPAddr *address );

Returns local IP and port that a listen socket created using CreateListenSocketIP is bound to.

Specific IPv6/IPv4 addresses may be returned if you bound the socket to a particular interface.
An IPv6 address of ::0 means "any IPv4 or IPv6"
An IPv6 address of ::ffff:0000:0000 means "any IPv4"

NOTE: This is not how you find our your public IP that clients can connect to.
Try ISteamGameServer::GetPublicIP.

Returns false if the handle is invalid, or the information is not available for that type of listen socket.

GetIdentity

bool GetIdentity( SteamNetworkingIdentity *pIdentity );
Get the identity assigned to this interface.

E.g. on Steam, this is the user's SteamID, or for the gameserver interface, the SteamID assigned to the gameserver. Returns false and sets the result to an invalid identity if we don't know our identity yet. (E.g. GameServer has not logged in. On Steam, the user will know their SteamID even if they are not signed into Steam.)

Gameservers hosted with Valve

The following functions are used when you are hosting gameservers in Valve data centers. This program is in beta. If you have a linux dedicated server running in a docker image, contact us to ask about participation.

ReceivedRelayAuthTicket

bool ReceivedRelayAuthTicket( const void *pvTicket, int cbTicket, SteamDatagramRelayAuthTicket *pOutParsedTicket );

Call this when you receive a ticket from your backend / matchmaking system. Puts the ticket into a persistent cache, and optionally returns the parsed ticket.

See stamdatagram_ticketgen.h for more details.

FindRelayAuthTicketForServer

int FindRelayAuthTicketForServer( const SteamNetworkingIdentity &identityGameServer, int nVirtualPort, SteamDatagramRelayAuthTicket *pOutParsedTicket );

Search cache for a ticket to talk to the server on the specified virtual port. If found, returns the number of seconds until the ticket expires, and optionally the complete cracked ticket. Returns 0 if we don't have a ticket.

Typically this is useful just to confirm that you have a ticket, before you call ConnectToHostedDedicatedServer to connect to the server.

ConnectToHostedDedicatedServer

HSteamNetConnection ConnectToHostedDedicatedServer( const SteamNetworkingIdentity &identityTarget, int nVirtualPort );
Client call to connect to a server hosted in a Valve data center, on the specified virtual port. You must have placed a ticket for this server into the cache, or else this connect attempt will fail!

You may wonder why tickets are stored in a cache, instead of simply being passed as an argument here. The reason is to make reconnection to a gameserver robust, even if the client computer loses connection to Steam or the central backend, or the app is restarted or crashes, etc.

If you use this, you probably want to call ISteamNetworkingUtils::InitializeRelayNetworkAccess when your app initializes.

GetHostedDedicatedServerPort

uint16 GetHostedDedicatedServerPort();
Returns the value of the SDR_LISTEN_PORT environment variable. This is the UDP server your server will be listening on. This will configured automatically for you in production environments. (You should set it yourself for testing.)

GetHostedDedicatedServerPOPID

SteamNetworkingPOPID GetHostedDedicatedServerPOPID();
If you are running in a production data center, this will return the data center code. Returns 0 otherwise.

GetHostedDedicatedServerAddress

bool GetHostedDedicatedServerAddress( SteamDatagramHostedAddress *pRouting );
Return info about the hosted server. You will need to send this information to your backend, and put it in tickets, so that the relays will know how to forward traffic from clients to your server. See SteamDatagramRelayAuthTicket for more info.

NOTE ABOUT DEVELOPMENT ENVIRONMENTS:
In production in our data centers, these parameters are configured via environment variables. In development, the only one you need to set is SDR_LISTEN_PORT, which is the local port you want to listen on. Furthermore, if you are running your server behind a corporate firewall, you probably will not be able to put the routing information returned by this function into tickets. Instead, it should be a public internet address that the relays can use to send data to your server. So you might just end up hardcoding a public address and setup port forwarding on your corporate firewall. In that case, the port you put into the ticket needs to be the public-facing port opened on your firewall, if it is different from the actual server port.

Returns false if the SDR_LISTEN_PORT environment variable is not set.

CreateHostedDedicatedServerListenSocket

HSteamListenSocket CreateHostedDedicatedServerListenSocket( int nVirtualPort );
Create a listen socket on the specified virtual port. The physical UDP port to use will be determined by the SDR_LISTEN_PORT environment variable. If a UDP port is not configured, this call will fail.

Note that this call MUST be made through the SteamGameServerNetworkingSockets() interface.

Callback structures

SteamNetConnectionStatusChangedCallback_t

This callback is posted whenever a connection is created, destroyed, or changes state. The m_info field will contain a complete description of the connection at the time the change occurred and the callback was posted. In particular, m_info.m_eState will have the new connection state.

You will usually need to listen for this callback to know when:
  • A new connection arrives on a listen socket. m_info.m_hListenSocket will be set, m_eOldState = k_ESteamNetworkingConnectionState_None, and m_info.m_eState = k_ESteamNetworkingConnectionState_Connecting. See AcceptConnection.
  • A connection you initiated has been accepted by the remote host. m_eOldState = k_ESteamNetworkingConnectionState_Connecting, and m_info.m_eState = k_ESteamNetworkingConnectionState_Connected. Some connections might transition to k_ESteamNetworkingConnectionState_FindingRoute first.
  • A connection has been actively rejected or closed by the remote host. m_eOldState = k_ESteamNetworkingConnectionState_Connecting or k_ESteamNetworkingConnectionState_Connected, and m_info.m_eState = k_ESteamNetworkingConnectionState_ClosedByPeer. m_info.m_eEndReason and m_info.m_szEndDebug will have more details. NOTE: upon receiving this callback, you must still destroy the connection using CloseConnection to free up local resources. (The details passed to the function are not used in this case, since the connection is already closed.)
  • A problem was detected with the connection, and it has been closed by the local host. The most common failure is timeout, but other configuration or authentication failures can cause this. m_eOldState = k_ESteamNetworkingConnectionState_Connecting or k_ESteamNetworkingConnectionState_Connected, and m_info.m_eState = k_ESteamNetworkingConnectionState_ProblemDetectedLocally. m_info.m_eEndReason and m_info.m_szEndDebug will have more details. NOTE: upon receiving this callback, you must still destroy the connection using CloseConnection to free up local resources. (The details passed to the function are not used in this case, since the connection is already closed.)

Remember that callbacks are posted to a queue, and networking connections can change at any time. It is possible that the connection has already changed state by the time you process this callback.

Also note that callbacks will be posted when connections are created and destroyed by your own API calls.

struct SteamNetConnectionStatusChangedCallback_t { /// Connection handle HSteamNetConnection m_hConn; /// Full connection info SteamNetConnectionInfo_t m_info; /// Previous state. (Current state is in m_info.m_eState) ESteamNetworkingConnectionState m_eOldState; };