Primeiros passos com as conexões de peering

As conexões de peering são parte das especificações do WebRTC que lida com conectando dois aplicativos em computadores diferentes para que eles se comuniquem usando um protocolo ponto a ponto. A comunicação entre pares pode ser de vídeo, áudio dados binários arbitrários (para clientes com suporte à API RTCDataChannel). Em para descobrir como dois pares podem se conectar, ambos precisam fornecer um endereço de e-mail Configuração do servidor. Esse é um servidor STUN ou TURN, e o papel dele é para dar candidatos ICE a cada cliente, que depois são transferidos para a peering. Essa transferência de candidatos ICE é chamada de sinalização.

Sinalização

A especificação WebRTC inclui APIs para a comunicação com uma instância de estabelecimento de conectividade), mas o componente de sinalização não faz parte reimplantá-lo. A sinalização é necessária para que dois colegas compartilhem como eles precisam se conectar. Geralmente, isso é resolvido com uma API Web normal baseada em HTTP (ou seja, uma API REST ou outro mecanismo de RPC) em que os aplicativos da Web podem retransmitir as antes que a conexão de peering seja iniciada.

O snippet de código a seguir mostra como esse serviço de sinalização fictícia pode ser usado para enviar e receber mensagens de forma assíncrona. Isso será usado no restante neste guia quando necessário.

// 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!');

A sinalização pode ser implementada de várias maneiras, e o WebRTC especificação não prefere uma solução específica.

Iniciar conexões de pares

Cada conexão de peering é processada por um objeto RTCPeerConnection. O construtor para essa classe usa um único objeto RTCConfiguration como parâmetro. Isso define como a conexão de peering é configurada e deve conter informações sobre os servidores ICE a serem usados.

Depois que o RTCPeerConnection for criado, precisamos criar uma oferta SDP ou resposta, dependendo se somos o ponto de chamada ou o ponto de recebimento. Depois que o SDP uma oferta ou resposta for criada, ela deve ser enviada ao terminal remoto por meio de um um canal diferente. A transmissão de objetos SDP para pares remotos é chamada de sinalização e não é coberto pela especificação WebRTC.

Para iniciar a configuração da conexão de peering no lado da chamada, criamos uma objeto RTCPeerConnection e, em seguida, chame createOffer() para criar um objeto RTCSessionDescription. A descrição desta sessão está definida como local usando setLocalDescription(), que é enviada pela nossa sinalização canal para o lado de destino. Também configuramos um listener para nossa canal para quando receber uma resposta à descrição da sessão oferecida do lado receptor.

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});
}

No lado do destinatário, aguardamos uma oferta recebida antes de criar nosso RTCPeerConnection. Depois de fazer isso, definimos a oferta recebida usando setRemoteDescription(): Em seguida, chamamos createAnswer() para criar uma resposta para a oferta recebida. Esta resposta é definida como a descrição local usando setLocalDescription() e enviados para o lado da chamada pela nossa sinalização servidor.

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});
    }
});

Depois que os dois pares definirem as descrições de sessão local e remota, conhecer os recursos do terminal remoto. Isso não significa que a conexão entre os pares está pronto. Para que isso funcione, precisamos coletar candidatos em cada grupo e transferem (pelo canal de sinalização) para os outros peering.

Candidatos ao ICE

Antes que dois pares possam se comunicar usando WebRTC, eles precisam trocar informações sobre conectividade. Como as condições da rede podem variar dependendo vários fatores, um serviço externo costuma ser usado para descobrir possíveis candidatos para se conectar com um colega. Esse serviço é chamado de ICE e é usando um servidor STUN ou TURN. STUN significa "travessia de sessão". Utilitários para NAT e geralmente é usado indiretamente na maioria dos aplicativos WebRTC.

O TURN (Traversal Using Relay NAT) é a solução mais avançada que incorpora os protocolos STUN e a maioria dos serviços comerciais baseados em WebRTC usam um servidor TURN para estabelecer conexões entre pares. A API WebRTC é compatível com STUN e TURN diretamente, e estão reunidos sob o termo mais completo Estabelecimento de conectividade. Ao criar uma conexão WebRTC, geralmente que fornece um ou vários servidores ICE na configuração do RTCPeerConnection.

Gelo de gotejamento

Depois que um objeto RTCPeerConnection é criado, o framework usa o servidores ICE fornecidos para reunir candidatos para estabelecimento de conectividade (ICE candidatos). O evento icegatheringstatechange em RTCPeerConnection indicadores em que estado está a coleta de ICE (new, gathering ou complete).

Embora seja possível que um par espere até que a coleta de ICE seja concluída, ela costuma ser muito mais eficiente usar um "gelo de gota" técnica e transmitem cada candidato da ICE ao par remoto à medida que eles são descobertos. Isso vai reduzir significativamente o tempo de configuração da conexão de peering e permitir que a para começar com menos atrasos.

Para reunir candidatos ICE, basta adicionar um listener para o evento icecandidate. O RTCPeerConnectionIceEvent emitido nesse listener contém propriedade candidate que representa um novo candidato que precisa ser enviado ao peering remoto (consulte Sinalização).

// 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);
        }
    }
});

Conexão estabelecida

Depois que os candidatos ICE forem recebidos, devemos esperar o estado para nosso colega vai mudar para o estado conectado. Para detectar isso, adicionamos um listener para nossa RTCPeerConnection, em que ouvimos connectionstatechange. eventos.

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

API RTCPeerConnection documentação