Crea i servizi di backend necessari per un'app WebRTC

Che cos'è la segnalazione?

La segnalazione è la procedura di coordinamento della comunicazione. Affinché un'app WebRTC possa configurare una chiamata, i suoi client devono scambiarsi le seguenti informazioni:

  • Messaggi di controllo della sessione utilizzati per aprire o chiudere la comunicazione
  • Messaggi di errore
  • Metadati dei contenuti multimediali, come codec, impostazioni dei codec, larghezza di banda e tipi di contenuti multimediali
  • Dati chiave utilizzati per stabilire connessioni sicure
  • Dati di rete, come l'indirizzo IP e la porta di un host come vengono visualizzati dal mondo esterno

Questo processo di segnalazione richiede un modo per i client di scambiarsi messaggi. Questo meccanismo non è implementato dalle API WebRTC. Devi costruirlo tu. Più avanti in questo articolo, scoprirai come creare un servizio di segnalazione. Prima, però, è necessario un po' di contesto.

Perché la segnalazione non è definita da WebRTC?

Per evitare la ridondanza e massimizzare la compatibilità con le tecnologie consolidate, i metodi e i protocolli di segnalazione non sono specificati dagli standard WebRTC. Questo approccio è descritto nel JavaScript Session Establishment Protocol (JSEP):

L'architettura di JSEP evita inoltre che un browser debba salvare lo stato, ovvero che funzioni come una macchina a stati di segnalazione. Ciò sarebbe problematico se, ad esempio, i dati di segnalazione venissero persi ogni volta che una pagina viene ricaricata. Lo stato di segnalazione può invece essere salvato su un server.

Diagramma dell'architettura JSEP
Architettura JSEP

JSEP richiede lo scambio tra peer di offerta e risposta, i metadati multimediali menzionati in precedenza. Offerte e risposte vengono comunicate in formato Session Description Protocol (SDP), che si presenta nel seguente modo:

v=0
o=- 7614219274584779017 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:W2TGCZw2NZHuwlnf
a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=mid:audio
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:9c1AHz27dZ9xPI91YNfSlI67/EMkjHHIHORiClQe
a=rtpmap:111 opus/48000/2

Vuoi sapere cosa significa tutto questo gergo SDP? Dai un'occhiata agli esempi dell'Internet Engineering Task Force (IETF).

Tieni presente che WebRTC è progettato in modo che l'offerta o la risposta possa essere modificata prima di essere impostata come descrizione locale o remota modificando i valori nel testo SDP. Ad esempio, la funzione preferAudioCodec() in appr.tc può essere utilizzata per impostare il codec e la velocità in bit predefiniti. SDP è un po' difficile da manipolare con JavaScript e si sta discutendo se le versioni future di WebRTC debbano utilizzare JSON, ma ci sono alcuni vantaggi nell'utilizzo di SDP.

RTCPeerConnection API e segnalazione: offerta, risposta e candidato

RTCPeerConnection è l'API utilizzata dalle app WebRTC per creare una connessione tra peer e comunicare audio e video.

Per avviare questa procedura, RTCPeerConnection ha due attività:

  • Verifica le condizioni dei media locali, ad esempio le funzionalità di risoluzione e codec. Questi sono i metadati utilizzati per il meccanismo di offerta e risposta.
  • Ottieni i potenziali indirizzi di rete per l'host dell'app, noti come candidati.

Una volta accertati questi dati locali, devono essere scambiati tramite un meccanismo di segnalazione con il peer remoto.

Immagina che Alice stia cercando di chiamare Eva. Ecco il meccanismo completo di offerta/risposta in tutti i suoi dettagli cruenti:

  1. Alice crea un oggetto RTCPeerConnection.
  2. Alice crea un'offerta (una descrizione della sessione SDP) con il metodo RTCPeerConnection createOffer().
  3. Alice chiama setLocalDescription() con la sua offerta.
  4. Alice converte l'offerta in una stringa e utilizza un meccanismo di segnalazione per inviarla a Eva.
  5. Eve chiama setRemoteDescription() con l'offerta di Alice, in modo che il suo RTCPeerConnection sia a conoscenza della configurazione di Alice.
  6. Eve chiama createAnswer() e il callback di successo per questa operazione riceve una descrizione della sessione locale, ovvero la risposta di Eve.
  7. Eve imposta la sua risposta come descrizione locale chiamando setLocalDescription().
  8. Eve utilizza quindi il meccanismo di segnalazione per inviare la risposta in formato stringa ad Alice.
  9. Alice imposta la risposta di Eva come descrizione della sessione remota utilizzando setRemoteDescription().

Anche Alice ed Eve devono scambiarsi informazioni sulla rete. L'espressione "trovare candidati" si riferisce al processo di ricerca di interfacce di rete e porte utilizzando il framework ICE.

  1. Alice crea un oggetto RTCPeerConnection con un gestore onicecandidate.
  2. Il gestore viene chiamato quando diventano disponibili candidati di rete.
  3. Nel gestore, Alice invia i dati del candidato in formato stringa a Eve tramite il canale di segnalazione.
  4. Quando Eve riceve un messaggio di un candidato da Alice, chiama addIceCandidate() per aggiungere il candidato alla descrizione del peer remoto.

JSEP supporta l'invio incrementale di candidati ICE, che consente al chiamante di fornire in modo incrementale i candidati al chiamato dopo l'offerta iniziale e al chiamato di iniziare ad agire sulla chiamata e configurare una connessione senza attendere l'arrivo di tutti i candidati.

Codice WebRTC per la segnalazione

Il seguente snippet di codice è un esempio di codice W3C che riassume l'intero processo di segnalazione. Il codice presuppone l'esistenza di un meccanismo di segnalazione, SignalingChannel. La segnalazione viene trattata in modo più dettagliato più avanti.

// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);

// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => signaling.send({candidate});

// Let the "negotiationneeded" event trigger offer generation.
pc.onnegotiationneeded = async () => {
  try {
    await pc.setLocalDescription(await pc.createOffer());
    // send the offer to the other peer
    signaling.send({desc: pc.localDescription});
  } catch (err) {
    console.error(err);
  }
};

// After remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
  // Don't set srcObject again if it is already set.
  if (remoteView.srcObject) return;
  remoteView.srcObject = event.streams[0];
};

// Call start() to initiate.
async function start() {
  try {
    // Get local stream, show it in self-view, and add it to be sent.
    const stream =
      await navigator.mediaDevices.getUserMedia(constraints);
    stream.getTracks().forEach((track) =>
      pc.addTrack(track, stream));
    selfView.srcObject = stream;
  } catch (err) {
    console.error(err);
  }
}

signaling.onmessage = async ({desc, candidate}) => {
  try {
    if (desc) {
      // If you get an offer, you need to reply with an answer.
      if (desc.type === 'offer') {
        await pc.setRemoteDescription(desc);
        const stream =
          await navigator.mediaDevices.getUserMedia(constraints);
        stream.getTracks().forEach((track) =>
          pc.addTrack(track, stream));
        await pc.setLocalDescription(await pc.createAnswer());
        signaling.send({desc: pc.localDescription});
      } else if (desc.type === 'answer') {
        await pc.setRemoteDescription(desc);
      } else {
        console.log('Unsupported SDP type.');
      }
    } else if (candidate) {
      await pc.addIceCandidate(candidate);
    }
  } catch (err) {
    console.error(err);
  }
};

Per vedere i processi di offerta/risposta e di scambio di candidati in azione, consulta simpl.info RTCPeerConnection e guarda il log della console per un esempio di chat video su una singola pagina. Se vuoi saperne di più, scarica un dump completo della segnalazione e delle statistiche WebRTC dalla pagina about://webrtc-internals in Google Chrome o dalla pagina opera://webrtc-internals in Opera.

Rilevamento peer

Si tratta di un modo elegante per chiedere: "Come faccio a trovare qualcuno con cui parlare?"

Per le chiamate telefoniche, hai numeri di telefono e directory. Per la messaggistica e le videochiamate online, hai bisogno di sistemi di gestione dell'identità e della presenza e di un modo per consentire agli utenti di avviare le sessioni. Le app WebRTC hanno bisogno di un modo per consentire ai client di segnalarsi a vicenda che vogliono avviare o partecipare a una chiamata.

I meccanismi di rilevamento dei peer non sono definiti da WebRTC e non vengono trattati nelle opzioni qui. La procedura può essere semplice come inviare un'email o un messaggio con un URL. Per le app di chat video, come Talky, tawk.to e Browser Meeting, inviti le persone a una chiamata condividendo un link personalizzato. Lo sviluppatore Chris Ball ha creato un interessante esperimento serverless-webrtc che consente ai partecipanti alle chiamate WebRTC di scambiarsi metadati tramite qualsiasi servizio di messaggistica preferiscano, come messaggistica istantanea, email o piccione viaggiatore.

Come puoi creare un servizio di segnalazione?

Ribadiamo che i protocolli e i meccanismi di segnalazione non sono definiti dagli standard WebRTC. Qualunque sia la tua scelta, hai bisogno di un server intermediario per scambiare messaggi di segnalazione e dati delle app tra i client. Purtroppo, un'app web non può semplicemente urlare su internet: "Connettimi al mio amico!"

Per fortuna, i messaggi di segnalazione sono piccoli e vengono scambiati principalmente all'inizio di una chiamata. Nei test con appr.tc per una sessione di chat video, il servizio di segnalazione ha gestito un totale di circa 30-45 messaggi con una dimensione totale di circa 10 KB.

Oltre a essere relativamente poco esigenti in termini di larghezza di banda, i servizi di segnalazione WebRTC non consumano molte risorse di elaborazione o memoria perché devono solo inoltrare i messaggi e conservare una piccola quantità di dati sullo stato della sessione, ad esempio quali client sono connessi.

Trasferire i messaggi dal server al client

Un servizio di messaggistica per la segnalazione deve essere bidirezionale: dal client al server e dal server al client. La comunicazione bidirezionale è contraria al modello di richiesta/risposta client/server HTTP, ma nel corso degli anni sono stati sviluppati vari hack, come il long polling, per eseguire il push dei dati da un servizio in esecuzione su un server web a un'app web in esecuzione in un browser.

Più di recente, l'API EventSource è stata implementata su larga scala. In questo modo vengono attivati gli eventi inviati dal server, ovvero i dati inviati da un server web a un client browser tramite HTTP. EventSource è progettato per la messaggistica unidirezionale, ma può essere utilizzato in combinazione con XHR per creare un servizio per lo scambio di messaggi di segnalazione. Un servizio di segnalazione trasmette un messaggio da un chiamante, inviato tramite richiesta XHR, inviandolo tramite EventSource al destinatario della chiamata.

WebSocket è una soluzione più naturale, progettata per la comunicazione client-server full duplex, ovvero messaggi che possono fluire in entrambe le direzioni contemporaneamente. Uno dei vantaggi di un servizio di segnalazione creato con WebSocket o eventi inviati dal server (EventSource) è che il backend di queste API può essere implementato su una serie di framework web comuni alla maggior parte dei pacchetti di web hosting per linguaggi come PHP, Python e Ruby.

Tutti i browser moderni, ad eccezione di Opera Mini, supportano WebSocket e, cosa ancora più importante, tutti i browser che supportano WebRTC supportano anche WebSocket, sia su computer che su dispositivi mobili. TLS deve essere utilizzato per tutte le connessioni per garantire che i messaggi non possano essere intercettati non criptati e anche per ridurre i problemi con l'attraversamento del proxy. Per ulteriori informazioni su WebSocket e sul proxy traversal, consulta il capitolo su WebRTC del libro High Performance Browser Networking di Ilya Grigorik.

È anche possibile gestire la segnalazione facendo in modo che i client WebRTC interroghino ripetutamente un server di messaggistica tramite Ajax, ma ciò comporta molte richieste di rete ridondanti, il che è particolarmente problematico per i dispositivi mobili. Anche dopo l'avvio di una sessione, i peer devono eseguire il polling per i messaggi di segnalazione in caso di modifiche o interruzione della sessione da parte di altri peer. L'esempio di app WebRTC Book utilizza questa opzione con alcune ottimizzazioni per la frequenza di polling.

Segnalazione di scalabilità

Anche se un servizio di segnalazione consuma una larghezza di banda e una CPU relativamente ridotte per client, i server di segnalazione per un'app popolare potrebbero dover gestire molti messaggi da diverse località con livelli elevati di concorrenza. Le app WebRTC che ricevono molto traffico hanno bisogno di server di segnalazione in grado di gestire un carico considerevole. Qui non entri nei dettagli, ma esistono diverse opzioni per la messaggistica ad alto volume e ad alte prestazioni, tra cui:

  • eXtensible Messaging and Presence Protocol (XMPP), originariamente noto come Jabber, un protocollo sviluppato per la messaggistica istantanea che può essere utilizzato per la segnalazione (le implementazioni del server includono ejabberd e Openfire). I client JavaScript, come Strophe.js, utilizzano BOSH per emulare lo streaming bidirezionale, ma per vari motivi, BOSH potrebbe non essere efficiente come WebSocket e, per gli stessi motivi, potrebbe non essere scalabile. Per inciso, Jingle è un'estensione XMPP per abilitare la voce e il video. Il progetto WebRTC utilizza i componenti di rete e di trasporto della libreria libjingle, un'implementazione C++ di Jingle.

  • Librerie open source, come ZeroMQ (utilizzata da TokBox per il servizio Rumour) e OpenMQ (NullMQ applica i concetti di ZeroMQ alle piattaforme web utilizzando il protocollo STOMP su WebSocket).

  • Piattaforme di messaggistica commerciale cloud che utilizzano WebSocket (anche se potrebbero ricorrere al long polling), come Pusher, Kaazing e PubNub (PubNub ha anche un'API per WebRTC).

  • Piattaforme WebRTC commerciali, come vLine

La guida alle tecnologie web in tempo reale dello sviluppatore Phil Leggetter fornisce un elenco completo di servizi e librerie di messaggistica.

Crea un servizio di segnalazione con Socket.io su Node

Di seguito è riportato il codice di una semplice app web che utilizza un servizio di segnalazione creato con Socket.io su Node. Il design di Socket.io semplifica la creazione di un servizio per lo scambio di messaggi ed è particolarmente adatto alla segnalazione WebRTC grazie al suo concetto integrato di stanze. Questo esempio non è progettato per essere scalabile come servizio di segnalazione di livello di produzione, ma è semplice da comprendere per un numero relativamente ridotto di utenti.

Socket.io utilizza WebSocket con fallback: long polling AJAX, streaming multipart AJAX, iframe permanente e polling JSONP. È stato eseguito il porting su vari backend, ma è forse più noto per la sua versione Node utilizzata in questo esempio.

In questo esempio non è presente WebRTC. È progettato solo per mostrare come integrare la segnalazione in un'app web. Visualizza il log della console per vedere cosa succede quando i client entrano in una stanza e scambiano messaggi. Questo codelab WebRTC fornisce istruzioni passo passo su come integrarlo in un'app di videochiamata WebRTC completa.

Ecco il client index.html:

<!DOCTYPE html>
<html>
  <head>
    <title>WebRTC client</title>
  </head>
  <body>
    <script src='/socket.io/socket.io.js'></script>
    <script src='js/main.js'></script>
  </body>
</html>

Ecco il file JavaScript main.js a cui viene fatto riferimento nel client:

const isInitiator;

room = prompt('Enter room name:');

const socket = io.connect();

if (room !== '') {
  console.log('Joining room ' + room);
  socket.emit('create or join', room);
}

socket.on('full', (room) => {
  console.log('Room ' + room + ' is full');
});

socket.on('empty', (room) => {
  isInitiator = true;
  console.log('Room ' + room + ' is empty');
});

socket.on('join', (room) => {
  console.log('Making request to join room ' + room);
  console.log('You are the initiator!');
});

socket.on('log', (array) => {
  console.log.apply(console, array);
});

Ecco l'app server completa:

const static = require('node-static');
const http = require('http');
const file = new(static.Server)();
const app = http.createServer(function (req, res) {
  file.serve(req, res);
}).listen(2013);

const io = require('socket.io').listen(app);

io.sockets.on('connection', (socket) => {

  // Convenience function to log server messages to the client
  function log(){
    const array = ['>>> Message from server: '];
    for (const i = 0; i < arguments.length; i++) {
      array.push(arguments[i]);
    }
      socket.emit('log', array);
  }

  socket.on('message', (message) => {
    log('Got message:', message);
    // For a real app, would be room only (not broadcast)
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', (room) => {
    const numClients = io.sockets.clients(room).length;

    log('Room ' + room + ' has ' + numClients + ' client(s)');
    log('Request to create or join room ' + room);

    if (numClients === 0){
      socket.join(room);
      socket.emit('created', room);
    } else if (numClients === 1) {
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room);
    } else { // max two clients
      socket.emit('full', room);
    }
    socket.emit('emit(): client ' + socket.id +
      ' joined room ' + room);
    socket.broadcast.emit('broadcast(): client ' + socket.id +
      ' joined room ' + room);

  });

});

Non è necessario imparare a utilizzare node-static per questo. Viene utilizzato in questo esempio solo per comodità.

Per eseguire questa app su localhost, devi aver installato Node, Socket.IO e node-static. Node può essere scaricato da Node.js (l'installazione è semplice e veloce). Per installare Socket.IO e node-static, esegui Node Package Manager da un terminale nella directory dell'app:

npm install socket.io
npm install node-static

Per avviare il server, esegui questo comando da un terminale nella directory dell'app:

node server.js

Dal browser, apri localhost:2013. Apri una nuova scheda o finestra in qualsiasi browser e apri di nuovo localhost:2013. Per vedere cosa succede, controlla la console. In Chrome e Opera, puoi accedere alla console tramite gli Strumenti per sviluppatori di Google Chrome con Ctrl+Shift+J (o Command+Option+J su Mac).

Qualunque approccio tu scelga per la segnalazione, il backend e l'app client devono, come minimo, fornire servizi simili a questo esempio.

Aspetti problematici della segnalazione

  • RTCPeerConnection non inizierà a raccogliere i candidati finché non verrà chiamato setLocalDescription(). Questo è obbligatorio nella bozza IETF di JSEP.
  • Sfrutta Trickle ICE. Chiama addIceCandidate() non appena arrivano i candidati.

Server di segnalazione pronti all'uso

Se non vuoi creare il tuo, sono disponibili diversi server di segnalazione WebRTC che utilizzano Socket.IO come l'esempio precedente e sono integrati con le librerie JavaScript del client WebRTC:

  • webRTC.io è una delle prime librerie di astrazione per WebRTC.
  • Signalmaster è un server di segnalazione creato per l'utilizzo con la libreria client JavaScript SimpleWebRTC.

Se non vuoi scrivere alcun codice, sono disponibili piattaforme WebRTC commerciali complete di aziende come vLine, OpenTok e Asterisk.

Per la cronaca, Ericsson ha creato un server di segnalazione utilizzando PHP su Apache nei primi tempi di WebRTC. Ora è un po' obsoleto, ma vale la pena esaminare il codice se stai prendendo in considerazione qualcosa di simile.

Sicurezza della segnalazione

"La sicurezza è l'arte di non far accadere nulla."

Salman Rushdie

La crittografia è obbligatoria per tutti i componenti WebRTC.

Tuttavia, i meccanismi di segnalazione non sono definiti dagli standard WebRTC, quindi spetta a te rendere sicura la segnalazione. Se un malintenzionato riesce a compromettere la segnalazione, può interrompere le sessioni, reindirizzare le connessioni e registrare, alterare o inserire contenuti.

Il fattore più importante per proteggere la segnalazione è l'utilizzo di protocolli sicuri, come HTTPS e WSS (ad esempio TLS), che garantiscono che i messaggi non possano essere intercettati non criptati. Inoltre, fai attenzione a non trasmettere messaggi di segnalazione in modo che possano essere accessibili ad altri chiamanti che utilizzano lo stesso server di segnalazione.

Dopo la segnalazione: utilizza ICE per gestire NAT e firewall

Per la segnalazione dei metadati, le app WebRTC utilizzano un server intermediario, ma per lo streaming effettivo di media e dati una volta stabilita una sessione, RTCPeerConnection tenta di connettere i client direttamente o peer-to-peer.

In un mondo più semplice, ogni endpoint WebRTC avrebbe un indirizzo univoco che potrebbe scambiare con altri peer per comunicare direttamente.

Connessione peer-to-peer semplice
Un mondo senza NAT e firewall

In realtà, la maggior parte dei dispositivi si trova dietro uno o più livelli di NAT, alcuni hanno un software antivirus che blocca determinate porte e protocolli e molti si trovano dietro proxy e firewall aziendali. Un firewall e NAT possono essere implementati dallo stesso dispositivo, ad esempio un router Wi-Fi domestico.

Peer dietro NAT e firewall
Il mondo reale

Le app WebRTC possono utilizzare il framework ICE per superare le complessità del networking nel mondo reale. Per consentire questa operazione, la tua app deve trasmettere gli URL del server ICE a RTCPeerConnection, come descritto in questo articolo.

ICE cerca di trovare il percorso migliore per connettere i peer. Prova tutte le possibilità in parallelo e sceglie l'opzione più efficiente. ICE tenta innanzitutto di stabilire una connessione utilizzando l'indirizzo host ottenuto dal sistema operativo e dalla scheda di rete di un dispositivo. Se l'operazione non va a buon fine (come accade per i dispositivi dietro NAT), ICE ottiene un indirizzo esterno utilizzando un server STUN e, se l'operazione non va a buon fine, il traffico viene instradato tramite un server di relay TURN.

In altre parole, un server STUN viene utilizzato per ottenere un indirizzo di rete esterno e i server TURN vengono utilizzati per inoltrare il traffico se la connessione diretta (peer-to-peer) non riesce.

Ogni server TURN supporta STUN. Un server TURN è un server STUN con funzionalità di relaying aggiuntive integrate. ICE gestisce anche le complessità delle configurazioni NAT. In realtà, il NAT hole-punching potrebbe richiedere più di un semplice indirizzo IP:porta pubblico.

Gli URL per i server STUN e/o TURN vengono (facoltativamente) specificati da un'app WebRTC nell'oggetto di configurazione iceServers, che è il primo argomento del costruttore RTCPeerConnection. Per appr.tc, il valore è simile a questo:

{
  'iceServers': [
    {
      'urls': 'stun:stun.l.google.com:19302'
    },
    {
      'urls': 'turn:192.158.29.39:3478?transport=udp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    },
    {
      'urls': 'turn:192.158.29.39:3478?transport=tcp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    }
  ]
}

Una volta che RTCPeerConnection dispone di queste informazioni, la magia di ICE avviene automaticamente. RTCPeerConnection utilizza il framework ICE per determinare il percorso migliore tra i peer, collaborando con i server STUN e TURN, se necessario.

STUN

I NAT forniscono a un dispositivo un indirizzo IP da utilizzare all'interno di una rete locale privata, ma questo indirizzo non può essere utilizzato esternamente. Senza un indirizzo pubblico, i peer WebRTC non possono comunicare. Per risolvere questo problema, WebRTC utilizza STUN.

I server STUN si trovano su internet pubblico e hanno un semplice compito: controllare l'indirizzo IP:porta di una richiesta in entrata (da un'app in esecuzione dietro un NAT) e inviare di nuovo l'indirizzo come risposta. In altre parole, l'app utilizza un server STUN per scoprire il proprio IP:porta da una prospettiva pubblica. Questo processo consente a un peer WebRTC di ottenere un indirizzo accessibile pubblicamente e di passarlo a un altro peer tramite un meccanismo di segnalazione per configurare un link diretto. In pratica, i diversi NAT funzionano in modi diversi e potrebbero esserci più livelli NAT, ma il principio è sempre lo stesso.

I server STUN non devono fare molto o ricordare molto, quindi server STUN con specifiche relativamente basse possono gestire un numero elevato di richieste.

La maggior parte delle chiamate WebRTC stabilisce una connessione utilizzando STUN, l'86% secondo Webrtcstats.com, anche se questa percentuale può essere inferiore per le chiamate tra peer protetti da firewall e configurazioni NAT complesse.

Connessione peer-to-peer tramite un server STUN
Utilizzo di server STUN per ottenere indirizzi IP:porta pubblici

TURN

RTCPeerConnection tenta di stabilire una comunicazione diretta tra i peer tramite UDP. Se non va a buon fine, RTCPeerConnection ricorre al protocollo TCP. Se non funziona, i server TURN possono essere utilizzati come fallback, inoltrando i dati tra gli endpoint.

Per ribadire, TURN viene utilizzato per ritrasmettere lo streaming di audio, video e dati tra i peer, non i dati di segnalazione.

I server TURN hanno indirizzi pubblici, quindi possono essere contattati dai peer anche se questi si trovano dietro firewall o proxy. I server TURN hanno un compito concettualmente semplice: inoltrare un flusso. Tuttavia, a differenza dei server STUN, consumano intrinsecamente molta larghezza di banda. In altre parole, i server TURN devono essere più potenti.

Connessione peer-to-peer tramite un server STUN
The full Monty: STUN, TURN, and signaling

Questo diagramma mostra TURN in azione. Pure STUN non è riuscito, quindi ogni peer ricorre all'utilizzo di un server TURN.

Deployment dei server STUN e TURN

Per i test, Google esegue un server STUN pubblico, stun.l.google.com:19302, utilizzato da appr.tc. Per un servizio STUN/TURN di produzione, utilizza rfc5766-turn-server. Il codice sorgente per i server STUN e TURN è disponibile su GitHub, dove puoi trovare anche link a diverse fonti di informazioni sull'installazione del server. È disponibile anche un'immagine VM per Amazon Web Services.

Un server TURN alternativo è restund, disponibile come codice sorgente e anche per AWS. Di seguito sono riportate le istruzioni per configurare restund su Compute Engine.

  1. Apri il firewall in base alle necessità per tcp=443, udp/tcp=3478.
  2. Crea quattro istanze, una per ogni IP pubblico, immagine Ubuntu 12.06 standard.
  3. Configura la configurazione del firewall locale (consenti TUTTO da TUTTO).
  4. Installa gli strumenti: shell sudo apt-get install make sudo apt-get install gcc
  5. Installa libre da creytiv.com/re.html.
  6. Recupera restund da creytiv.com/restund.html e decomprimi./
  7. wget hancke.name/restund-auth.patch e applicalo con patch -p1 < restund-auth.patch.
  8. Esegui make, sudo make install per libre e restund.
  9. Adatta restund.conf alle tue esigenze (sostituisci gli indirizzi IP e assicurati che contenga lo stesso segreto condiviso) e copia in /etc.
  10. Copia restund/etc/restund in /etc/init.d/.
  11. Configura restund:
    1. Imposta LD_LIBRARY_PATH.
    2. Copia restund.conf in /etc/restund.conf.
    3. Imposta restund.conf per utilizzare il valore 10 corretto. Indirizzo IP.
  12. Esegui restund
  13. Testa utilizzando il client stund dalla macchina remota: ./client IP:port

Oltre alle chiamate individuali: WebRTC multiparte

Potresti anche dare un'occhiata allo standard IETF proposto da Justin Uberti per un'API REST per l'accesso ai servizi TURN.

È facile immaginare casi d'uso per lo streaming di contenuti multimediali che vanno oltre una semplice chiamata individuale. Ad esempio, una videoconferenza tra un gruppo di colleghi o un evento pubblico con un relatore e centinaia o milioni di spettatori.

Un'app WebRTC può utilizzare più RTCPeerConnection in modo che ogni endpoint si connetta a tutti gli altri endpoint in una configurazione mesh. Questo è l'approccio adottato da app come talky.io e funziona molto bene per un piccolo gruppo di colleghi. Inoltre, l'elaborazione e il consumo di larghezza di banda diventano eccessivi, soprattutto per i client mobile.

Mesh: piccola chiamata N-way
Topologia mesh completa: tutti sono connessi a tutti

In alternativa, un'app WebRTC potrebbe scegliere un endpoint per distribuire i flussi a tutti gli altri in una configurazione a stella. Sarebbe anche possibile eseguire un endpoint WebRTC su un server e creare un proprio meccanismo di ridistribuzione (webrtc.org fornisce un'app client di esempio).

A partire da Chrome 31 e Opera 18, un MediaStream di un RTCPeerConnection può essere utilizzato come input per un altro. Ciò può consentire architetture più flessibili perché consente a un'app web di gestire il routing delle chiamate scegliendo a quale altro peer connettersi. Per vedere questo in azione, consulta WebRTC samples Peer connection relay e WebRTC samples Multiple peer connections.

Multipoint Control Unit

Un'opzione migliore per un numero elevato di endpoint è utilizzare un'unità di controllo multipunto (MCU). Si tratta di un server che funge da ponte per distribuire i contenuti multimediali a un numero elevato di partecipanti. Le MCU possono gestire diverse risoluzioni, codec e frame rate in una videoconferenza, gestire la transcodifica, eseguire l'inoltro selettivo degli stream e mixare o registrare audio e video. Per le chiamate di gruppo, ci sono una serie di problemi da considerare, in particolare come visualizzare più input video e mixare l'audio da più fonti. Anche le piattaforme cloud, come vLine, tentano di ottimizzare il routing del traffico.

È possibile acquistare un pacchetto hardware MCU completo o crearne uno personalizzato.

Vista posteriore di Cisco MCU5300
Il retro di una MCU Cisco

Sono disponibili diverse opzioni software MCU open source. Ad esempio, Licode (precedentemente noto come Lynckia) produce una MCU open source per WebRTC. OpenTok ha Mantis.

Oltre i browser: VoIP, telefoni e messaggistica

La natura standardizzata di WebRTC consente di stabilire la comunicazione tra un'app WebRTC in esecuzione in un browser e un dispositivo o una piattaforma in esecuzione su un'altra piattaforma di comunicazione, ad esempio un telefono o un sistema di videoconferenza.

SIP è un protocollo di segnalazione utilizzato dai sistemi VoIP e di videoconferenza. Per consentire la comunicazione tra un'app web WebRTC e un client SIP, ad esempio un sistema di videoconferenza, WebRTC ha bisogno di un server proxy per mediare la segnalazione. La segnalazione deve passare attraverso il gateway, ma una volta stabilita la comunicazione, il traffico SRTP (video e audio) può fluire direttamente peer-to-peer.

La rete telefonica generale (RTG) è la rete a commutazione di circuito di tutti i telefoni analogici "semplici". Per le chiamate tra app web WebRTC e telefoni, il traffico deve passare attraverso un gateway RTG. Allo stesso modo, le app web WebRTC hanno bisogno di un server XMPP intermedio per comunicare con gli endpoint Jingle, come i client di messaggistica istantanea. Jingle è stato sviluppato da Google come estensione di XMPP per abilitare voce e video per i servizi di messaggistica. Le attuali implementazioni di WebRTC si basano sulla libreria C++ libjingle, un'implementazione di Jingle inizialmente sviluppata per Talk.

Diverse app, librerie e piattaforme utilizzano la capacità di WebRTC di comunicare con il mondo esterno:

  • sipML5: un client SIP JavaScript open source
  • jsSIP: libreria SIP JavaScript
  • Phono: API per telefoni JavaScript open source creata come plug-in
  • Zingaya: un widget per telefono incorporabile
  • Twilio: voce e messaggistica
  • Uberconference: conferencing

Gli sviluppatori di sipML5 hanno anche creato il gateway webrtc2sip. Tethr e Tropo hanno dimostrato un framework per le comunicazioni in caso di disastri "in una valigetta" utilizzando una cella OpenBTS per consentire le comunicazioni tra feature phone e computer tramite WebRTC. Si tratta di comunicazione telefonica senza operatore.

Scopri di più

Il codelab WebRTC fornisce istruzioni passo passo su come creare un'app di chat video e di testo utilizzando un servizio di segnalazione Socket.io in esecuzione su Node.

Presentazione di WebRTC di Google I/O del 2013 con il responsabile tecnico di WebRTC, Justin Uberti

Presentazione di Chris Wilson per SFHTML5 - Introduction to WebRTC Apps

Il libro di 350 pagine WebRTC: APIs and RTCWEB Protocols of the HTML5 Real-Time Web fornisce molti dettagli sui percorsi di dati e segnalazione e include una serie di diagrammi dettagliati della topologia di rete.

WebRTC and Signaling: What Two Years Has Taught Us: post del blog di TokBox sul motivo per cui escludere la segnalazione dalle specifiche è stata una buona idea

A Practical Guide to Building WebRTC Apps di Ben Strong fornisce molte informazioni sulle topologie e sull'infrastruttura WebRTC.

Il capitolo su WebRTC del libro High Performance Browser Networking di Ilya Grigorik analizza in dettaglio l'architettura, i casi d'uso e le prestazioni di WebRTC.