Подключение #
Почему WebRTC нужна выделенная подсистема для подключения? #
Большинство приложений, развернутых сегодня, устанавливают клиент-серверные соединения. Клиент-серверное соединение требует, чтобы сервер имел стабильный, известный транспортный адрес. Клиент связывается с сервером, и сервер отвечает.
WebRTC не использует клиент-серверную модель, а устанавливает одноранговые (P2P) соединения. В P2P-соединении задача создания соединения равномерно распределена между обоими пирами. Это связано с тем, что транспортный адрес (IP и порт) в WebRTC не может быть предположен и может даже измениться во время сеанса. WebRTC соберет всю доступную информацию и приложит все усилия для достижения двунаправленной связи между двумя WebRTC-агентами.
Установление одноранговой связности может быть сложным. Эти агенты могут находиться в разных сетях без прямой связности. В ситуациях, где прямая связность существует, все еще могут возникать другие проблемы. В некоторых случаях ваши клиенты не говорят на одних сетевых протоколах (UDP <-> TCP) или используют разные версии IP (IPv4 <-> IPv6).
Несмотря на эти сложности при настройке P2P-соединения, вы получаете преимущества перед традиционной клиент-серверной технологией благодаря следующим атрибутам, которые предлагает WebRTC.
Уменьшенные затраты на пропускную способность #
Поскольку медиакоммуникация происходит непосредственно между пирами, вам не нужно платить за, или размещать отдельный сервер для передачи медиа.
Нижняя задержка #
Связь быстрее, когда она прямая! Когда пользователь должен запускать все через ваш сервер, это замедляет передачи.
Безопасная E2E-связь #
Прямая связь безопаснее. Поскольку пользователи не маршрутизируют данные через ваш сервер, им даже не нужно доверять вам, что вы не расшифруете их.
Как это работает? #
Процесс, описанный выше, называется Установление интерактивной связности (ICE). Другой протокол, который предшествует WebRTC.
ICE - это протокол, который пытается найти лучший способ связи между двумя ICE-агентами. Каждый ICE-агент публикует способы, которыми он достижим, это называется кандидатами. Кандидат - это по сути транспортный адрес агента, который он считает достижимым для другого пира. ICE затем определяет лучшую пару кандидатов.
Фактический процесс ICE описывается более подробно позже в этой главе. Для понимания того, почему существует ICE, полезно понять, какие сетевые поведения мы преодолеваем.
Сетевые реальные ограничения #
ICE - это все о преодолении ограничений реальных сетей. Прежде чем мы исследуем решение, давайте поговорим о реальных проблемах.
Не в одной сети #
Большую часть времени другой WebRTC-агент даже не будет в той же сети. Обычный звонок обычно происходит между двумя WebRTC-агентами в разных сетях с отсутствием прямой связности.
Ниже приведен график двух разных сетей, соединенных через публичный интернет. В каждой сети у вас два хоста.
Для хостов в одной сети соединение очень просто. Связь между 192.168.0.1 -> 192.168.0.2
легко сделать! Эти два хоста могут подключиться друг к другу без какой-либо внешней помощи.
Однако хост, использующий Router B
, не имеет способа напрямую получить доступ к чему-либо за Router A
. Как вы можете сказать разницу между 192.168.0.1
за Router A
и тем же IP за Router B
? Это частные IP! Хост, использующий Router B
, мог бы отправить трафик непосредственно к Router A
, но запрос завершится там. Как Router A
знает, какой хост он должен передать сообщение?
Протокольные ограничения #
Некоторые сети вообще не позволяют трафик UDP, или, возможно, они не позволяют TCP. Некоторые сети могут иметь очень низкий MTU (Maximum Transmission Unit). Существует множество переменных, которые сетевые администраторы могут изменять, что может сделать связь сложной.
Правила брандмауэра/IDS #
Другой - “Глубокая проверка пакетов” и другие интеллектуальные фильтры. Некоторые сетевые администраторы будут запускать программное обеспечение, которое пытается обрабатывать каждый пакет. Часто это программное обеспечение не понимает WebRTC, поэтому оно блокирует его, потому что оно не знает, что делать, например, обрабатывая пакеты WebRTC как подозрительные UDP-пакеты на произвольном порту, который не белый.
NAT-маппинг #
NAT (Network Address Translation) маппинг - это волшебство, которое делает возможным подключение WebRTC. Это как WebRTC позволяет двум пирам в совершенно разных подсетях общаться, решая проблему “не в одной сети” выше. Хотя это создает новые вызовы, давайте объясним, как работает NAT-маппинг сначала.
Он не использует ретрансляцию, прокси или сервер. Снова у нас есть Agent 1
и Agent 2
, и они находятся в разных сетях. Однако трафик течет полностью через. Визуализируется это так:
Чтобы сделать эту связь, вы устанавливаете NAT-маппинг. Agent 1 использует порт 7000 для установки WebRTC-соединения с Agent 2. Это создает привязку 192.168.0.1:7000
к 5.0.0.1:7000
. Это затем позволяет Agent 2 достичь Agent 1, отправляя пакеты на 5.0.0.1:7000
. Создание NAT-маппинга, как в этом примере, похоже на автоматизированную версию настройки портового перенаправления в вашем роутере.
Недостатком NAT-маппинга является то, что нет единого вида маппинга (например, статическое портовое перенаправление), и поведение несогласовано между сетями. ISPs и производители оборудования могут делать это по-разному. В некоторых случаях сетевые администраторы могут даже отключить его.
Хорошая новость в том, что полный диапазон поведений понятен и наблюдаем, поэтому ICE-агент способен подтвердить, что он создал NAT-маппинг, и атрибуты маппинга.
Документ, описывающий эти поведения, - RFC 4787.
Создание маппинга #
Создание маппинга - это самая простая часть. Когда вы отправляете пакет по адресу вне вашей сети, создается маппинг! NAT-маппинг - это временный публичный IP и порт, который выделяется вашим NAT. Исходящее сообщение будет переписано, чтобы иметь свой источник, заданный новым адресом маппинга. Если сообщение отправлено на маппинг, оно будет автоматически маршрутизироваться обратно к хосту внутри NAT, который его создал. Детали вокруг маппинга - это то, где все усложняется.
Поведение создания маппинга #
Создание маппинга делится на три разных категории:
Конечная точка, независимая от маппинга #
Создается один маппинг для каждого отправителя внутри NAT. Если вы отправите два пакета на два разных удаленных адреса, маппинг будет переиспользован. Оба удаленных хоста увидят один и тот же IP-адрес и порт. Если удаленные хосты отвечают, они будут отправлены обратно к той же локальной слушательной точке.
Это лучший сценарий. Для работы звонка, по крайней мере, одна сторона МОЖЕТ быть этого типа.
Адрес, зависимый от маппинга #
Создается новый маппинг каждый раз, когда вы отправляете пакет по новому адресу. Если вы отправите два пакета на разные хосты, создадутся два маппинга. Если вы отправите два пакета на один удаленный хост, но разные порты назначения, новый маппинг НЕ будет создан.
Адрес и порт, зависимый от маппинга #
Создается новый маппинг, если удаленный IP или порт отличается. Если вы отправите два пакета на один удаленный хост, но разные порты назначения, создается новый маппинг.
Поведение фильтрации маппинга #
Поведение фильтрации - это правила, которые определяют, кто может использовать маппинг. Они делятся на три похожие категории:
Конечная точка, независимая от фильтрации #
Кто угодно может использовать маппинг. Вы можете поделиться маппингом с несколькими другими пирами, и они могут все отправить трафик к нему.
Адрес, зависимый от фильтрации #
Только хост, для которого создан маппинг, может использовать маппинг. Если вы отправите пакет на хост A
, вы можете получить ответ только от этого же хоста. Если хост B
попытается отправить пакет на этот маппинг, он будет проигнорирован.
Адрес и порт, зависимый от фильтрации #
Только хост и порт, для которых создан маппинг, могут использовать этот маппинг. Если вы отправите пакет на A:5000
, вы можете получить ответ только от этого же хоста и порта. Если A:5001
попытается отправить пакет на этот маппинг, он будет проигнорирован.
Обновление маппинга #
Рекомендуется, чтобы если маппинг не использовался в течение 5 минут, он должен быть уничтожен. Это полностью зависит от ISP или производителя оборудования.
STUN #
STUN (Session Traversal Utilities for NAT) - это протокол, созданный специально для работы с NAT. Это еще одна технология, которая предшествует WebRTC (и ICE!). Он определен в RFC 8489, который также определяет структуру STUN-пакета. Протокол STUN также используется ICE/TURN.
STUN полезен, потому что он позволяет программному созданию NAT-маппингов. До STUN мы могли создать NAT-маппинг, но у нас не было ни малейшего представления о том, какой у него IP-адрес и порт! STUN не только дает вам возможность создать маппинг, но и предоставляет детали, чтобы вы могли поделиться ими с другими, так что они могут отправить трафик обратно к вам через маппинг, который вы только что создали.
Давайте начнем с базового описания STUN. Позже мы расширим TURN и использование ICE. На данный момент мы просто опишем поток запрос/ответ для создания маппинга. Затем мы поговорим о том, как получить детали, чтобы поделиться ими. Это процесс, который происходит, когда у вас есть stun:
сервер в ваших ICE-URL для WebRTC PeerConnection. В сущности, STUN помогает конечной точке за NAT определить, какой маппинг был создан, запрашивая STUN-сервер за пределами NAT, чтобы сообщить, что он наблюдает.
Структура пакета #
Каждый STUN-пакет имеет следующую структуру:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0| STUN Message Type | Message Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Magic Cookie |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Transaction ID (96 bits) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Тип STUN-сообщения #
Каждый STUN-пакет имеет тип. На данный момент нам важно следующее:
- Запрос привязки -
0x0001
- Ответ привязки -
0x0101
Чтобы создать NAT-маппинг, мы делаем Запрос привязки
. Затем сервер отвечает с Ответ привязки
.
Длина сообщения #
Это длина секции Данные
. Эта секция содержит произвольные данные, определяемые Тип сообщения
.
Magic Cookie #
Фиксированное значение 0x2112A442
в сетевом порядке байтов, оно помогает отличить трафик STUN от других протоколов.
Transaction ID #
96-битный идентификатор, который уникально идентифицирует запрос/ответ. Это помогает вам сопоставить ваши запросы и ответы.
Данные #
Данные будут содержать список STUN-атрибутов. STUN-атрибут имеет следующую структуру:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Value (variable) ....
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
STUN Запрос привязки
не использует атрибуты. Это означает, что STUN Запрос привязки
содержит только заголовок.
STUN Ответ привязки
использует атрибут XOR-MAPPED-ADDRESS (0x0020)
. Этот атрибут содержит IP и порт. Это IP и порт NAT-маппинга, который создается!
Создание NAT-маппинга #
Создание NAT-маппинга с использованием STUN - это просто отправка одного запроса! Вы отправляете STUN Запрос привязки
на STUN-сервер. STUN-сервер затем отвечает с STUN Ответ привязки
.
Этот STUN Ответ привязки
будет содержать Mapped Address
. Mapped Address
- это как STUN-сервер видит вас и является вашим NAT-маппингом
.
Mapped Address
- это то, что вы бы поделились, если хотели, чтобы кто-то отправил пакеты к вам.
Люди также называют Mapped Address
вашим Общественным IP
или Серверный рефлексивный кандидат
.
Определение типа NAT #
К сожалению, Mapped Address
может быть бесполезен во всех случаях. Если это Адрес, зависимый
, только STUN-сервер может отправить трафик обратно к вам. Если вы поделились им и другой пир попытался отправить сообщения, они будут отброшены. Это делает его бесполезным для общения с другими. Возможно, вы обнаружите, что случай Адрес, зависимый
на самом деле решаем, если хост, который запускает STUN-сервер, также может передавать пакеты для вас к пиру! Это приводит нас к решению, использующему TURN ниже.
RFC 5780 определяет метод для запуска теста, чтобы определить ваш тип NAT. Это полезно, потому что вы бы знали заранее, возможно ли прямое подключение.
TURN #
TURN (Traversal Using Relays around NAT) определен в RFC 8656 является решением, когда прямое подключение невозможно. Это может быть из-за того, что у вас два несовместимых типа NAT, или, возможно, они не могут говорить на одном и том же протоколе! TURN также может использоваться для целей конфиденциальности. Запуская все ваше общение через TURN, вы скрываете фактический адрес клиента.
TURN использует отдельный сервер. Этот сервер действует как прокси для клиента. Клиент подключается к TURN-серверу и создает Allocation
. Создание Allocation
позволяет клиенту получить временный IP/Port/Протокол, который может использоваться для отправки трафика обратно к клиенту. Этот новый слушатель известен как Relayed Transport Address
. Думайте об этом как о пересылаемом адресе, вы его раздаете, чтобы другие могли отправить вам трафик через TURN! Для каждого пира вы даете Relay Transport Address
, вы должны создать новую Permission
, чтобы разрешить общение с вами.
Когда вы отправляете исходящий трафик через TURN, он отправляется через Relayed Transport Address
. Когда удаленный пир получает трафик, он видит, что он приходит от TURN-сервера.
Жизненный цикл TURN #
Следующее - это все, что должен сделать клиент, который хочет создать TURN-allocation. Общение с кем-то, кто использует TURN, не требует изменений. Другой пир получает IP и порт, и они общаются с ним, как с любым другим хостом.
Allocation #
Allocation - это ядро TURN. Allocation
- это по сути “TURN-сессия”. Чтобы создать TURN-allocation, вы общаетесь с TURN Server Transport Address
(обычно порт 3478
).
При создании Allocation
вам нужно предоставить следующее:
- Имя пользователя/Пароль - Создание TURN-allocation требует аутентификации.
- Transport Allocation - Транспортный протокол между сервером (
Relayed Transport Address
) и пирами, может быть UDP или TCP. - Even-Port - Вы можете запросить последовательные порты для нескольких allocation, не относящихся к WebRTC.
Если запрос выполнен, вы получаете ответ от TURN-сервера со следующими STUN-атрибутами в секции Данные
:
XOR-MAPPED-ADDRESS
-Mapped Address
TURN Client
. Когда кто-то отправляет данные наRelayed Transport Address
, это куда они перенаправляются.RELAYED-ADDRESS
- Это адрес, который вы выдаете другим клиентам. Если кто-то отправляет пакет на этот адрес, он передается TURN-клиенту.LIFETIME
- Через сколько времени эта TURN-allocation уничтожается. Вы можете продлить срок действия, отправив запросRefresh
.
Permissions #
Удаленный хост не может отправить в ваш Relayed Transport Address
, пока вы не создадите для него разрешение. Когда вы создаете разрешение, вы говорите TURN-серверу, что этот IP и порт разрешен для отправки входящего трафика.
Удаленный хост должен отправить STUN Binding Request
на TURN-сервер. Обычная ошибка - это то, что удаленный хост отправит STUN Binding Request
на другой сервер. Они затем попросят вас создать разрешение для этого IP.
Предположим, вы хотите создать разрешение для хоста за Адрес, зависимым маппингом
. Если вы сгенерируете Mapped Address
от другого TURN-сервера, все входящие трафик будет отброшен. Каждый раз, когда они общаются с разным хостом, генерируется новый маппинг. Разрешения истекают через 5 минут, если они не обновляются.
SendIndication/ChannelData #
Эти два сообщения предназначены для TURN-клиента, чтобы отправить сообщения удаленному пиру.
SendIndication - это самодостаточное сообщение. В нем находится данные, которые вы хотите отправить, и кто вы хотите отправить их. Это бесполезно, если вы отправляете много сообщений удаленному пиру. Если вы отправите 1,000 сообщений, вы повторите их IP-адрес 1,000 раз!
ChannelData позволяет вам отправлять данные, но не повторять IP-адрес. Вы создаете Channel с IP и портом. Затем вы отправляете с ChannelId, и IP и порт будут заполнены с серверной стороны. Это лучший выбор, если вы отправляете много сообщений.
Обновление #
Allocation уничтожаются автоматически. TURN-клиент должен обновлять их раньше, чем LIFETIME
, указанный при создании allocation.
Использование TURN #
Использование TURN существует в двух формах. Обычно один пир действует как “TURN-клиент”, а другой - как прямое общение. В некоторых случаях у вас может быть использование TURN с обеих сторон, например, потому что оба клиента находятся в сетях, которые блокируют UDP, и, следовательно, подключение к соответствующим TURN-серверам происходит через TCP.
Эти диаграммы помогают проиллюстрировать, как это может выглядеть.
Одна TURN-allocation для общения #
Две TURN-allocations для общения #
ICE #
ICE (Interactive Connectivity Establishment) - это как WebRTC подключает два агента. Определен в RFC 8445, это еще одна технология, которая предшествует WebRTC! ICE - это протокол для установления связи. Он определяет все возможные маршруты между двумя пирами и затем обеспечивает, что вы остаетесь подключенными.
Эти маршруты известны как Candidate Pairs
, которые являются парой локального и удаленного транспортного адреса. Это где STUN и TURN вступают в игру с ICE. Эти адреса могут быть ваш локальный IP-адрес плюс порт, NAT-маппинг
, или Relayed Transport Address
. Каждая сторона собирает все адреса, которые хотят использовать, обмениваются ими и затем пытаются подключиться!
Два ICE-агента общаются с помощью пакетов ICE ping (или формально называемых проверками связности) для установления связи. После установления связи они могут отправлять все, что хотят. Это будет как использование обычного сокета. Эти проверки используют протокол STUN.
Создание ICE-агента #
ICE-агент либо Controlling
или Controlled
. Controlling
Agent - это тот, кто решает выбранную Candidate Pair
. Обычно пир, отправляющий офер, является контролирующей стороной.
Каждая сторона должна иметь user fragment
и password
. Эти два значения должны быть обменены перед началом проверки связности. User fragment
отправляется в открытом виде и полезен для демультиплексирования нескольких ICE-сессий. Password
используется для генерации атрибута MESSAGE-INTEGRITY
. В конце каждого STUN-пакета есть атрибут, который является хэшем всего пакета с использованием Password
в качестве ключа. Это используется для аутентификации пакета и обеспечения того, что он не был изменен.
Для WebRTC все эти значения распределяются через Session Description
, как описано в предыдущей главе.
Сбор кандидатов #
Теперь нам нужно собрать все возможные адреса, которые мы достижимы. Эти адреса известны как кандидаты.
Хост #
Хост-кандидат слушает непосредственно на локальном интерфейсе. Это может быть UDP или TCP.
mDNS #
mDNS-кандидат похож на хост-кандидат, но IP-адрес скрыт. Вместо того, чтобы сообщать другой стороне ваш IP-адрес, вы даете им UUID как имя хоста. Затем вы устанавливаете многоадресный слушатель и отвечаете, если кто-то запросит UUID, который вы опубликовали.
Если вы находитесь в той же сети, что и агент, вы можете найти друг друга через Multicast. Если вы не находитесь в той же сети, вы не сможете подключиться (если сетевой администратор явно настроил сеть, чтобы разрешить пакеты Multicast для прохождения).
Это полезно для целей конфиденциальности. Пользователь мог бы узнать ваш локальный IP-адрес через WebRTC с хост-кандидатом (без даже пытаясь подключиться к вам), но с mDNS-кандидатом, теперь они получают случайный UUID.
Серверный рефлексивный #
Серверный рефлексивный кандидат генерируется путем выполнения STUN Binding Request
к STUN-серверу.
Когда вы получаете STUN Binding Response
, XOR-MAPPED-ADDRESS
является вашим серверным рефлексивным кандидатом.
Пир-рефлексивный #
Пир-рефлексивный кандидат создается, когда удаленный пир получает ваш запрос от адреса, который ранее был неизвестен для пира. При получении он сообщает (отражает) этот адрес обратно к вам. Пир знает, что запрос был отправлен вами, а не кем-то другим, потому что ICE - это аутентифицированный протокол.
Это обычно происходит, когда Хост-кандидат
общается с Серверный рефлексивный кандидат
, который находится в другой подсети, что приводит к созданию нового NAT-маппинга
. Помните, мы сказали, что проверки связности на самом деле STUN-пакеты? Формат ответа STUN естественным образом позволяет пиру сообщить обратно пир-рефлексивный адрес.
Relay #
Relay-кандидат генерируется путем использования TURN-сервера.
После начального рукопожатия с TURN-сервером вам дается RELAYED-ADDRESS
, это ваш Relay-кандидат.
Проверка связности #
Теперь мы знаем user fragment
, password
удаленного агента и кандидаты. Мы можем теперь попытаться подключиться! Каждый кандидат связывается с каждым другим. Так что если у вас 3 кандидата с каждой стороны, теперь у вас 9 пар кандидатов.
Визуально это выглядит так:
Выбор кандидата #
Контролирующий и контролируемый агенты начинают отправлять трафик на каждой паре. Это необходимо, если один агент находится за Адрес, зависимым маппингом
, это приведет к созданию Пир-рефлексивного кандидата
.
Каждая Candidate Pair
, которая видела сетевой трафик, затем повышается до Valid Candidate
пары. Контролирующий агент затем выбирает одну Valid Candidate
пару и номинирует ее. Это становится Nominated Pair
. Контролирующий и контролируемый агенты затем пытаются установить один последний раунд двунаправленной связи. Если это удается, Nominated Pair
становится Selected Candidate Pair
! Эта пара используется для остальной части сессии.
Перезапуски #
Если Selected Candidate Pair
перестает работать по какой-либо причине (NAT-маппинг истек, TURN-сервер упал) ICE-агент переходит в состояние Failed
. Оба агента могут быть перезапущены, и процесс будет повторен полностью.