ICE
ICE (Interactive Connectivity Establishment, RFC 8445) — фреймворк для установки соединения между пирами наилучшим доступным способом. Комбинирует STUN, TURN, локальные IP и пытается все варианты параллельно.
Проблема
Между двумя пирами могут быть разные варианты пути:
- Прямое подключение по локальной сети (если оба в одном LAN).
- Прямое подключение через интернет (если оба публичные).
- Через NAT hole-punching (если STUN помог).
- Через TURN-релей (если больше ничего не сработало).
Мы не знаем заранее что сработает. ICE пробует всё сразу и выбирает лучшее.
Три шага
- Gathering. Каждый пир собирает список своих кандидатов (candidate) — endpoint'ов через которые его можно достать.
- Exchanging. Пиры обмениваются списками через сигналинг (SDP-offer/answer).
- Checking. Каждый пир пробует все пары (свой кандидат — чужой кандидат) через STUN Binding request. Успешные помечает.
- 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 |
| relay | Relayed адрес от 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
- Regular nomination — сначала находим ВСЕ рабочие пары, потом выбираем лучшую. Медленнее.
- Aggressive — берём первую же рабочую. Быстрее, но не оптимально.
Все браузеры используют 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
};