Skip to main content
ICE candidates represent potential network endpoints for establishing peer-to-peer connections. Each candidate describes a transport address (IP and port) that can be used for communication.

What is a Candidate?

A candidate is defined by the Candidate interface in candidate.go:22:

Candidate Types

ICE defines four candidate types (from candidatetype.go:12-18):

Host Candidates

Host candidates represent direct addresses from the local machine’s network interfaces.
Host candidates are the most efficient connection type as they enable direct peer-to-peer communication without intermediary servers.
Characteristics:
  • Gathered from local network interfaces (WiFi, Ethernet, VPN, etc.)
  • Highest priority (126) for preference
  • No additional infrastructure required
  • May be private IPs (192.168.x.x, 10.x.x.x) behind NAT
Creation:
Implementation: candidate_host.go:32-70

Server Reflexive Candidates

Server reflexive (srflx) candidates represent the public IP address and port as seen by a STUN server. Characteristics:
  • Discovered by sending STUN requests to a STUN server
  • Shows how the local endpoint appears from outside the NAT
  • Priority: 100
  • Includes related address (the local host candidate that was used)
How it works:
  1. Agent sends STUN Binding Request to STUN server
  2. STUN server responds with XOR-MAPPED-ADDRESS
  3. This address becomes a server reflexive candidate
Implementation: candidate_server_reflexive.go:30-68 Gathering: gather.go:735-824

Peer Reflexive Candidates

Peer reflexive (prflx) candidates are discovered during connectivity checks, not during gathering. Characteristics:
  • Learned when receiving binding requests from unexpected addresses
  • Priority: 110 (higher than server reflexive!)
  • Occurs when peers are behind symmetric NATs
  • Automatically created by the agent
When created: From agent.go:1562-1589, when an inbound binding request arrives from an unknown address:
Implementation: candidate_peer_reflexive.go:32-66
Peer reflexive candidates have higher priority than server reflexive because they represent the actual path being used for connectivity checks, which may differ from the path through the STUN server.

Relay Candidates

Relay candidates represent addresses allocated on TURN (Traversal Using Relays around NAT) servers. Characteristics:
  • Obtained by allocating a relay address on a TURN server
  • Priority: 0 (lowest - used as last resort)
  • All media flows through the relay server
  • Most reliable but highest latency and server cost
  • Supports UDP, TCP, TLS, and DTLS transport
Relay protocols (from candidate_relay.go:11-18):
Creation:
Implementation: candidate_relay.go:45-130 Gathering: gather.go:827-1047

Candidate Priority

Type Preferences

From candidatetype.go:44-57, RFC 5245 recommended preferences:

Priority Calculation

The full priority formula combines:
  • Type preference (126, 110, 100, or 0)
  • Local preference (65535 by default)
  • Component ID (1 for RTP, 2 for RTCP)
This means:
  • Host candidates are always preferred over reflexive
  • Reflexive are preferred over relay
  • Within the same type, local preference distinguishes interfaces
  • RTP components are slightly preferred over RTCP

Candidate Pair Priority

When forming pairs, the priority is calculated from candidatepair.go:92-129:

Candidate Gathering

The Gathering Process

Gathering is initiated with GatherCandidates() from gather.go:51-77:

Gathering Stages

Implemented in gather.go:196-220:
From gather.go:245-428, host gathering:
  1. Enumerate network interfaces using the configured filters
  2. For each interface address:
    • Apply IP filters (skip loopback unless configured)
    • Apply interface filters (skip Docker, VMs, etc.)
    • Apply location tracking filters (skip IPv6 link-local)
    • Create UDP or TCP socket on available port
  3. Apply NAT 1:1 mappings if configured
  4. Create CandidateHost for each valid address
  5. Add to local candidates and emit via OnCandidate
Special handling for mDNS:
From gather.go:735-824, srflx gathering:
  1. For each STUN URL:
    • Open UDP socket
    • Send STUN Binding Request
    • Wait for response with timeout (default 5s)
    • Extract XOR-MAPPED-ADDRESS
  2. Create CandidateServerReflexive
  3. Associate with local candidate as related address
  4. Add to local candidates and emit
Parallel requests:
From gather.go:827-1047, relay gathering:
  1. For each TURN URL:
    • Connect to TURN server (UDP, TCP, TLS, or DTLS)
    • Authenticate with username/password
    • Send TURN Allocate request
    • Receive allocated relay address
  2. Create CandidateRelay
  3. Maintain TURN client for the allocation lifetime
  4. Add to local candidates and emit
Protocol matrix:

Continual Gathering

Pion ICE supports continual gathering (monitoring for network changes):
From gather.go:89-112, continual gathering:
  1. Performs initial gathering
  2. Monitors network interfaces every 2 seconds (configurable)
  3. Detects new IP addresses
  4. Automatically gathers candidates for new interfaces
  5. Continues until agent is closed

Candidate Selection

Forming Candidate Pairs

The agent forms pairs from local and remote candidates (from agent.go:860-868):

The Checklist

Pairs go through states (from candidatepair_state.go:9-26):

Selection Algorithm

From agent.go:827-858, finding the best valid pair:

Nomination

The controlling agent nominates the selected pair:

Candidate String Format

Candidates are serialized following RFC 5245:
Examples:

Best Practices

Always gather multiple candidate types for the best chance of connectivity. Even if you have a public IP, gathering relay candidates provides a fallback for restrictive networks.
Filter candidates carefully when behind corporate firewalls. Some organizations block UDP or restrict ports, making TCP or relay candidates necessary.
To reduce gathering time:
Be careful not to sacrifice connectivity for speed.
Symmetric NATs allocate different mappings for different destinations. This makes server reflexive candidates ineffective.Indicators:
  • STUN server shows one port
  • Remote peer sees different port
  • Connection only works with relay
Solution: Peer reflexive candidates (automatically created) or relay candidates.
  • ICE Protocol - Understanding the overall ICE process
  • Agents - Managing the ICE agent lifecycle
  • Connectivity - How candidates are tested and selected