ICE

ICE (Interactive Connectivity Establishment, RFC 8445) — фреймворк для установки соединения между пирами наилучшим доступным способом. Комбинирует STUN, TURN, локальные IP и пытается все варианты параллельно.

Проблема

Между двумя пирами могут быть разные варианты пути:

Мы не знаем заранее что сработает. ICE пробует всё сразу и выбирает лучшее.

Три шага

  1. Gathering. Каждый пир собирает список своих кандидатов (candidate) — endpoint'ов через которые его можно достать.
  2. Exchanging. Пиры обмениваются списками через сигналинг (SDP-offer/answer).
  3. Checking. Каждый пир пробует все пары (свой кандидат — чужой кандидат) через STUN Binding request. Успешные помечает.
  4. Nominating. Выбирается лучшая пара по priority и включается для реального трафика.

Типы кандидатов

ТипКак полученPriority
hostЛокальный IP (192.168.x.x, 10.x.x.x, публичный если есть)~2113
srflx (server reflexive)Внешний IP от STUN-сервера~1677
prflx (peer reflexive)Обнаружен в процессе connectivity check~1854
relayRelayed адрес от TURN~5 (низший)

Формула priority включает preference типа и IP-family (IPv6 обычно выше). Host лучше srflx, srflx лучше relay.

SDP пример

a=candidate:1 1 udp 2113937151 192.168.1.100 54321 typ host
a=candidate:2 1 udp 1677729535 85.140.10.50 54321 typ srflx raddr 192.168.1.100 rport 54321
a=candidate:3 1 udp 41886719 turn.example.com 49152 typ relay raddr 85.140.10.50 rport 54322

Trickle ICE

Стандартный ICE ждёт пока все кандидаты соберутся, потом шлёт SDP. Это медленно (STUN может занимать сотни мс).

Trickle ICE (RFC 8838) — шлём каждого кандидата по мере обнаружения через отдельные сообщения сигналинга. Экономит ~500-2000 мс на установке соединения.

ICE-lite

Упрощённый режим для серверов с публичным IP (media-серверы, TURN). Только host-кандидаты, не делает свои connectivity checks — только отвечает. WebRTC SFU (Selective Forwarding Units) типа Janus, Jitsi Videobridge, mediasoup обычно ICE-lite.

Aggressive nomination vs Regular

Все браузеры используют aggressive.

ICE restart

Если соединение прервалось (IP клиента поменялся — переключился с Wi-Fi на LTE) — можно перезапустить ICE без разрыва media. WebRTC делает это автоматически через createOffer({iceRestart: true}).

ICE в WebRTC

const pc = new RTCPeerConnection({
  iceServers: [...],
  iceTransportPolicy: 'all',       // или 'relay' чтобы форсировать TURN
  iceCandidatePoolSize: 10          // pre-gather кандидатов
});

pc.onicecandidate = e => {
  if (e.candidate) {
    signaling.send({ candidate: e.candidate });  // trickle
  }
};

pc.oniceconnectionstatechange = () => {
  console.log(pc.iceConnectionState);  // new → checking → connected → completed
};

См. также