WebRTC and Signaling
Two browsers want to send video directly to each other. They can’t just open a TCP connection: they’re behind NAT, firewalls, and don’t know each other’s public IP addresses. WebRTC solves peer-to-peer media transport. But before peers can connect, they need a signaling server to exchange connection metadata. WebRTC handles the media; signaling handles the handshake.
The Signaling Problem#
WebRTC is transport-agnostic about signaling: it doesn’t specify how peers find each other or exchange connection parameters. You implement signaling yourself, typically over WebSockets.
The handshake uses SDP (Session Description Protocol): a text format describing what codecs the peer supports, what network addresses it can be reached at, and what media streams it wants to send. The flow:
Caller generates an SDP offer and sends it to the signaling server. Signaling server relays it to the callee. Callee generates an SDP answer and sends it back through the signaling server. Once offer and answer are exchanged, peers have enough information to attempt a direct connection.
ICE and NAT Traversal#
SDP contains ICE candidates: the network addresses each peer can potentially be reached at. Candidates come from three sources:
Host candidates: the peer’s local network IP (192.168.x.x). Useless for peers on different networks.
Server-reflexive candidates: the peer’s public IP as seen by a STUN server. A STUN server returns “your public IP is X, your port is Y.” This works when both peers are behind NAT that supports hairpinning.
Relay candidates: a TURN server that relays traffic between peers when direct connection fails. TURN is the fallback: always works, but adds latency and burns server bandwidth. About 15-20% of WebRTC connections require TURN.
ICE tries candidates in priority order: host first, server-reflexive second, relay last.
Signaling Server Scale#
The signaling server is stateful: it must know which WebSocket connection belongs to which user to relay messages correctly. At scale, users on different signaling server instances can’t reach each other without a coordination layer. Solutions: consistent hashing to route users to the same instance, or a pub/sub backbone (Redis pub/sub) between instances.
At Oracle#
Oracle Cloud Infrastructure uses similar signaling for its remote desktop product: browser-to-cloud-VM connections over WebRTC. The signaling service ran as a stateless relay with a Redis pub/sub layer underneath, so any signaling server instance could relay messages for any session. TURN was required for roughly 18% of connections due to corporate firewall restrictions blocking UDP. Those connections had 30-40ms higher latency than direct connections.
What I’m Learning#
WebRTC separates media transport (peer-to-peer, fast) from signaling (flexible, you implement it). The complexity is in NAT traversal: most peers can connect directly, but the TURN fallback for the minority that can’t is the expensive path that must scale independently.
Have you built WebRTC applications and what percentage of your connections required TURN relay?