WebRTC (Web Real Time Communication) — это проект с открытым исходным кодом, позволяющий создавать одноранговые (P2P) аудио- и видеосвязи через JavaScript API.
Для того чтобы установить P2P-соединение, одноранговые пользователи должны общаться о типах носителей, которыми они хотят обмениваться, сообщить друг другу, когда они хотят начать или прекратить общение, а также должны найти друг друга в сети. Весь этот процесс называется сигнализацией, но нас интересует только последняя часть — как соединить одноранговые узлы, максимально избегая посредников.
Это не так просто, как кажется. Устройства пользователей обычно не имеют публичного IP-адреса или могут быть не допущены до установления какого-либо прямого соединения. Вот почему нам нужна платформа Interactive Connectivity Establishment (ICE).
Nat’ы
Если бы два пользователя совершали телефонный звонок, то один должен был бы только набрать номер телефона другого, а другой должен был бы только принять звонок. Каждый телефонный номер соответствует только одному устройству, а их вполне достаточно, чтобы обеспечить прямое соединение.
С другой стороны, в Интернете исторически не хватало “номеров” для каждого подключенного устройства. С IPv4 было доступно только около 4 миллиардов адресов. Недостаток адресов был решен путем группировки многих устройств под одним общим адресом с маршрутизатором, переводящим адреса в пакеты, проходящие через него. Этот процесс называется трансляцией сетевых адресов (NAT).
Существуют различные типы NAT, но некоторые из них выделяют публичный IP-адрес и порт для потоков UDP (то, что нам нужно). Поэтому, когда нужно создать P2P-соединение с одноранговым узлом, первая задача состоит в том, чтобы выяснить, за каким типом NAT вы находитесь, и если он есть, получить IP-адрес и порт, который сможете дать своему контакту.
STUN
В этом поможет протокол STUN (Session Traversal Utilities for NAT). Необходимо предоставить сервер STUN при попытке установить P2P-соединение. В WebRTC вы предоставляете его при создании объекта JavaScript, представляющего соединение:
Чтобы определить, находитесь ли вы за NAT, и получить публичный IP-адрес, если это возможно, агент ICE отправит запрос на указанный вами сервер STUN. Если NAT есть, он установит свой публичный адрес и порт в заголовке сообщения. Сервер STUN попытается пинговать этот публичный адрес с разных IP-адресов, чтобы проверить, находитесь ли вы за NAT, и если да, то какой это тип. Если все пойдет правильно, то сервер STUN вернет адрес и порт.
Вы NAT STUN сервер
|------ STUN запрос ------>| |
| src:198.145.1.2:3000 | |
| |------ STUN запрос ------>|
| | src:123.11.22.13:8888 |
| | |
| |<------STUN ответ --------|
| | ext:128.11.22.13:8888 |
|<------STUN ответ---------| |
| ext:128.11.12.13:8888 | |
Если все работает так же гладко для контакта, с которым вы пытаетесь общаться, он также получит свой публичный адрес. Но на этом проблемы не заканчиваются.
Реализации NAT
Не все NAT реализованы одинаково и могут отличаться в том, как они позволяют пакетам проходить. Некоторые реализации NAT, такие как NAT один к одному, позволят установить P2P-соединение. Некоторые, например симметричные, этого не делают.
NAT один к одному (или Full Cone NAT)
В этой реализации, как только внутренний IP-адрес/порт duo сопоставляется с внешним IP-адресом/портом duo, все пакеты, поступающие на внешний адрес/порт, независимо от того, откуда они поступают, будут отправлены через исходный внутренний адрес/порт.
Если за таким NAT стоят одноранговые узлы, то для установления P2P-соединения достаточно получить публичный IP-адрес и порт обоих одноранговых узлов.
Симметричный NAT
В симметричных NAT’ах внешний IP-адрес/порт зависит от внутреннего IP-адреса/порта duo и назначения. Запросы от 198.145.1.2: 3000 к серверу TURN сопоставляются с заданным IP-адресом и портом внешнего источника. Но если один и тот же внутренний хост отправляет пакет в другое место назначения, например в одноранговый узел, с которым он пытается связаться, внешний IP-адрес/порт будет отличаться. Кроме того, внешний хост должен получить пакет от внутреннего хоста, прежде чем он сможет отправить пакет обратно.
Если контакты находятся за симметричным NAT, они не смогут общаться. Вот почему нам нужно другое решение: TURN.
TURN
Когда прямое соединение не может быть установлено, связь должна проходить через сервер TURN. TURN обозначает “Traversal Using Relays around NAT”. Как следует из названия, соединение будет проходить через ретрансляционный сервер.
Это очевидно ухудшает производительность и имеет финансовые издержки. В то время как сервер STUN имеет дело с очень маленькими и легкими запросами, сервер TURN ретранслирует весь разговор, что генерирует гораздо больше трафика. Вот почему вы можете найти публичные серверы STUN, но не публичные серверы TURN (по крайней мере, ни один владелец которых не знает, что он является публичным).
ICE в WebRTC
Чтобы быть установленным в TURN, для WebRTConnection нужно указать URL-адрес вашего сервера в объекте RCTPeerConnection:
Серверы TURN по причине, описанной выше, обычно защищены паролем.
Агент ICE сначала попытается установить соединение непосредственно между одноранговыми узлами и переключится на опцию TURN только в том случае, если это не сработает. Вам не нужно заботиться об этом самостоятельно, нужно только слушать событие onicecandidate RTCPeerConnection. Оно будет срабатывать каждый раз, когда обнаруживается кандидат ICE. Затем вам нужно отправить кандидата своему контакту через свой сигнальный механизм:
Когда вы получаете кандидата от своего контакта, нужно доставить его агенту ICE, вызвав addIceCandidate. Остальная часть переговоров и окончательный отбор кандидатов затем осуществляется агентом ICE. В конце переговоров о кандидате, в случае успеха, коллеги могут начать общение.
Читайте также:
- Нерассказанная история: исключение Stale Element Reference в Selenium
- Новый взгляд на старые истины: принцип «Не повторяйся!» (DRY)
- Почему нельзя разрешать поля GraphQL как конечные точки REST
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Dornhoth: WebRTC: the ICE Framework, STUN and TURN Servers