Google is committed to advancing racial equity for Black communities. See how.
Эта страница была переведа с помощью Cloud Translation API.
Switch to English

Начало работы с одноранговыми соединениями

Одноранговые соединения - это часть спецификаций WebRTC, которая касается соединения двух приложений на разных компьютерах для обмена данными по одноранговому протоколу. Связь между одноранговыми узлами может быть видео, аудио или произвольными двоичными данными (для клиентов, поддерживающих API RTCDataChannel ). Чтобы выяснить, как могут соединяться два партнера, оба клиента должны предоставить конфигурацию сервера ICE. Это либо STUN, либо TURN-сервер, и их роль заключается в предоставлении кандидатов ICE каждому клиенту, который затем передается на удаленный узел. Эта передача кандидатов ICE обычно называется сигнализацией.

передача сигналов

Спецификация WebRTC включает в себя API-интерфейсы для связи с сервером ICE (установления соединения с Интернетом), но компонент сигнализации не является его частью. Сигнализация необходима для того, чтобы два партнера могли поделиться, как они должны соединиться. Обычно это решается с помощью обычного веб-API на основе HTTP (т. Е. Службы REST или другого механизма RPC), где веб-приложения могут передавать необходимую информацию до того, как будет инициировано одноранговое соединение.

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

 // Set up an asynchronous communication channel that will be
// used during the peer connection setup
const signalingChannel = new SignalingChannel(remoteClientId);
signalingChannel.addEventListener('message', message => {
    // New message from remote client received
});

// Send an asynchronous message to the remote client
signalingChannel.send('Hello!');
 

Сигнализация может быть реализована многими различными способами, и спецификация WebRTC не предпочитает какого-либо конкретного решения.

Инициирование одноранговых соединений

Каждое одноранговое соединение обрабатывается объектом RTCPeerConnection . Конструктор для этого класса принимает один объект RTCConfiguration качестве параметра. Этот объект определяет, как устанавливается одноранговое соединение, и должен содержать информацию об используемых ICE-серверах.

После создания RTCPeerConnection нам нужно создать предложение или ответ SDP, в зависимости от того, являемся ли мы вызывающим или принимающим узлом. Как только предложение или ответ SDP созданы, их необходимо отправить удаленному узлу через другой канал. Передача объектов SDP удаленным одноранговым узлам называется сигнализацией и на нее не распространяется спецификация WebRTC.

Чтобы инициировать настройку однорангового соединения с вызывающей стороны, мы создаем объект RTCPeerConnection и затем вызываем createOffer() для создания объекта RTCSessionDescription . Это описание сеанса устанавливается как локальное описание с использованием setLocalDescription() и затем отправляется по нашему каналу сигнализации на принимающую сторону. Мы также настраиваем прослушиватель на наш канал сигнализации для получения ответа от нашего предлагаемого описания сеанса от принимающей стороны.

 async function makeCall() {
    const configuration = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}]}
    const peerConnection = new RTCPeerConnection(configuration);
    signalingChannel.addEventListener('message', async message => {
        if (message.answer) {
            const remoteDesc = new RTCSessionDescription(message.answer);
            await peerConnection.setRemoteDescription(remoteDesc);
        }
    });
    const offer = await peerConnection.createOffer();
    await peerConnection.setLocalDescription(offer);
    signalingChannel.send({'offer': offer});
}
 

На получающей стороне мы ждем входящее предложение, прежде чем создадим наш экземпляр RTCPeerConnection . Как только это будет сделано, мы устанавливаем полученное предложение с помощью setRemoteDescription() . Далее мы вызываем createAnswer() чтобы создать ответ на полученное предложение. Этот ответ устанавливается как локальное описание с помощью setLocalDescription() а затем отправляется вызывающей стороне через наш сервер сигнализации.

 const peerConnection = new RTCPeerConnection(configuration);
signalingChannel.addEventListener('message', async message => {
    if (message.offer) {
        peerConnection.setRemoteDescription(new RTCSessionDescription(message.offer));
        const answer = await peerConnection.createAnswer();
        await peerConnection.setLocalDescription(answer);
        signalingChannel.send({'answer': answer});
    }
});
 

Как только два узла установили описания как локального, так и удаленного сеанса, они знают возможности удаленного узла. Это не значит, что связь между пирами готова. Чтобы это работало, нам нужно собрать кандидатов ICE на каждом узле и передать (по каналу сигнализации) другому узлу.

Кандидаты ICE

Прежде чем два партнера смогут общаться с помощью WebRTC, им необходимо обменяться информацией о подключении. Поскольку условия сети могут варьироваться в зависимости от ряда факторов, обычно используется внешняя служба для обнаружения возможных кандидатов на подключение к одноранговому узлу. Эта служба называется ICE и использует сервер STUN или TURN. STUN расшифровывается как Session Traversal Utilities для NAT и обычно используется косвенно в большинстве приложений WebRTC.

TURN (Traversal Using Relay NAT) - это более продвинутое решение, которое включает протоколы STUN, и большинство коммерческих служб на основе WebRTC используют сервер TURN для установления соединений между узлами. API WebRTC поддерживает STUN и TURN напрямую и собрана в более полный термин «Установление подключения к Интернету». При создании соединения WebRTC мы обычно предоставляем один или несколько серверов ICE в конфигурации для объекта RTCPeerConnection .

Струйка льда

После RTCPeerConnection объекта RTCPeerConnection базовая структура использует предоставленные серверы ICE для сбора кандидатов на установление соединения (кандидатов ICE). Событие icegatheringstatechange на RTCPeerConnection сигнализирует, в каком состоянии сбор ICE ( new , gathering или complete ).

Хотя одноранговый узел может ожидать завершения сбора ICE, обычно гораздо эффективнее использовать метод «струйки льда» и передавать каждого кандидата ICE удаленному одноранговому узлу при его обнаружении. Это значительно сократит время настройки для подключения к равноправному узлу и позволит начать видеовызов с меньшими задержками.

Чтобы собрать кандидатов на ICE, просто добавьте слушателя для события icecandidate . RTCPeerConnectionIceEvent этим слушателем, будет содержать свойство candidate , представляющее нового кандидата, который должен быть отправлен удаленному узлу (см. Сигнализация).

 // Listen for local ICE candidates on the local RTCPeerConnection
peerConnection.addEventListener('icecandidate', event => {
    if (event.candidate) {
        signalingChannel.send({'new-ice-candidate': event.candidate});
    }
});

// Listen for remote ICE candidates and add them to the local RTCPeerConnection
signalingChannel.addEventListener('message', async message => {
    if (message.iceCandidate) {
        try {
            await peerConnection.addIceCandidate(message.iceCandidate);
        } catch (e) {
            console.error('Error adding received ice candidate', e);
        }
    }
});
 

Соединение установлено

Как только кандидаты ICE будут получены, мы должны ожидать, что состояние нашего однорангового соединения в конечном итоге изменится на состояние соединения. Чтобы обнаружить это, мы добавляем прослушиватель в нашу RTCPeerConnection где мы прослушиваем события connectionstatechange .

 // Listen for connectionstatechange on the local RTCPeerConnection
peerConnection.addEventListener('connectionstatechange', event => {
    if (peerConnection.connectionState === 'connected') {
        // Peers connected!
    }
});
 

Документация по API RTCPeerConnection