Crea i servizi di backend necessari per un'app WebRTC

Che cos'è l'indicatore?

L'indicatore è il processo 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, ad esempio l'indirizzo IP e la porta di un host come appaiono al mondo esterno

Questa procedura di segnalazione richiede un modo per consentire ai client di inviare messaggi avanti e indietro. Questo meccanismo non è implementato dalle API WebRTC. Devi crearlo autonomamente. Più avanti in questo articolo scoprirai come creare un servizio di segnalazione. Per prima cosa, però, devi avere un po' di contesto.

Perché l'indicatore non è definito 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 dal JSEP (JavaScript Session Establishment Protocol):

L'architettura JSEP evita inoltre che un browser debba salvare lo stato, ovvero funzionare come una macchina a stati di segnalazione. Ciò sarebbe problematico se, ad esempio, i dati di segnalazione andassero 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 offer e answer, i metadati multimediali sopra menzionati. Le offerte e le risposte vengono comunicate in formato Session Description Protocol (SDP), che ha il seguente aspetto:

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 significano tutte queste parole complicate relative ai dati proprietari? 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' complicato da manipolare con JavaScript e si discute se le versioni future di WebRTC debbano utilizzare invece JSON, ma ci sono alcuni vantaggi nell'utilizzare SDP.

RTCPeerConnection API e segnalazioni: offerta, risposta e candidato

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

Per inizializzare questa procedura, RTCPeerConnection ha due attività:

  • Verifica le condizioni dei media locali, ad esempio la risoluzione e le funzionalità del codec. Questi sono i metadati utilizzati per il meccanismo di offerta e risposta.
  • Ottieni 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:

  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 stringa e utilizza un meccanismo di segnalazione per inviarla a Eva.
  5. Eva chiama setRemoteDescription() con l'offerta di Alice, in modo che RTCPeerConnection venga a conoscenza della configurazione di Alice.
  6. Eva chiama createAnswer() e al callback di successo viene passata una descrizione della sessione locale, ovvero la risposta di Eva.
  7. Eva imposta la sua risposta come descrizione locale chiamando setLocalDescription().
  8. Eva utilizza quindi il meccanismo di segnalazione per inviare la sua risposta sotto forma di stringa ad Alice.
  9. Alice imposta la risposta di Eva come descrizione della sessione remota utilizzando setRemoteDescription().

Alice ed Eva devono anche 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. L'handler viene chiamato quando le reti candidate diventano disponibili.
  3. Nell'handler, Alice invia a Eva i dati candidati sotto forma di stringa tramite il canale di segnalazione.
  4. Quando Eva riceve un messaggio del candidato da Alice, chiama addIceCandidate() per aggiungerlo alla descrizione del peer remoto.

JSEP supporta la funzionalità ICE Candidate Trickling, che consente all'utente che chiama di fornire in modo incrementale i candidati all'utente chiamato dopo l'offerta iniziale e all'utente chiamato di iniziare ad agire sulla chiamata e configurare una connessione senza attendere l'arrivo di tutti i candidati.

Codice WebRTC per l'invio di indicatori

Il seguente snippet di codice è un esempio di codice W3C che riassume la procedura completa di indicazione. Il codice presuppone l'esistenza di un qualche meccanismo di segnalazione, SignalingChannel. La segnalazione viene discussa più nel dettaglio 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 in azione le procedure di offerta/risposta e di scambio di candidati, consulta simpl.info RTCPeerConnection e controlla il log della console per un esempio di chat video a una pagina. Per saperne di più, scarica un dump completo degli indicatori e delle statistiche di WebRTC dalla pagina about://webrtc-internals in Google Chrome o dalla pagina opera://webrtc-internals in Opera.

Rilevamento di peer

È un modo elegante per chiedere "Come faccio a trovare qualcuno con cui parlare?"

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

I meccanismi di rilevamento dei peer non sono definiti da WebRTC e non puoi accedere alle opzioni qui. La procedura può essere semplice come inviare un'email o un messaggio con un URL. Per le app di videochat, come Talky, tawk.to e Browser Meeting, puoi invitare gli utenti a una chiamata condividendo un link personalizzato. Lo sviluppatore Chris Ball ha creato un esperimento intrigante su serverless-webrtc che consente ai partecipanti alle chiamate WebRTC di scambiarsi metadati tramite qualsiasi servizio di messaggistica, ad esempio messaggistica istantanea, email o piccioni viaggiatori.

Come puoi creare un servizio di segnalazione?

Lo ripetiamo: i protocolli e i meccanismi di segnalazione non sono definiti dagli standard WebRTC. Qualunque sia la tua scelta, devi avere 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".

Fortunatamente, i messaggi di segnalazione sono piccoli e vengono scambiati principalmente all'inizio di una chiamata. Durante i 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 per tutti i messaggi.

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

Invia messaggi dal server al client

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

Di recente, l'API EventSource è stata ampiamente implementata. In questo modo vengono attivati gli eventi inviati dal server, ovvero i dati inviati da un server web a un client del 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 passa un messaggio da un chiamante, inviato tramite richiesta XHR, inviandolo tramite EventSource al chiamato.

WebSocket è una soluzione più naturale, progettata per la comunicazione client-server full duplex, ovvero i messaggi possono fluire in entrambe le direzioni contemporaneamente. Un vantaggio 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 hosting web per linguaggi come PHP, Python e Ruby.

Tutti i browser moderni, ad eccezione di Opera Mini, supportano WebSocket e, cosa 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 di attraversamento del proxy. Per ulteriori informazioni su WebSocket e sul traversale del proxy, consulta il capitolo WebRTC in High Performance Browser Networking di Ilya Grigorik.

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

Segnalazioni su larga scala

Sebbene un servizio di segnalazione consumi relativamente poca larghezza di banda e CPU per client, i server di segnalazione di un'app popolare potrebbero dover gestire molti messaggi da località diverse con livelli elevati di concorrenza. Le app WebRTC che ricevono molto traffico richiedono server di segnalazione in grado di gestire un carico considerevole. 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 l'invio di indicatori (le implementazioni del server includono ejabberd e Openfire. I client JavaScript, come Strophe.js, utilizzano BOSH per emulare lo streaming bidirezionale, ma per diversi motivi, BOSH potrebbe non essere efficiente come WebSocket e, per gli stessi motivi, potrebbe non essere scalabile. A proposito, Jingle è un'estensione XMPP per abilitare la voce e il video. Il progetto WebRTC utilizza componenti di rete e trasporto della libreria libjingle, un'implementazione in 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 che utilizzano il protocollo STOMP su WebSocket.)

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

  • Piattaforme WebRTC commerciali, come vLine

La Real-Time Web Technologies Guide (Guida alle tecnologie web in tempo reale) dello sviluppatore Phil Leggetter fornisce un elenco completo di librerie e servizi di messaggistica.

Creare 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 e Socket.io è particolarmente adatto al signaling WebRTC grazie al suo concetto di stanze integrato. Questo esempio non è progettato per essere scalato come servizio di segnalazione di livello di produzione, ma è facile da comprendere per un numero relativamente ridotto di utenti.

Socket.io utilizza WebSocket con soluzioni di riserva: polling lungo AJAX, streaming multipart AJAX, iframe permanente e polling JSONP. È stato portato su vari backend, ma è forse più noto per la versione Node utilizzata in questo esempio.

In questo esempio non è presente WebRTC. È progettato solo per mostrare come creare l'indicatore in un'app web. Visualizza il log della console per vedere cosa succede quando i client si uniscono a una stanza e scambiano messaggi. Questo codelab WebRTC fornisce istruzioni dettagliate su come integrarlo in un'app di video chat WebRTC completa.

Ecco il cliente 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 conoscere node-static per farlo. È solo che viene utilizzato in questo esempio.

Per eseguire questa app su localhost, devi installare 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 il seguente comando da un terminale nella directory dell'app:

node server.js

Apri localhost:2013 dal browser. 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 scegli per l'invio di indicatori, il tuo backend e la tua app client devono fornire servizi simili a questo esempio.

Problemi relativi alla segnalazione

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

Server di segnalazione pronti all'uso

Se non vuoi crearne uno tuo, sono disponibili diversi server di segnalazione WebRTC che utilizzano Socket.IO come nell'esempio precedente e sono integrati con le librerie JavaScript 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 giorni di WebRTC. Questo codice è ormai un po' obsoleto, ma vale la pena esaminarlo se stai valutando qualcosa di simile.

Sicurezza dei segnali

"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 è tua responsabilità rendere sicura la segnalazione. Se un malintenzionato riesce a intercettare i segnali, può interrompere le sessioni, reindirizzare le connessioni e registrare, modificare o iniettare contenuti.

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

Dopo l'indicatore: utilizza ICE per gestire NAT e firewall

Per l'indicazione dei metadati, le app WebRTC utilizzano un server intermedio, ma per lo streaming di contenuti multimediali e dati effettivi, una volta stabilita una sessione, RTCPeerConnection tenta di connettere i client direttamente o in peer-to-peer.

In un mondo ideale, 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 dispongono di software antivirus che bloccano determinate porte e protocolli e molti si trovano dietro proxy e firewall aziendali. In effetti, un firewall e una NAT possono essere implementati dallo stesso dispositivo, ad esempio un router Wi-Fi di casa.

Peer dietro NAT e firewall
Il mondo reale

Le app WebRTC possono utilizzare il framework ICE per superare le complessità della rete reale. Per consentire questo, l'app deve trasmettere gli URL dei 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 che funziona. ICE tenta innanzitutto di stabilire una connessione utilizzando l'indirizzo host ottenuto dal sistema operativo e dalla scheda di rete di un dispositivo. Se la ricerca non va a buon fine (come accade per i dispositivi dietro NAT), ICE ottiene un indirizzo esterno utilizzando un server STUN e, se la ricerca non va a buon fine, il traffico viene indirizzato tramite un server di inoltro 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 va a buon fine.

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

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

{
  '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 ha queste informazioni, la magia di ICE avviene automaticamente. RTCPeerConnection utilizza il framework ICE per trovare il percorso migliore tra i peer, lavorando 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 aggirare questo problema, WebRTC utilizza STUN.

I server STUN sono disponibili su internet pubblico e hanno un'unica semplice attività: controllare l'indirizzo IP:porta di una richiesta in entrata (da un'app in esecuzione dietro un NAT) e inviare nuovamente l'indirizzo come risposta. In altre parole, l'app utilizza un server STUN per scoprire il proprio indirizzo IP:porta da un punto di vista pubblico. Questo processo consente a un peer WebRTC di ottenere un indirizzo pubblicamente accessibile per sé e poi di trasmetterlo a un altro peer tramite un meccanismo di segnalazione per configurare un link diretto. In pratica, NAT diversi funzionano in modi diversi e potrebbero essere presenti più livelli NAT, ma il principio rimane lo stesso.

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

La maggior parte delle chiamate WebRTC stabilisce una connessione utilizzando STUN (86% secondo Webrtcstats.com), anche se questo valore 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

GIRA

RTCPeerConnection tenta di configurare la comunicazione diretta tra i peer tramite UDP. Se non va a buon fine, RTCPeerConnection ricorre al protocollo TCP. In caso di fallimento, i server TURN possono essere utilizzati come soluzione alternativa, inoltrando i dati tra gli endpoint.

Ti ricordo che TURN viene utilizzato per inoltrare lo streaming di audio, video e dati tra peer, non per 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: ritrasmettere uno stream. 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
Il pacchetto completo: STUN, TURN e segnalazioni

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

Deployment di server STUN e TURN

Per i test, Google gestisce 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 anche trovare 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 il rimborso su Compute Engine.

  1. Apri il firewall come necessario per tcp=443, udp/tcp=3478.
  2. Crea quattro istanze, una per ogni IP pubblico, con immagine Ubuntu 12.06 standard.
  3. Configura la configurazione del firewall locale (consenti QUALSIASI da QUALSIASI).
  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 il file restund da creytiv.com/restund.html e decomprimilo./
  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 la stessa chiave segreta condivisa) e copia in /etc.
  10. Copia restund/etc/restund in /etc/init.d/.
  11. Configura il rimborso:
    1. Imposta LD_LIBRARY_PATH.
    2. Copia restund.conf in /etc/restund.conf.
    3. Imposta restund.conf per utilizzare il 10 corretto. Indirizzo IP.
  12. Esegui restund
  13. Esegui il test utilizzando il client di gestione da una macchina remota: ./client IP:port

Oltre la comunicazione uno a uno: WebRTC multipartito

Ti consigliamo inoltre di 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 uno a uno. Ad esempio, le videoconferenze tra un gruppo di colleghi o un evento pubblico con un oratore e centinaia o milioni di spettatori.

Un'app WebRTC può utilizzare più RTCPeerConnections 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 numero ristretto di pari. Oltre questo limite, il consumo di risorse di elaborazione e larghezza di banda diventa eccessivo, in particolare per i client mobile.

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

In alternativa, un'app WebRTC potrebbe scegliere un endpoint per distribuire gli stream a tutti gli altri in una configurazione a stella. È anche possibile eseguire un endpoint WebRTC su un server e creare il 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 come funziona, consulta Esempi di WebRTC: inoltro della connessione tra pari ed Esempi di WebRTC: più connessioni tra pari.

Unità di controllo multipunto

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 contenuti multimediali tra un numero elevato di partecipanti. Le MCU possono gestire risoluzioni, codec e frequenze fotogrammi diversi in una videoconferenza, gestire la transcodifica, inoltrare stream in modo selettivo e mixare o registrare audio e video. Per le chiamate con più partecipanti, è necessario considerare una serie di problemi, in particolare come visualizzare più input video e mixare l'audio da più sorgenti. Anche le piattaforme cloud, come vLine, tentano di ottimizzare l'instradamento del traffico.

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

Vista posteriore di Cisco MCU5300
La parte posteriore di un'MCU Cisco

Sono disponibili diverse opzioni di software MCU open source. Ad esempio, Licode (precedentemente noto come Lynckia) produce un MCU open source per WebRTC. OpenTok dispone di 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 videoconferenze, WebRTC ha bisogno di un server proxy per mediare il signaling. Il signaling deve passare attraverso il gateway, ma una volta stabilita la comunicazione, il traffico SRTP (video e audio) può essere trasmesso direttamente in peer-to-peer.

La rete telefonica generale (RTG) è la rete a commutazione di circuito di tutti i telefoni analogici "comuni". Per le chiamate tra app web WebRTC e telefoni, il traffico deve passare attraverso un gateway PSTN. Analogamente, le app web WebRTC richiedono 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 la voce e il 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 sfruttano la capacità di WebRTC di comunicare con il mondo esterno:

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

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

Scopri di più

Il codelab WebRTC fornisce istruzioni dettagliate 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 al Google I/O del 2013 con il Technical Lead di WebRTC, Justin Uberti

Presentazione di Chris Wilson su SFHTML5 - Introduzione alle app WebRTC

Il libro di 350 pagine WebRTC: API e protocolli RTCWEB del web in tempo reale HTML5 fornisce molti dettagli sui percorsi di dati e segnalazione e include una serie di diagrammi dettagliati della topologia di rete.

WebRTC e Signaling: cosa ci hanno insegnato due anni: post del blog di TokBox sul perché è stata una buona idea escludere Signaling dalla specifica

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

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