# About that P2P support....

SteamNetworkingSockets supports peer-to-peer connections.  A "peer-to-peer"
connection in this context means that the hosts do not (initially) know
each other's IP address.  Furthermore, they may be behind NAT, and so they
may not know their *own* public IP address.  They may not even *have* a public
IP that other hosts can use to send them inbound traffic.  [Here](https://tailscale.com/blog/how-nat-traversal-works/)
is a good article about the problem of NAT traversal.

[ICE](https://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment)
is an internet standard protocol for discovering and sharing IP addresses,
negotiating NAT, and establishing a direct connection or fallback to relaying
the connection if necessary.

The library includes an ICE client implementation with no external dependencies
that is compiled in by default.  (See the vcpkg feature `ice` or the cmake flag
`ENABLE_ICE`).

Alternatively, the library can also be compiled to use
[Google WebRTC](https://webrtc.googlesource.com/src)'s ICE implementation.
We interface with the WebRTC code at a relatively low level and only use it
for datagram transport.  We don't use DTLS or WebRTC data channels.
See also `k_ESteamNetworkingConfig_P2P_Transport_ICE_Implementation`.

The two candidates are compatible with one another.  (A peer using the WebRTC
ICE client can conneect to a peer using the native client.)

## Symmetric connect mode

SteamNetworkingSockets offers a unique feature known as *symmetric
connect mode*, which really puts the "peer" in "peer-to-peer connection".
This feature is useful in the following common use case:

* Two hosts wish to establish a *single* connection between them.
* Either host may initiate the connection.
* The hosts may initiate the connection at the same time.

This situation involves race conditions that can be tricky to get
right, especially when authentication, encryption, ICE roles, etc
are involved.  With symmetric connect mode, sorting out these race
conditions and producing a single connection is handled by the API.

See the ``k_ESteamNetworkingConfig_SymmetricConnect``
connection flag in [steamnetworkingtypes.h](include/steam/steamnetworkingtypes.h)
for more info.

## ISteamNetworkingMessages

Most P2P libraries, such as google WebRTC, and indeed our own
[ISteamNetworkingSockets](include/steam/isteamnetworkingsockets.h), are *connection
oriented*.  To talk to a peer, you first establish a connection to the peer, and
when you send and receive messages, the peer is identified by the connection handle.

Much existing network code is based on UDP with a single socket, where
 connection handles are not used.  Instead, packets are sent with the IP address
 of the recipeient specified for each packet.   (E.g. ``sentto()`` and ``recvfrom()``).
[ISteamNetworkingMessages](include/steam/isteamnetworkingmessages.h) was created
to provide a more "ad-hoc" interface like UDP.  It can be useful when adding P2P
support to existing code, depending on the abstraction you are working with.  If
the code you are modifing already has the concept of a connection, then you might
find it easier to use ISteamNetworkinSockets directly.  But if you are modifying code
at a lower level, you may find that you need to maintain a table of active connections,
and each time you send a packet, use the existing connection if one exists, or
create a new connection if one does not exist.  This is exactly what
ISteamNetworkingMessages does for you.  It creates symmetric-mode connections on
demand the first time you communicate with a given peer, and idle connections are
automatically closed when they are no longer needed.

## Requirements

Peer-to-peer connections require more than just working code.  In addition
to the code in this library, there are several other prerequisites.
(Note that the Steamworks API provides all of these services for Steam games.)

### Signaling service

A side channel, capable of relaying small rendezvous
messages from one host to another.  This means hosts must have a constant
connection to your service, once that enables you to *push* messages to them.

SteamNetworkingSockets supports a pluggable signaling service.  The requirements
placed on your signaling service are relatively minimal:

* Individual rendezvous messages are small.  (Perhaps bigger than IP MTU,
  but never more than a few KB.)
* Only datagram "best-effort" delivery is required.  The signaling protocol
  is tolerant of dropped, duplicated, or reordered messages.
  These anomalies may cause negotiation to take longer, but not fail.
  This means, for example that there doesn't need to be a mechanism to
  inform the system when your connection to the signaling service is
  disrupted.
* The channel can be relatively low bandwidth and high latency.  Usually
  there is a burst of a handful of exchanges when a connection is created,
  followed by silence, unless something changes with the connection.


### STUN server(s)

A [STUN](https://en.wikipedia.org/wiki/STUN) server is used to help peers
discover their own public IP address and open up firewalls.  STUN
servers are relatively low bandwidth, and there are publicly-available ones.

### Relay fallback

Unfortunatley, for some pairs of hosts, NAT piercing is not successful.
In this situation, the traffic must be relayed.  In the ICE protocol, this is
done using [TURN](https://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT)
servers.  (NOTE: Any TURN server also doubles as a STUN server.)  Because the TURN
server is relaying every packet, is is a relatively costly service, so you probably
will need to run your own, or just fail connections that cannot pierce NAT.

On Steam we use a custom relay service known as [Steam Datgaram Relay](https://partner.steamgames.com/doc/features/multiplayer/steamdatagramrelay)
-- SDR for short -- carrying packets through our network of relays and
on our backbone.   (You may see this mentioned in the opensource code here,
but the SDR support code is not opensource.)  Also, on Steam we often
do not share public IP addresses between untrusted peers, so that malicious
players cannot DoS attack.  In that case NAT punch is not possible and traffic
would be relayed.

### Naming hosts and matchmaking

The above requirements are just what is needed to make a connection between two
hosts, once you know who to connect to.  But before that, you need a way to assign an
identity to a host, authenticate them, matchmaking them, etc.  Those services are
also included with Steam, but outside the scope of a transport library like this.

## Using P2P

Assuming you have all of those requirements, you can use SteamNetworkingSockets
to make P2P connections!

To enable P2P/ICE support, just use the vcpkg feature `ice`, or build with the cmake
flag `-DENABLE_ICE=ON`.  Note that both of these are the default, so you just need
to avoid explicitly turning it off.

If you want to use the Google WebRTC ICE implementation, set
`-DUSE_STEAMWEBRTC=ON`.  cmake will automatically activate two git submodules
and pull down the Google WebRTC code.

Take a look at these files for more information:

* [steamnetworkingcustomsignaling.h](include/steam/steamnetworkingcustomsignaling.h)
  contains the interfaces you'll need to implement for your signaling service.
* An example of a really trivial signaling protocol:
  * [trivial_signaling_server.py](examples/trivial_signaling_server.py) server
  * [trivial_signaling_client.cpp](examples/trivial_signaling_client.cpp) client
* A test case that puts everything together.  It starts up an example trivial
  signaling protocol server and two peers, and has them connect to each other
  and exchange a few messages.  We use the publicly-available google STUN servers.
  (No TURN servers for relay fallback in this example.)
  * [test_p2p.py](tests/test_p2p.py) Executive test script that starts all the processes.
  * [test_p2p.cpp](tests/test_p2p.cpp) Code for the process each peer runs.

## Roadmap

Here are some things we have in mind to make P2P support better:

* Get plugins written for standard, opensource protocols that could be used for
  signaling, such as [XMPP](https://xmpp.org/).  This would be a great project for
  somebody else to do!  We would welcome contributions to this repository, or
  happily link to your own project.  (See issue #136)
* LAN beacon support, so that P2P connections can be made even when signaling
  is down or the hosts do not have Internet connectivity.  (See issue #82)
* Support for [PCP](https://tools.ietf.org/html/rfc6887) as an additional
  NAT traversal mechanism.
* Shared candidate pool across multiple P2P connections.  Currently, each
  connection goes through a full ICE gathering process independently, including
  opening its own sockets and creating its own TURN allocation.  A server forming
  connections with many peers (e.g. a full mesh) could instead gather candidates
  once per interface, share a single set of sockets, and reuse a single TURN
  allocation across all peers.  This would be a significant efficiency improvement.
* TURN channels, for more efficient relay of data packets once a relayed path
  is selected.
* MTU probing: ICE connectivity checks should be sent at the full path MTU, so
  that MTU issues are discovered during negotiation rather than after the
  connection is established.
