4-02 Установка WebRTC-соединения: offer, answer и ICE

В этом разделе подробно рассматривается процесс установления однорангового (peer-to-peer, P2P) соединения между двумя участниками с использованием технологии WebRTC. Хотя на первый взгляд WebRTC может казаться простой «видеосвязью в браузере», на самом деле за кулисами происходит сложная координация множества протоколов. В данном тексте мы пошагово разберём, как браузеры договариваются о параметрах соединения, преодолевают сетевые ограничения и обеспечивают защищённую передачу медиаданных.
1. Общая архитектура установления WebRTC-соединения
WebRTC — это технология, позволяющая организовать прямое (P2P) соединение между двумя устройствами (например, браузерами) для передачи аудио, видео и произвольных данных. Однако сам WebRTC не включает в себя протокол сигнализации — механизм для обмена служебной информацией, такой как параметры соединения и сетевые адреса. Вместо этого разработчик должен реализовать или выбрать собственный способ обмена этой информацией, например, через WebSocket, HTTP или другой канал.
Соединение устанавливается между двумя участниками, условно называемыми Peer A и Peer B. Основные этапы включают:
- Инициализация соединения.
- Обмен описаниями сессии (SDP).
- Сбор и обмен сетевых кандидатов (ICE).
- Установление защищённого транспорта.
- Передача медиапотоков.
Каждый из этих этапов использует специализированные протоколы и API, которые мы рассмотрим подробно.
2. Инициализация и создание предложения (offer)
Процесс начинается с одной из сторон — например, Peer A. Она инициирует установление соединения, создавая объект RTCPeerConnection. Этот объект — центральный элемент WebRTC, отвечающий за всё: от согласования параметров до передачи данных и обработки безопасности.
const peerA = new RTCPeerConnection();
После инициализации объекта Peer A вызывает метод createOffer(). Этот метод генерирует специальное текстовое описание сессии, называемое SDP (Session Description Protocol). Такое описание содержит:
- Поддерживаемые аудио- и видео-кодеки (например, Opus, VP8, H.264).
- Направления передачи медиа (отправлять, принимать или оба).
- Транспортные параметры (порты, протоколы).
- Информацию о потоке данных (data channels, если используются).
💡 Что такое SDP?
SDP — это формат описания мультимедийных сессий. Он не передаёт медиаданные и не управляет соединением напрямую, а служит «языком переговоров» между участниками. Представьте, что два человека хотят поговорить: один говорит: «Я говорю по-английски и могу слышать и говорить», — это и есть SDP-предложение (offer).
После генерации SDP-описания оно называется offer (предложение). Но прежде чем отправить его, Peer A должен сохранить его как локальное описание — это нужно для внутренней синхронизации состояния соединения.
const offer = await peerA.createOffer();
await peerA.setLocalDescription(offer);
Метод setLocalDescription() сообщает WebRTC-движку, что данное описание теперь является текущим локальным состоянием соединения.
3. Передача предложения через сигналинговый сервер
Теперь Peer A должен передать offer стороне Peer B. Поскольку WebRTC не определяет, как именно происходит этот обмен, используется внешний сигналинговый канал — например, WebSocket-соединение с сервером.
// Пример: отправка offer через WebSocket
signalingSocket.send(JSON.stringify({ type: 'offer', sdp: offer }));
Сигналинговый сервер — это просто посредник. Он не участвует в медиатранспорте, а лишь пересылает служебные сообщения между участниками. Это важно: WebRTC остаётся P2P, даже если для настройки используется централизованный сервер.
4. Получение предложения и формирование ответа (answer)
Когда Peer B получает offer, он должен его обработать. Сначала он инициализирует свой собственный RTCPeerConnection:
const peerB = new RTCPeerConnection();
Затем он устанавливает полученное SDP как удалённое описание:
await peerB.setRemoteDescription(offer);
Это означает, что Peer B теперь знает, какие параметры предлагает Peer A, и может подготовить ответ, совместимый с этими параметрами.
Далее Peer B вызывает createAnswer() — метод, аналогичный createOffer(), но формирующий ответ на уже полученное предложение. Ответ также представляет собой SDP, но уже с учётом собственных возможностей Peer B.
const answer = await peerB.createAnswer();
После этого Peer B устанавливает ответ как своё локальное описание:
await peerB.setLocalDescription(answer);
И, наконец, отправляет answer обратно к Peer A через тот же сигналинговый канал:
signalingSocket.send(JSON.stringify({ type: 'answer', sdp: answer }));
Peer A, получив answer, устанавливает его как удалённое описание:
await peerA.setRemoteDescription(answer);
На этом этапе обе стороны знают, какие кодеки, направления и транспортные параметры будут использоваться. Однако соединение ещё не установлено — нужно решить, по какому сетевому пути передавать данные.
5. Сбор и обмен ICE-кандидатов
Даже если стороны договорились о параметрах, они могут находиться за NAT (Network Address Translation) — устройствами, которые скрывают внутренние IP-адреса. Это мешает прямому P2P-соединению, потому что внешние участники не могут напрямую обратиться к приватным адресам вроде 192.168.1.10.
Для решения этой проблемы используется ICE (Interactive Connectivity Establishment) — протокол, который находит все возможные пути (кандидаты) для соединения и проверяет, какой из них работает.
Как работает сбор кандидатов?
Каждая сторона (и Peer A, и Peer B) параллельно собирает ICE-кандидаты — возможные сетевые адреса, по которым её можно достичь. Эти кандидаты бывают трёх типов:
| Тип кандидата | Описание |
|---|---|
| Host candidate | Локальный IP-адрес и порт (например, 192.168.1.10:50000). Работает только в пределах локальной сети. |
| Server reflexive candidate | Внешний IP и порт, определённый через STUN-сервер (например, 203.0.113.5:60000). Позволяет узнать, как устройство видно извне. |
| Relayed candidate | Адрес на TURN-сервере, через который идёт ретрансляция трафика. Используется, когда прямое соединение невозможно. Более затратно по ресурсам, но надёжно. |
💡 STUN и TURN — вспомогательные протоколы
- STUN (Session Traversal Utilities for NAT) помогает устройству узнать свой публичный IP и порт.
- TURN (Traversal Using Relays around NAT) — это «запасной путь»: если прямое соединение не удаётся, трафик идёт через сервер-ретранслятор.
ICE использует оба, чтобы максимизировать шансы на успешное соединение.
Каждый найденный кандидат передаётся другой стороне через сигналинговый канал. Например:
peerA.onicecandidate = (event) => {
if (event.candidate) {
signalingSocket.send(JSON.stringify({
type: 'ice-candidate',
candidate: event.candidate
}));
}
};
Peer B, получив кандидата, добавляет его в свой RTCPeerConnection:
await peerB.addIceCandidate(candidate);
То же самое происходит и в обратную сторону — обе стороны обмениваются кандидатами.
6. ICE-переговоры и выбор рабочего пути
После добавления кандидатов запускается ICE-процедура — система начинает проверять все возможные пары кандидатов (один от Peer A, один от Peer B), чтобы найти хотя бы один рабочий путь.
Например, проверяется:
- Может ли Peer A отправить пакет на
203.0.113.5:60000(STUN-адрес Peer B)? - Работает ли соединение через TURN-ретранслятор?
ICE использует STUN-пакеты для проверки достижимости. Как только находится хотя бы одна рабочая пара, соединение считается установленным на транспортном уровне.
💡 Визуализация процесса
Представьте, что два человека хотят встретиться. Один предлагает: «Я могу прийти к метро А, Б или через такси». Второй говорит: «Я могу приехать к метро Б, В или вызвать такси». Они проверяют все комбинации и находят, что «метро Б» подходит обоим — это и есть выбранный путь.
7. Установление защищённого соединения: DTLS и SRTP
Когда транспортный путь найден, WebRTC автоматически инициирует DTLS-рукопожатие (Datagram Transport Layer Security) — аналог TLS, но для UDP. Этот процесс нужен для:
- Аутентификации сторон.
- Обмена криптографическими ключами.
- Установления защищённого канала.
После успешного DTLS-рукопожатия генерируются ключи для SRTP (Secure Real-time Transport Protocol) — защищённой версии RTP, которая шифрует аудио- и видеопотоки.
💡 Почему SRTP?
Обычный RTP не шифрует данные. SRTP добавляет шифрование полезной нагрузки (например, видеофреймов), что критично для конфиденциальности. В WebRTC всегда используется SRTP, и ключи обмениваются через DTLS, а не в открытом виде.
8. Начало передачи медиапотоков
Только после завершения всех предыдущих этапов — согласования параметров, нахождения рабочего пути и установления шифрования — начинается передача аудио и видео.
Медиапотоки передаются напрямую между браузерами по UDP, минуя сигналинговый сервер. Это обеспечивает низкую задержку и высокую эффективность.
// Пример: добавление медиапотока в соединение
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
stream.getTracks().forEach(track => peerA.addTrack(track, stream));
Каждый медиапакет проходит по следующему стеку:
[Видео/Аудио] → Кодирование (VP8, Opus) → SRTP (шифрование) → UDP → Сеть → ICE (выбранный путь)
9. Итог: как все протоколы работают вместе
WebRTC — это не отдельный протокол, а интегрированная система, объединяющая несколько технологий:
| Протокол | Роль в WebRTC |
|---|---|
| SDP | Описание параметров сессии: кодеки, направления, порты. |
| ICE | Поиск и проверка рабочих сетевых путей. |
| STUN | Определение публичного IP-адреса. |
| TURN | Ретрансляция трафика при невозможности прямого соединения. |
| DTLS | Шифрование и обмен ключами. |
| SRTP | Защищённая передача аудио и видео. |
| Сигналинг | Внешний механизм обмена SDP и ICE-кандидатами (не стандартизирован). |
💡 Важно понимать: WebRTC «прячет» сложность от пользователя, но для инженера важно видеть, что под капотом работают те же протоколы, что и в классических системах видеосвязи — просто в более автоматизированной форме.
Заключение
Установление WebRTC-соединения — это многоэтапный процесс, требующий слаженной работы нескольких протоколов. От createOffer до передачи первого видеокадра проходит несколько секунд, в течение которых:
- Стороны договариваются о параметрах через SDP.
- Находят рабочий сетевой путь с помощью ICE, STUN и TURN.
- Устанавливают защищённый канал с DTLS и SRTP.
Понимание этого процесса критически важно для диагностики проблем: например, если видеосвязь не устанавливается, нужно проверить, дошёл ли offer, прошёл ли ICE, успешно ли прошло DTLS. Каждый этап — это потенциальное «узкое место», которое можно проверить с помощью инструментов вроде Wireshark, логов браузера или встроенных WebRTC-статистик (getStats()).