# Connections

[`RTCPeerConnection`](../api/interfaces/rtc_peer_connection.md#webrtc.interfaces.rtc_peer_connection.RTCPeerConnection) is one end of a connection. Each end describes itself in an SDP description, and
the two exchange their descriptions and ICE candidates over a signalling channel the application provides.

## Configuration

A connection takes an [`RTCConfiguration`](../api/models/rtc_configuration.md#webrtc.models.rtc_configuration.RTCConfiguration). To connect across NATs, give it STUN and TURN servers:

```python
config = webrtc.RTCConfiguration(
    ice_servers=[
        webrtc.RTCIceServer(urls='stun:stun.l.google.com:19302'),
        webrtc.RTCIceServer(urls='turn:turn.example.com:3478', username='user', credential='secret'),
    ],
)
pc = webrtc.RTCPeerConnection(config)
```

Dictionaries of the specification are typed dataclasses, with the names in snake_case. Methods take these classes
in place of plain dicts, so a misspelled member raises an error where it’s written. See [MDN: RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection).

## Signalling

The side that starts creates an offer, the other one answers:

```python
# the caller
offer = await pc.create_offer()
await pc.set_local_description(offer)
await signaling.send(offer)

# the callee
await pc.set_remote_description(offer)
answer = await pc.create_answer()
await pc.set_local_description(answer)
await signaling.send(answer)

# the caller again
await pc.set_remote_description(answer)
```

`signaling` stands for your own channel. Descriptions are [`RTCSessionDescriptionInit`](../api/models/rtc_session_description_init.md#webrtc.models.rtc_session_description_init.RTCSessionDescriptionInit) objects with a
`type` and an `sdp`, which is all a channel has to carry.

Candidates are gathered after `set_local_description`, and trickle in the `icecandidate` event. Send each one to the
other side, which adds it with [`add_ice_candidate()`](../api/interfaces/rtc_peer_connection.md#webrtc.interfaces.rtc_peer_connection.RTCPeerConnection.add_ice_candidate). The last event has no candidate,
which means gathering is complete. See [MDN: RTCPeerConnection/icecandidate_event](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icecandidate_event).

```python
@pc.on('icecandidate')
async def on_candidate(event):
    if event.candidate is not None:
        await signaling.send(event.candidate.to_json())
```

To skip trickling, send `pc.local_description` once the candidates you need have arrived: it includes them. Don’t wait
for `ice_gathering_state` to be `'complete'`: with a STUN server, an address that can’t reach it, like a VPN’s, holds
gathering open for about 40 seconds. Stop at the first `srflx` candidate, or after a short pause with no new ones.

## Peers on one machine

Like the browsers, connections skip the loopback interface. Where peers on one machine can only reach each other
through it, like on a Mac without Local Network permission, allow it before the first connection:

```python
webrtc.allow_loopback()  # or WRTC_ALLOW_LOOPBACK=1
```

## States

The state of a connection is in [`connection_state`](../api/interfaces/rtc_peer_connection.md#webrtc.interfaces.rtc_peer_connection.RTCPeerConnection.connection_state), and each change fires
`connectionstatechange` (see [MDN: RTCPeerConnection/connectionState](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState)):

```python
@pc.on('connectionstatechange')
def on_state(_event):
    if pc.connection_state == 'failed':
        pc.restart_ice()
```

Enums are strings: `pc.connection_state == webrtc.RTCPeerConnectionState.connected` and
`pc.connection_state == 'connected'` are the same check.

## Stats

[`get_stats()`](../api/interfaces/rtc_peer_connection.md#webrtc.interfaces.rtc_peer_connection.RTCPeerConnection.get_stats) returns an [`RTCStatsReport`](../api/models/rtc_stats.md#webrtc.models.rtc_stats.RTCStatsReport), a mapping of ids to typed
stats objects (see [MDN: RTCStatsReport](https://developer.mozilla.org/en-US/docs/Web/API/RTCStatsReport)):

```python
report = await pc.get_stats()
for stats in report.values():
    if stats.type == 'candidate-pair' and stats.nominated:
        print('round trip time:', stats.current_round_trip_time)
```

## Closing

[`close()`](../api/interfaces/rtc_peer_connection.md#webrtc.interfaces.rtc_peer_connection.RTCPeerConnection.close) stops the transports and the media at once: its transceivers read stopped
right away, and remote tracks end with their `ended` event. A closed connection can’t be reused, so create a new one
to connect again.

Calls pending when the connection closes are cancelled: they raise [`asyncio.CancelledError`](https://docs.python.org/3/library/asyncio-exceptions.html#asyncio.CancelledError).

A connection with handlers or a call in flight stays alive until it’s closed (see [Events](events.md)).
