Inviare dati tra browser con canali di dati WebRTC

L'invio di dati tra due browser per comunicazione, giochi o trasferimento di file può essere un processo piuttosto complicato. Richiede la configurazione e il pagamento di un server per il trasferimento dei dati e, eventualmente, la scalabilità a più data center. In questo scenario, è possibile che si verifichino latenze elevate ed è difficile mantenere privati i dati.

Questi problemi possono essere attenuati utilizzando l'API RTCDataChannel di WebRTC per trasferire i dati direttamente da un peer all'altro. Questo articolo illustra le nozioni di base su come configurare e utilizzare i canali di dati, nonché i casi d'uso comuni attualmente disponibili sul web.

Perché un altro canale di dati?

Abbiamo WebSocket, AJAX e Server Sent Events. Perché abbiamo bisogno di un altro canale di comunicazione? WebSocket è bidirezionale, ma tutte queste tecnologie sono progettate per la comunicazione verso o da un server.

RTCDataChannel adotta un approccio diverso:

  • Funziona con l'API RTCPeerConnection, che consente la connettività peer-to-peer. Ciò può comportare una latenza inferiore: nessun server intermedio e meno "hop".
  • RTCDataChannel utilizza il protocollo Stream Control Transmission Protocol (SCTP), che consente la configurazione della trasmissione e della ritrasmissione con semantica di invio configurabile e fuori sequenza.

RTCDataChannel è ora disponibile con il supporto SCTP su computer e Android in Google Chrome, Opera e Firefox.

Un avvertimento: segnalazione, STUN e TURN

WebRTC consente la comunicazione peer-to-peer, ma ha comunque bisogno di server per il signaling per scambiare metadati di rete e multimediali al fine di avviare una connessione tra peer.

WebRTC gestisce i NAT e i firewall con:

  • Il framework ICE per stabilire il percorso di rete migliore possibile tra i peer.
  • Server STUN per verificare un indirizzo IP e una porta accessibili pubblicamente per ogni peer.
  • Server TURN se la connessione diretta non va a buon fine e il trasferimento dei dati è necessario.

Per saperne di più su come WebRTC funziona con i server per il signaling e la rete, consulta WebRTC nel mondo reale: STUN, TURN e signaling.

Le funzionalità

L'API RTCDataChannel supporta un insieme flessibile di tipi di dati. L'API è progettata per imitare esattamente WebSocket e RTCDataChannel supporta le stringhe e alcuni dei tipi di dati binari in JavaScript, come Blob, ArrayBuffer e ArrayBufferView. Questi tipi possono essere utili quando si lavora con il trasferimento di file e i giochi multiplayer.

RTCDataChannel può funzionare in modalità non affidabile e non ordinata (analoga a User Datagram Protocol o UDP), in modalità affidabile e ordinata (analoga a Transmission Control Protocol o TCP) e in modalità affidabili parziali:

  • La modalità affidabile e ordinata garantisce la trasmissione dei messaggi e anche l'ordine in cui vengono recapitati. Ciò comporta un overhead aggiuntivo, che potrebbe rendere questa modalità più lenta.
  • La modalità non affidabile e non ordinata non garantisce che ogni messaggio arrivi all'altra parte né l'ordine in cui arriva. In questo modo, viene rimosso il sovraccarico, consentendo a questa modalità di funzionare molto più velocemente.
  • La modalità attendibile parziale garantisce la trasmissione del messaggio in base a una condizione specifica, ad esempio un timeout di ritrasmissione o un numero massimo di ritrasmissioni. Anche l'ordinamento dei messaggi è configurabile.

Le prestazioni delle prime due modalità sono circa le stesse quando non si verificano perdite di pacchetti. Tuttavia, in modalità affidabile e ordinata, un pacchetto perso causa il blocco di altri pacchetti e il pacchetto perso potrebbe essere obsoleto al momento della sua ritrasmissione e dell'arrivo. È ovviamente possibile utilizzare più canali di dati all'interno della stessa app, ciascuno con la propria semantica attendibile o non attendibile.

Ecco una tabella utile tratta da High Performance Browser Networking di Ilya Grigorik:

TCPUDPSCTP
AffidabilitàAffidabileNon attendibileConfigurabile
DeliveryOrderedNon ordinatiConfigurabile
TrasmissioneOrientata ai byteOrientata ai messaggiOrientata ai messaggi
Controllo del flussoNo
Controllo della congestioneNo

Successivamente, scoprirai come configurare RTCDataChannel per utilizzare la modalità affidabile e ordinata o non affidabile e non ordinata.

Configurazione dei canali di dati

Esistono diverse dimostrazioni semplici di RTCDataChannel online:

In questi esempi, il browser effettua una connessione peer con se stesso, quindi crea un canale di dati e invia un messaggio tramite la connessione peer. Viene quindi creato un canale di dati e il messaggio viene inviato tramite la connessione peer. Finalmente, il messaggio viene visualizzato nella casella sull'altro lato della pagina.

Il codice per iniziare è breve:

const peerConnection = new RTCPeerConnection();

// Establish your peer connection using your signaling channel here
const dataChannel =
  peerConnection.createDataChannel("myLabel", dataChannelOptions);

dataChannel.onerror = (error) => {
  console.log("Data Channel Error:", error);
};

dataChannel.onmessage = (event) => {
  console.log("Got Data Channel Message:", event.data);
};

dataChannel.onopen = () => {
  dataChannel.send("Hello World!");
};

dataChannel.onclose = () => {
  console.log("The Data Channel is Closed");
};

L'oggetto dataChannel viene creato da una connessione peer già stabilita. Può essere creato prima o dopo l'invio dell'indicatore. Poi, specifica un'etichetta per distinguere questo canale dagli altri e un insieme di impostazioni di configurazione facoltative:

const dataChannelOptions = {
  ordered: false, // do not guarantee order
  maxPacketLifeTime: 3000, // in milliseconds
};

È anche possibile aggiungere un'opzione maxRetransmits (il numero di volte da provare prima del fallimento), ma puoi specificare solo maxRetransmits o maxPacketLifeTime, non entrambe. Per la semantica UDP, imposta maxRetransmits su 0 e ordered su false. Per ulteriori informazioni, consulta queste RFC IETF: Stream Control Transmission Protocol ed Stream Control Transmission Protocol Partial Reliability Extension.

  • ordered: se il canale di dati deve garantire l'ordine o meno
  • maxPacketLifeTime: il tempo massimo per tentare di ritrasmettere un messaggio non riuscito
  • maxRetransmits: il numero massimo di volte per tentare di ritrasmettere un messaggio non riuscito
  • protocol: consente di utilizzare un sottoprotocollo che fornisce metainformazioni all'app
  • negotiated: se impostato su true, rimuove la configurazione automatica di un canale dati sull'altro peer, fornendo un modo per creare un canale dati con lo stesso ID sull'altro lato
  • id: ti consente di fornire il tuo ID canale, che può essere utilizzato solo in combinazione con negotiated impostato su true.

Le uniche opzioni che la maggior parte degli utenti deve utilizzare sono le prime tre: ordered, maxPacketLifeTime e maxRetransmits. Con SCTP (ora utilizzato da tutti i browser che supportano WebRTC), affidabile e ordinato è true per impostazione predefinita. Ha senso utilizzare non attendibili e non ordinati se vuoi il controllo completo dal livello dell'app, ma nella maggior parte dei casi è utile un'affidabilità parziale.

Tieni presente che, come per WebSocket, RTCDataChannel attiva gli eventi quando viene stabilita, chiusa o presenta errori una connessione e quando riceve un messaggio dall'altro peer.

È una challenge sicura?

La crittografia è obbligatoria per tutti i componenti WebRTC. Con RTCDataChannel, tutti i dati sono protetti con Datagram Transport Layer Security (DTLS). DTLS è un derivato di SSL, il che significa che i tuoi dati saranno protetti come con qualsiasi connessione basata su SSL standard. DTLS è standardizzato e integrato in tutti i browser che supportano WebRTC. Per ulteriori informazioni, consulta la wiki di Wireshark.

Modificare il modo in cui pensi ai dati

La gestione di grandi quantità di dati può essere un problema in JavaScript. Come hanno sottolineato gli sviluppatori di Sharefest, questo ha richiesto di ripensare ai dati in un modo nuovo. Se stai trasferendo un file più grande della memoria disponibile, devi pensare a nuovi modi per salvare queste informazioni. È qui che entrano in gioco tecnologie come l'API FileSystem, come vedrai di seguito.

Creare un'app di condivisione di file

Ora è possibile creare un'app web che può condividere file nel browser con RTCDataChannel. Se sviluppi su RTCDataChannel, i dati dei file trasferiti sono criptati e non vengono toccati dai server di un fornitore di app. Questa funzionalità, combinata con la possibilità di connettersi a più client per una condivisione più rapida, rende la condivisione di file WebRTC una valida candidata per il web.

Per un trasferimento riuscito sono necessari diversi passaggi:

  1. Leggi un file in JavaScript utilizzando l'API File.
  2. Crea una connessione peer tra i client con RTCPeerConnection.
  3. Crea un canale di dati tra i clienti con RTCDataChannel.

Esistono diversi aspetti da considerare quando si tenta di inviare file tramite RTCDataChannel:

  • Dimensioni del file: se le dimensioni del file sono ragionevolmente ridotte e possono essere archiviate e caricate come un unico blob, puoi caricarle in memoria utilizzando l'API File e poi inviare il file così com'è tramite un canale affidabile (anche se tieni presente che i browser impongono limiti alle dimensioni massime del trasferimento). Man mano che le dimensioni del file aumentano, le cose si complicano. Quando è necessario un meccanismo di suddivisione in blocchi, i blocchi di file vengono caricati e inviati a un altro peer, insieme ai metadati chunkID per consentirne il riconoscimento. Tieni presente che, in questo caso, devi anche salvare prima i chunk nello spazio di archiviazione offline (ad esempio utilizzando l'API FileSystem) e salvarli sul disco dell'utente solo quando hai il file nella sua interezza.
  • Dimensione del chunk:si tratta dei più piccoli "atomi" di dati per la tua app. Il chunking è necessario perché al momento esiste un limite di dimensione di invio (anche se questo verrà risolto in una versione futura dei canali di dati). Il consiglio attuale per la dimensione massima del chunk è 64 KB.

Una volta che il file è stato completamente trasferito all'altro lato, può essere scaricato utilizzando un tag di ancoraggio:

function saveFile(blob) {
  const link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = 'File Name';
  link.click();
};

Queste app di condivisione di file su PubShare e GitHub utilizzano questa tecnica. Entrambi sono open source e forniscono una buona base per un'app di condivisione file basata su RTCDataChannel.

Cosa puoi fare?

RTCDataChannel apre le porte a nuovi modi per creare app per la condivisione di file, giochi multiplayer e la pubblicazione di contenuti.

  • Condivisione di file peer-to-peer come descritto in precedenza
  • Giochi multiplayer, abbinati ad altre tecnologie, come WebGL, come nel caso di BananaBread di Mozilla
  • La distribuzione dei contenuti è stata reinventata da PeerCDN, un framework che carica risorse web tramite la comunicazione di dati peer-to-peer

Cambiare il modo in cui crei le app

Ora puoi offrire app più coinvolgenti utilizzando connessioni ad alte prestazioni e bassa latenza tramite RTCDataChannel. Framework come PeerJS e l'SDK PubNub WebRTC semplificano l'implementazione di RTCDataChannel e l'API ora è ampiamente supportata su tutte le piattaforme.

L'avvento di RTCDataChannel può cambiare il modo in cui pensi al trasferimento dei dati nel browser.

Scopri di più