Что такое WebRTC? Объясняем простым языком — обложка

Большинство материалов по WebRTC сосредоточены на написании кода и не объясняют саму технологию. Давайте разберёмся глубже: как устанавливается соединение, зачем нужны серверы TURN и STUN, что такое дешифратор и кандидаты сессии.

Введение

WebRTC — это технология для браузеров, которая позволяет клиентам обмениваться видеоданными. Основные её особенности — встроенная поддержка в браузере (не требуются внешние технологии вроде Adobe Flash) и возможность подключения клиентов напрямую, без дополнительных серверов (p2p-соединение).

p2p — соединение, при котором два человека взаимодействуют напрямую, без участия третьих лиц.

Установить p2p-соединение довольно сложно, поскольку компьютеры не всегда имеют публичные IP-адреса. Из-за нехватки адресов IPv4 и соображений безопасности был придуман NAT. Он позволяет создавать локальные сети, например, для домашнего использования. Многие домашние маршрутизаторы поддерживают NAT, поэтому все устройства, подключённые к роутеру, могут выходить в интернет, хотя провайдер обычно выдаёт только один IP-адрес. Публичные IP-адреса уникальны, а частные — нет, поэтому p2p-соединения установить трудно.

Чтобы лучше понять концепцию, рассмотрим три сценария:

1. Оба узла находятся в одной сети

2. Оба узла находятся в разных сетях — частной и публичной

3. Оба узла находятся в разных частных сетях и имеют одинаковые IP-адреса

Первая буква на изображениях выше обозначает тип узла: r — маршрутизатор, p — одноранговый узел.

  1. На первом изображении показана хорошая ситуация. Узлы в своих сетях идентифицируются с помощью IP-адресов и могут напрямую соединяться друг с другом.
  2. На изображении 2 показаны две разные сети с одинаково упорядоченными узлами. Здесь представлены маршрутизаторы, у которых по два сетевых интерфейса — внутренний и внешний. Соответственно, у них два IP-адреса. Обычно узлы имеют только один интерфейс и используют его для связи внутри своей сети. Если им нужно отправить данные за пределы своей системы, они делают это через NAT в маршрутизаторе. Поэтому эти узлы отображаются под внешним IP-адресом маршрутизатора — это их публичный адрес. Так, узел p1 имеет внешний IP (10.50.200.5) и внутренний (192.168.0.200), причём первый адрес является внешним и для всех остальных узлов сети. Узел p2 находится в аналогичной ситуации, поэтому их соединение невозможно, если использовать только внутренние адреса. Можно использовать внешние IP-адреса, но это создаёт проблему: все узлы одной частной сети имеют один и тот же внешний адрес. NAT решает эту проблему.
  3. Что произойдет, если мы решим соединить узлы через их внутренние адреса? Данные не покинут сеть. Чтобы усилить эффект, представьте ситуацию с третьего изображения, где оба узла имеют одинаковые внутренние адреса. Если они используют эти адреса для связи друг с другом, то оба будут общаться сами с собой.

Здесь на помощь приходит WebRTC. Чтобы решить эти проблемы, WebRTC использует протокол ICE, для которого нужны дополнительные серверы: STUN и TURN.

Две фазы WebRTC

Чтобы соединить два узла с помощью протокола WebRTC (или просто RTC, если речь идёт о двух iPhone), нужно выполнить несколько подготовительных шагов для установления соединения. Это первая фаза. Вторая фаза — передача видеоданных.

Хотя WebRTC использует разные протоколы связи (TCP и UDP) и умеет гибко переключаться между ними, у этой технологии нет встроенного способа передачи данных о соединении. Это понятно: организовать связь между двумя p2p-устройствами — задача непростая. Поэтому нужен отдельный, не связанный с WebRTC, способ обмена начальной информацией. Такой способ может быть реализован через HTTP, WebSocket или даже SMTP — всё зависит от архитектуры системы. Этот метод передачи стартовых данных называют сигнальным механизмом. Передаётся не так много информации — она состоит из двух частей: SDP и Ice Candidate (подробнее об этом можно почитать здесь). SDP отвечает за настройку логического соединения, а Ice Candidate — за установление физического канала связи. Важно понимать: WebRTC сам по себе не передаёт данные, он лишь предоставляет информацию, которую нужно доставить другому узлу. Как только эта информация передана, устройства могут подключиться самостоятельно — и наша помощь больше не нужна. Поэтому сигнальный механизм нужен только на этапе подключения, а не во время передачи видео или аудио.

Итак, рассмотрим первый этап. Он включает несколько шагов. Сначала разберём его для узла, который инициирует соединение, а затем — для узла, который его принимает.

  • Инициатор (вызывающий узел):
  1. Получение локального медиапотока и его передача (getUserMediaStream).
  2. Предложение начать передачу видеоданных (createOffer)
  3. Получение собственного объекта SDP и отправка его через механизм сигнализации (SDP)
  4. Получение собственных объектов Ice candidate и отправка их через механизм сигнализации (Ice candidate)
  5. Получение удалённого медиапотока и его отображение на экране (onAddStream)
  • Получатель (вызываемый узел)
  1. Получение локального медиапотока и его передача (getUserMediaStream)
  2. Предложение начать передачу видеоданных и создание ответа (createAnswer)
  3. Получение собственного объекта SDP и отправка его через механизм сигнализации
  4. Получение собственных объектов Ice candidate и отправка их через механизм сигнализации (Ice candidate)
  5. Получение удалённого медиапотока и его отображение на экране (на AddStream).

Только второй шаг отличается.

Какими бы сложными ни казались эти шаги, на самом деле их всего три: отправка локального медиапотока (шаг 1), настройка параметров соединения (шаги 2–4) и приём удалённого медиапотока (шаг 5). Второй шаг — самый сложный, потому что он состоит из двух частей: нужно установить логическое и физическое соединение. Физическое соединение определяет путь, по которому пакеты данных добираются от одного узла к другому, а логическое — какие параметры использовать для видео и аудио: качество, кодеки и так далее.

Подключите шаги createOffer и createAnswer к этапам передачи объектов SDP и Ice Candidate.

Вскоре мы рассмотрим такие понятия, как MediaStream, SDP и ice Candidate.

Основные сущности

MediaStream

MediaStream — это базовая сущность, состоящая из потоков видео и аудио данных. Существует два типа медиапотоков — локальные и удалённые. Локальные потоки получают данные от устройств ввода (камера, микрофон), удалённые потоки — из сети.

Таким образом, каждый узел имеет локальный и удалённый поток. В WebRTC для этих потоков предусмотрен интерфейс MediaStream, а также его подтип LocalMediaStream, предназначенный специально для локального потока. В JavaScript вы работаете только с первым, но если используете libjingle, то можете взаимодействовать и со вторым.

WebRTC предполагает сложную структуру внутри потока. Каждый поток состоит из нескольких медиадорожек (MediaTrack), а каждая дорожка может включать несколько медиаканалов (MediaChannel). При этом может быть несколько медиапотоков.

Например, мы хотим передать не только видео с собой, но и стол с листом бумаги, на котором будем что-то писать. Нам понадобится два видеоисточника (мы и стол) и один аудиопоток (наше голосовое сопровождение). Очевидно, что мы и стол должны быть в разных потоках, так как они мало связаны между собой. Поэтому у нас будет два медиапотока: один — для нас (с видео и аудио), второй — только для стола (только видео).

Медиапоток должен поддерживать хранение разных типов данных — видео и аудио. Эта возможность заложена в технологию: каждый тип данных представлен через MediaTrack (медиадорожку). У MediaTrack есть особое свойство — kind, которое указывает, является ли дорожка видео- или аудиодорожкой.

Как же всё происходит внутри программы? Мы создаём два медиапотока. Затем — две видеодорожки и одну аудиодорожку. Получаем доступ к камере и микрофону. Указываем каждой дорожке, какую функцию она должна выполнять. Добавляем видео- и аудиодорожки в первый медиапоток, а видеодорожку со второй камеры — во второй медиапоток.

Как различать медиадорожки на другом конце? По функции label, которая есть у каждого медиаканала. У всех медиадорожек эта функция одинаковая.

Итак, если мы можем идентифицировать медиадорожки с помощью метки, почему в этом примере нужно использовать две, а не одну? Вы можете передавать один медиапоток и использовать в нём разные дорожки. Теперь мы подходим к важной особенности медиадорожек — они синхронизируют дорожки внутри себя. Разные медиадорожки не синхронизируются между собой, но все дорожки воспроизводятся одновременно в пределах каждой медиадорожки.

Поэтому, если мы хотим, чтобы наши слова, выражение лица и лист бумаги воспроизводились одновременно, нам нужно использовать одну и ту же медиадорожку. Если синхронность не критична, лучше использовать разные медиадорожки — так изображение будет плавнее.

Если дорожку нужно выключить во время передачи, мы можем использовать функцию enabled медиадорожки.

В заключение стоит подумать о стереозвуке. Стерео — это два разных звуковых сигнала, которые нужно передавать отдельно. Для этого используется MediaChannel. Медиадорожка может включать несколько каналов — например, шесть, если нужен формат 5.1. Каналы внутри одной медиадорожки синхронизируются между собой. При воспроизведении видео обычно задействуется один канал, но можно использовать и несколько, например, чтобы наложить рекламу.

Подведём итог: мы используем медиапоток для передачи видео- и аудиоданных. Данные синхронизируются внутри каждого медиапотока. Если синхронизация не нужна, можно использовать разные медиаканалы. Внутри каждого потока — две медиадорожки: одна для видео, другая для аудио. Дорожек может быть больше, если нужно передавать разные изображения, например собеседника и его стол. Каждая дорожка может содержать несколько каналов, но чаще всего используется только стереозвук.

В простейшем случае у нас не будет видеочата, а будет только один локальный медиапоток из двух дорожек — аудио и видео. Каждая дорожка состоит из одного основного канала. Видеодорожка отвечает за камеру, а аудиодорожка — за микрофон. Медиапоток служит контейнером для обеих дорожек.

Дескриптор сессии (SDP)

На разных компьютерах установлены разные камеры, микрофоны, видеокарты и так далее. У каждого из этих устройств множество параметров. Чтобы передать медиаданные между двумя устройствами в сети, их нужно согласовать. WebRTC делает это автоматически и создаёт специальный объект — SDP. Отправьте этот SDP другому устройству — и вы сможете передавать видео. Но при этом прямая связь между устройствами ещё не установлена.

Здесь может помочь любой сигнальный механизм. SDP можно передавать через сокеты, через людей (например, передать SDP другому узлу по телефону) или даже через обычную почту. Вы получаете готовый SDP — и его нужно просто отправить. Когда другой узел получает SDP, он должен передать его в WebRTC. Файл хранится в виде текста и может редактироваться приложением, но это обычно не требуется. Например, при соединении десктопа и телефона иногда приходится вручную выбирать нужный аудиокодек.

Обычно при установлении соединения обязательно указывается адрес, например, URL. Здесь это не нужно, потому что вы сами будете отправлять данные через механизм сигнализации. Чтобы сообщить WebRTC, что мы хотим установить p2p-соединение, нужно вызвать функцию createOffer. После этого и создания специального обратного вызова появится новый объект SDP, который будет передан в этот же обратный вызов. Вам остаётся только отправить этот объект другому узлу по сети.

Механизм сигнализации помогает передать данные — для этого используется объект SDP. Этот дескриптор сессии приходит извне, поэтому содержит полезную информацию.

Получение этого объекта — сигнал к началу соединения. Поэтому нужно принять его и вызвать функцию createAnswer. Она полностью аналогична createOffer. Ваш обратный вызов получит локальный дескриптор сессии, который затем следует передать через сигнальный механизм обратно.

Стоит отметить, что вызов функции createAnswer возможен только после получения чужого объекта SDP. Это связано с тем, что локальный SDP-объект, который будет сгенерирован при вызове createAnswer, должен опираться на удаленный SDP-объект. Только тогда можно будет согласовать свои настройки видео с настройками собеседника. Также не следует вызывать createAnswer и createOffer до получения локального медиапотока, так как им будет нечего записывать в SDO-объект.

Поскольку WebRTC позволяет изменять SDP-объект, после его получения нужно установить локальный дескриптор. Отправка обратно того, что вернул WebRTC, может показаться странной, но таков протокол. При получении также необходимо установить удалённый дескриптор.

После этого своеобразного «рукопожатия» узлы узнают о возможностях друг друга. Например, если узел 1 поддерживает кодеки A и B, а узел 2 — кодеки B и C, они оба выберут кодек B. Это происходит потому, что узлы обладают информацией как о своих, так и о чужих дескрипторах. Логика соединения определена, и теперь можно передавать медиапотоки. Однако остаётся ещё одна проблема: узлы по-прежнему связаны только через сигнальный канал.

Ice-кандидаты

При установлении соединения адрес узла, с которым нужно соединиться, не указывается. Сначала создаётся логическое соединение, а потом — физическое, хотя раньше порядок был обратным. Однако это не кажется странным, если учесть, что мы используем внешний сигнальный механизм.

Итак, логическое соединение установлено, но ещё нет пути, по которому узлы могут передавать данные. Здесь всё не так просто, но начать можно с простого. Представьте, что узлы находятся в одной частной сети. Как мы знаем, они легко могут соединяться друг с другом через внутренние IP-адреса (или другие адреса, если TCP/IP не используется).

WebRTC сообщает о кандидатах ICE через специальные обратные вызовы. Эти данные тоже приходят в текстовом виде и должны быть переданы через механизм сигнализации, как и дескрипторы сессии. Если дескриптор сессии содержит информацию о настройках камеры и микрофона, то кандидаты описывают наше положение в сети. Отправив их другому узлу, мы позволяем ему установить с нами соединение. Поскольку у него уже есть дескриптор сессии, он сможет принимать данные. А если он не забудет отправить нам свой кандидат (информацию о своём расположении в сети), мы сможем подключиться к нему.

Есть ещё одно отличие от классического взаимодействия клиент-сервер. Общение с HTTP-сервером происходит по схеме «запрос-ответ»: клиент отправляет данные, сервер их обрабатывает и отправляет ответ обратно по адресу, указанному в запросе. В WebRTC обязательно нужно знать два адреса и устанавливать соединение с обеих сторон.

Отличие от дескрипторов сессий в том, что нужно устанавливать только удалённые кандидаты. Редактирование здесь не допускается и не даст результата. В разных реализациях WebRTC кандидаты должны быть добавлены только после согласования дескрипторов сессии.

Итак, почему может быть один дескриптор сессии, но много кандидатов? Потому что расположение в сети определяется не только собственным внутренним IP-адресом, но и адресом внешнего маршрутизатора (одного или нескольких), а также адресами TURN-серверов.

Итак, у нас есть два кандидата в одной сети (рисунок ниже). Как их различить? Только по IP-адресам. Конечно, может использоваться разный транспорт — TCP или UDP, — а также разные порты. Именно эта информация содержится в объекте-кандидате: IP, TRANSPORT, PORT и так далее. Например, возьмём порт 531 и транспорт UDP.

Итак, когда мы находимся внутри узла p1, WebRTC отправит нам следующий кандидат: [10.50.200.5, 531, udp]. Это не точные данные, а просто схема. Если мы находимся внутри узла p2, кандидат изменится на [10.50.150.3, 531, udp]. P1 получит IP и PORT узла p2 через механизм сигнализации и сможет подключиться к p2 напрямую. Фактически, p1 отправит данные на 10.50.150.3:531, надеясь, что они достигнут p2. Принадлежит ли этот адрес p2 или посреднику — не так важно. Главное, что данные будут отправлены на этот адрес и смогут достичь p2.

Пока узлы находятся в одной сети, всё просто: у каждого узла только один кандидат — он сам, и он размещает свой объект в сети. Но если узлы находятся в разных сетях, количество кандидатов резко возрастает.

Давайте рассмотрим более сложный случай. Один узел находится за маршрутизатором (NAT), а второй — в той же сети, что и этот маршрутизатор, например, в интернете.

У этого случая есть своё решение. Домашний маршрутизатор обычно имеет таблицу NAT. Этот механизм позволяет узлам внутри частной сети маршрутизатора взаимодействовать, например, с веб-сайтами.

Предположим, что веб-сервер подключён к интернету напрямую — у него есть публичный IP. Пусть это будет узел p2. Тогда узел p1 (веб-клиент) отправляет запрос на адрес 10.50.200.10. Сначала данные поступают на маршрутизатор r1, точнее — на его внутренний интерфейс 192.168.0.1. После этого маршрутизатор запоминает адрес отправителя (p1) и добавляет его в таблицу NAT. Затем он меняет адрес отправителя на свой (p1r1) и через внешний интерфейс отправляет данные на веб-сервер p2. Сервер обрабатывает запрос, формирует ответ и отправляет его обратно маршрутизатору. Получив данные, маршрутизатор смотрит в таблицу NAT и перенаправляет ответ на узел p1. Таким образом, маршрутизатор выступает посредником.

А что, если несколько устройств из внутренней сети одновременно отправляют запросы во внешнюю сеть? Как маршрутизатор поймёт, кому из них нужно вернуть ответ? Эту проблему решает использование портов. Когда маршрутизатор заменяет IP-адрес устройства на свой, он также подставляет вместо исходного порта уникальный номер. Если два устройства обращаются в интернет, маршрутизатор назначает им разные порты. Когда ответ приходит от веб-сервера, маршрутизатор смотрит на порт и понимает, какое устройство должно получить пакет. Пример приведён ниже.

Возвращаясь к WebRTC и части, где он использует протокол ICE (и, соответственно, кандидатов ICE). Узел p2 имеет одного кандидата — его внутренний IP-адрес 10.50.200.10. Узел p1, находящийся за маршрутизатором с NAT, имеет два кандидата: локальный (192.168.0.200) и публичный адрес маршрутизатора (10.50.200.5). Первый кандидат в данном случае бесполезен, но всё равно генерируется, потому что WebRTC не знает, находится ли удалённый узел в той же сети. Второй кандидат полезен, и, как мы знаем, порт будет играть ключевую роль при проходе через NAT.

Запись в таблице NAT создается только тогда, когда данные покидают внутреннюю сеть. Поэтому узел p1 должен сначала отправить свои данные, и только потом данные от p2 могут достичь p1.

Фактически, оба узла окажутся за NAT. Чтобы создать запись в таблице NAT каждого маршрутизатора, узлы должны отправить что-то удалённому узлу, но на этот раз ни один из них не сможет до него добраться. Причина в том, что узлы не знают своих внешних IP-адресов, а отправлять данные на внутренние адреса — бесполезно.

Однако если внешние адреса известны, соединение установится без проблем. Если первый узел отправит данные маршрутизатору второго узла, тот проигнорирует их — в этот момент его таблица NAT пуста. При этом маршрутизатор первого узла уже получил запись в своей таблице NAT. Как только второй узел отправит данные на маршрутизатор первого узла, тот передаст их первому узлу. Теперь в таблице NAT второго маршрутизатора тоже есть нужные данные.

Проблема в том, что для определения внешнего IP нужен узел, находящийся в публичной сети. Чтобы решить эту задачу, используются дополнительные серверы, подключённые к Интернету напрямую. Они также помогают создавать записи в таблице NAT.

Серверы STUN и TURN

Доступные серверы STUN и TURN нужно указать при инициализации WebRTC — с этого момента будем называть их ICE-серверами. Если серверы не указаны, подключиться смогут только устройства в одной сети (подключённые без NAT). Обратите внимание: в сетях 3g обязательно использовать серверы TURN.

STUN-сервер — это сервер в интернете, который отправляет обратный адрес (адрес источника узла) обратно. Узел, находящийся за маршрутизатором, связывается с сервером STUN, чтобы обойти NAT. Пакет, пришедший на сервер STUN, содержит адрес источника. Это адрес маршрутизатора, другими словами, внешний адрес нашего узла. Это адрес, который возвращает сервер STUN. Таким образом, узел получает свой внешний IP и порт, что делает его доступным в сети. Затем WebRTC создает дополнительного кандидата с этим адресом (внешний адрес маршрутизатора и порт). Теперь в таблице NAT есть запись, которая пропускает пакеты к нашему узлу, которые отправляются на маршрутизатор через правильный порт.

Пример STUN-сервера: как это работает

STUN-сервер будет называться s1. Маршрутизатор и узел обозначим как r1 и p1 соответственно. Также будем отслеживать таблицу NAT — обозначим её как r1_nat. В этой таблице обычно хранится много записей от разных узлов подсети — мы их не будем упоминать.

Начнём с пустой r1-nat:

Что такое WebRTC? Объясняем простым языком, image #11

В таблице 4 столбца. В ней каждому из первых двух столбцов (IP, PORT) соответствует пара из последних двух (IP, PORT).

P1 посылает пакет на s1. Мы видим четыре интересных поля в таблице внизу, они находятся в заголовке транспортного пакета (TCP или UDP) — IP и PORT источника и приемника. Давайте представим, что это адреса.

Что такое WebRTC? Объясняем простым языком, image #12

P1 отправляет этот пакет на r1. Маршрутизатору нужно подставить реальный адрес источника Src IP, потому что адрес из пакета не подходит для работы в интернете. Кроме того, такие адреса зарезервированы и не используются в глобальной сети. Маршрутизатор изменяет пакет и создаёт новую запись в таблице r1_nat. Чтобы это сделать, ему нужно выбрать номер порта. Поскольку несколько устройств внутри локальной сети могут одновременно выходить в интернет, таблица NAT должна хранить дополнительную информацию, чтобы маршрутизатор мог понять, кому из них вернуть ответ от сервера. Допустим, маршрутизатор выбрал порт 888.

Изменённый заголовок пакета:

Что такое WebRTC? Объясняем простым языком, image #13

10.50.200.5 — внешний IP-адрес роутера.

r1_nat:

Что такое WebRTC? Объясняем простым языком, image #14

IP-адрес и порт подсети совпадают с теми, что были в исходном пакете. На самом деле, чтобы отправить пакет обратно, нужно уметь полностью восстановить эти параметры. IP-адрес для внешней сети — это адрес маршрутизатора, а порт изменится на тот, который создал маршрутизатор.

Реальный порт, к которому узел p1 подключается, — 35777, но сервер отправляет данные на фиктивный порт 888. Позже он будет изменён на настоящий — 35777.

Итак, маршрутизатор подставил в заголовок пакета адрес и порт источника и добавил запись в NAT. Теперь пакет отправляется по сети на сервер — на узел s1. На входе s1 получает такой пакет:

Что такое WebRTC? Объясняем простым языком, image #15

Итак, сервер STUN знает, что получил пакет от 10.50.200.5:888. Теперь он отправляет этот адрес обратно. Здесь стоит на секунду остановиться и посмотреть ещё раз.

Таблицы выше — это фрагмент заголовка пакета, а не его содержимого. Мы не рассматривали содержимое, потому что оно не так важно — всё описано в протоколе STUN. Теперь, однако, поговорим и о содержимом. Оно будет простым и будет включать адрес маршрутизатора — 10.50.200.5:888, хотя мы взяли его из заголовка пакета. Такой подход встречается редко, потому что протоколы обычно не заботятся об адресах узлов. Главное — чтобы пакеты дошли до получателя. Но в данном случае мы анализируем протокол, который определяет путь между двумя узлами.

Теперь у нас есть второй пакет, который идёт в обратном направлении:

Что такое WebRTC? Объясняем простым языком, image #16

Заголовок изменился, потому что источник и получатель поменялись местами — логично, ведь теперь пакет идёт в другое место назначения.

Что такое WebRTC? Объясняем простым языком, image #17

Это содержимое пакета. На самом деле, он может содержать много информации, но здесь упоминается только то, что важно для понимания работы сервера STUN.

Далее пакет движется по сети и попадает на внешний интерфейс r1. Маршрутизатор понимает, что пакет не для него. Как он это определяет? По порту. Порт 888 маршрутизатор использует не по прямому назначению, а для механизма NAT. Поэтому он проверяет таблицу. При этом он смотрит в колонку External PORT и ищет строку, совпадающую с Dest PORT из входящего пакета — то есть 888.

Что такое WebRTC? Объясняем простым языком, image #18

Нам повезло, что этот ряд существует. Если бы не повезло, пакет был бы отброшен. Теперь нужно понять, на какой узел подсети отправить пакет. Не спешите — давайте вспомним, насколько важны порты в этом механизме. Два узла подсети могут отправлять запросы во внешнюю сеть. Тогда, если маршрутизатор создал порт 888 для первого узла, для второго он создаст порт 889. Предположим, что это так, и r1_nat выглядит следующим образом:

Что такое WebRTC? Объясняем простым языком, image #19

По порту 888 мы можем понять, что нужный внутренний адрес — 192.168.0.200:35777. Маршрутизатор изменяет адрес этого приёмника на

Что такое WebRTC? Объясняем простым языком, image #20

на

Что такое WebRTC? Объясняем простым языком, image #21

Пакет успешно доходит до узла r1. Просмотрев содержимое пакета, узел узнаёт свой внешний IP-адрес — адрес в глобальной сети. Также он знает порт, через который прокладывает путь через NAT.

Что дальше? Чем это полезно? Полезность заключается в записи в таблицу r1_nat. Если кто-то отправит пакет с портом 888 на r1, пакет будет перенаправлен на p1. Таким образом создаётся прямой путь к скрытому узлу p1.

Из приведённого примера вы можете понять, как работают NAT и STUN-сервер. На самом деле серверы ICE и STUN/TURN используются для обхода ограничений NAT.

Между узлом и сервером может быть несколько маршрутизаторов. В этом случае узел получит адрес того маршрутизатора, который находится в той же сети, что и сервер. Иными словами, мы получим адрес маршрутизатора, подключённого к серверу STUN. Это именно то, что нужно для p2p-коммуникации, потому что для каждого маршрутизатора будет обновляться важная запись в таблице NAT. Поэтому обратный путь пройдёт без проблем.

Сервер TURN — это улучшенная версия сервера STUN, поэтому любой сервер TURN может выполнять функции STUN. У TURN есть свои преимущества. Если прямое соединение p2p невозможно, например, в сетях 3G, сервер начинает работать как ретранслятор и выступает в роли посредника. Конечно, в этом случае p2p-соединение отсутствует, но вне механизма ICE узлы продолжают считать, что общаются напрямую.

Когда TURN-сервер является обязательным условием? Почему серверов STUN недостаточно? Потому что существуют разные виды NAT. Они одинаково заменяют IP-адрес и порт, но некоторые из них имеют встроенную защиту от подделки. Например, в таблице симметричного NAT хранятся ещё два параметра — IP и порт удалённого узла. Пакет из внешней сети проходит через NAT во внутреннюю сеть только в том случае, если адрес и порт источника совпадают с теми, что указаны в таблице. Поэтому трюк с сервером STUN не работает: в таблице NAT хранится адрес и порт сервера STUN. Когда маршрутизатор получает пакет от собеседника WebRTC, он его отбрасывает, считая фальсифицированным — ведь пакет пришёл не от сервера STUN.

Поэтому сервер TURN необходим, когда два собеседника находятся за симметричным NAT (каждый — за своим).

А теперь кратко

Медиапоток

  • Видео- и аудиоданные упакованы в медиапотоки
  • Медиапотоки синхронизируют медиадорожки, из которых они состоят
  • Различные медиапотоки не синхронизируются между собой
  • Медиапотоки могут быть как локальными, так и удалёнными. Локальные отвечают за камеру и микрофон, а удалённые получают данные из сети в виде кода
  • Существует два типа медиапотоков: видео и аудио.
  • Медиадорожки можно включать и выключать
  • Медиадорожки состоят из медиаканалов
  • Медиадорожки синхронизируют медиаканалы, из которых они состоят
  • Медиапотоки и медиадорожки имеют метки, которые помогают различать их между собой.

Дескриптор сессии

  • Дескриптор сессии используется для логического соединения двух узлов в сети.
  • Дескриптор сессии хранит информацию о доступных способах кодирования аудио- и видеоданных
  • WebRTC использует внешний сигнальный механизм. Передача дескрипторов сессий (SDP) ложится на приложение
  • Механизм логического соединения состоит из двух шагов: предложение и ответ
  • Генерация дескриптора сессии невозможна без локального медиапотока с предложением. Также она невозможна без удалённого дескриптора сессии с ответом.
  • Полученный дескриптор необходимо передать реализации WebRTC — неважно, получен он удалённо или локально от той же самой реализации WebRTC.
  • Существует также возможность незначительно изменить дескриптор сессии

Кандидаты

  • Ice-кандидат — это адрес узла в сети.
  • Адрес может быть собственным, маршрутизатора или сервера TURN.
  • Существует множество кандидатов
  • Кандидат состоит из IP-адреса, порта и типа транспорта (TCP или UDP).
  • Кандидаты используются для установления физического соединения между двумя узлами в сети
  • Кандидаты должны передаваться через механизм сигнализации
  • Только удалённые кандидаты должны передаваться в реализацию WebRTC
  • В некоторых реализациях WebRTC кандидаты могут отправляться только после установления дескриптора сессии

STUN/TURN/ICE/NAT

  • NAT — это механизм, который позволяет устройствам в локальной сети выходить в интернет
  • Домашние маршрутизаторы поддерживают специальную таблицу NAT
  • Маршрутизаторы подменяют адреса в пакетах. Если пакет отправляется во внешнюю сеть, адрес источника заменяется на адрес самого маршрутизатора. Если пакет приходит из внешней сети, адрес источника заменяется на адрес устройства во внутренней сети.
  • NAT использует порты, чтобы несколько устройств могли одновременно выходить в интернет.
  • ICE — это механизм, позволяющий обойти NAT
  • Серверы STUN и TURN помогают обойти NAT
  • Сервер STUN создаёт записи в таблице NAT и возвращает адрес внешнего узла
  • Сервер TURN расширяет функционал STUN и обеспечивает его постоянную работу
  • В худших сценариях TURN-сервер используется в качестве ретранслятора, поэтому p2p превращается в соединение «клиент — сервер — клиент»
  • Технологии