Introduzione ai WebSocket: portare i socket sul Web

Il problema: connessioni client-server e server-client a bassa latenza

Il web si è costruito in gran parte attorno al cosiddetto paradigma di richiesta/risposta di HTTP. Un client carica una pagina web e poi non accade nulla finché l'utente non fa clic sulla pagina successiva. Intorno al 2005, AJAX ha iniziato a rendere il web più dinamico. Tuttavia, tutte le comunicazioni HTTP erano gestite dal client, che ha richiesto l'interazione dell'utente o il polling periodico per caricare nuovi dati dal server.

Le tecnologie che consentono al server di inviare dati al client proprio quando sa che sono disponibili nuovi dati sono in circolazione da un po' di tempo. Si chiamano "Push" o "Comet". Una delle compromissioni più comuni per creare l'illusione di una connessione avviata da un server è chiamata "long polling". Con il polling lungo, il client apre una connessione HTTP al server che lo tiene aperto fino all'invio della risposta. Ogni volta che il server dispone di nuovi dati, invia la risposta (altre tecniche prevedono richieste Flash, XHR multiparte e i cosiddetti file html). I sondaggi lunghi e le altre tecniche funzionano abbastanza bene. Li utilizzi ogni giorno in applicazioni come la chat di Gmail.

Tuttavia, tutte queste soluzioni condividono un problema: portano avanti l'overhead di HTTP, che non le rende adatte alle applicazioni a bassa latenza. Pensa a giochi sparatutto in prima persona multiplayer nel browser o a qualsiasi altro gioco online con un componente in tempo reale.

WebSocket: trasferire i socket sul web

La specifica WebSocket definisce un'API che stabilisce connessioni "socket" tra un browser web e un server. In parole povere: esiste una connessione persistente tra il client e il server ed entrambe le parti possono iniziare a inviare dati in qualsiasi momento.

Per iniziare

Puoi aprire una connessione WebSocket semplicemente chiamando il costruttore WebSocket:

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

Osserva l'ws:. Questo è il nuovo schema di URL per le connessioni WebSocket. È disponibile anche wss: per la connessione WebSocket sicura, così come viene utilizzato https: per le connessioni HTTP sicure.

Collegare immediatamente alcuni gestori di eventi alla connessione ti consente di sapere quando la connessione viene aperta, quando vengono ricevuti i messaggi in arrivo o quando si verifica un errore.

Il secondo argomento accetta sottoprotocolli facoltativi. Può essere una stringa o un array di stringhe. Ogni stringa deve rappresentare il nome di un sottoprotocollo e il server accetta solo uno dei sottoprotocolli passati nell'array. Il sottoprotocollo accettato può essere determinato accedendo alla proprietà protocol dell'oggetto WebSocket.

I nomi di sottoprotocollo devono essere uno dei nomi di sottoprotocollo registrati nel registro IANA. Al momento, a febbraio 2012 è stato registrato un solo nome di sottoprotocollo (sapone).

// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};

Comunicazione con il server

Non appena avremo una connessione al server (quando viene attivato l'evento open), possiamo iniziare a inviare i dati al server utilizzando il metodo send('your message') per l'oggetto connessione. In passato supportava solo le stringhe, ma con le specifiche più recenti ora può anche inviare messaggi binari. Per inviare dati binari, puoi utilizzare l'oggetto Blob o ArrayBuffer.

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

Analogamente, il server potrebbe inviarci messaggi in qualsiasi momento. In questo caso, viene attivato il callback onmessage. Il callback riceve un oggetto evento e il messaggio effettivo è accessibile tramite la proprietà data.

WebSocket può anche ricevere messaggi binari nella specifica più recente. I frame binari possono essere ricevuti nel formato Blob o ArrayBuffer. Per specificare il formato del file binario ricevuto, imposta la proprietà binariType dell'oggetto WebSocket su "blob" o "arraybuffer". Il formato predefinito è "blob". Non è necessario allineare il parametro binariType all'invio.

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};

Un'altra funzione appena aggiunta di WebSocket sono le estensioni. Con le estensioni sarà possibile inviare frame compressi, multiplix e così via. Puoi trovare le estensioni accettate dal server esaminando la proprietà delle estensioni dell'oggetto WebSocket dopo l'evento aperto. A febbraio 2012 non sono ancora state pubblicate specifiche per le estensioni ufficialmente.

// Determining accepted extensions
console.log(connection.extensions);

Comunicazione multiorigine

Essendo un protocollo moderno, la comunicazione multiorigine è integrata direttamente in WebSocket. Sebbene dovresti comunque assicurarti di comunicare solo con client e server che ritieni affidabili, WebSocket consente la comunicazione tra parti su qualsiasi dominio. Il server decide se rendere il servizio disponibile per tutti i client o solo per quelli che risiedono su un insieme di domini ben definiti.

Server proxy

Ogni nuova tecnologia comporta una nuova serie di problemi. Nel caso di WebSocket, è la compatibilità con i server proxy che media le connessioni HTTP nella maggior parte delle reti aziendali. Il protocollo WebSocket utilizza il sistema di upgrade HTTP (che solitamente viene utilizzato per HTTP/SSL) per "eseguire l'upgrade" di una connessione HTTP a una connessione WebSocket. Alcuni server proxy non apprezzano questo tipo di connessione e interromperanno la connessione. Pertanto, anche se un determinato client utilizza il protocollo WebSocket, potrebbe non essere possibile stabilire una connessione. Ciò rende la sezione successiva ancora più importante :)

Usa WebSocket oggi stesso

WebSocket è ancora una tecnologia giovane e non completamente implementata in tutti i browser. Tuttavia, quando WebSocket non è disponibile, puoi utilizzare WebSocket con le librerie che utilizzano uno dei fallback menzionati sopra. Una libreria che è diventata molto popolare in questo dominio è socket.io, che viene fornita con un client e un'implementazione server del protocollo e include i fallback (socket.io non supporta ancora la messaggistica binaria a partire da febbraio 2012). Esistono anche soluzioni commerciali come PusherApp, che possono essere facilmente integrate in qualsiasi ambiente web fornendo un'API HTTP per inviare messaggi WebSocket ai client. A causa dell'ulteriore richiesta HTTP ci sarà sempre un overhead aggiuntivo rispetto a WebSocket puro.

Il lato server

L'utilizzo di WebSocket crea un modello di utilizzo completamente nuovo per le applicazioni lato server. Sebbene gli stack di server tradizionali come LAMP siano progettati attorno al ciclo richiesta/risposta HTTP, spesso non sono adatti a un gran numero di connessioni WebSocket aperte. Mantenere aperte contemporaneamente un numero elevato di connessioni richiede un'architettura che riceva un'elevata contemporaneità a costi ridotti per le prestazioni. Queste architetture sono generalmente progettate attorno al threading o ai cosiddetti IO non bloccanti.

Implementazioni lato server

Versioni protocollo

Il protocollo Wire (un handshake e il trasferimento di dati tra client e server) per WebSocket ora è RFC6455. Le versioni più recenti di Chrome e Chrome per Android sono completamente compatibili con RFC6455, inclusi i messaggi binari. Inoltre, Firefox sarà compatibile con la versione 11 di Internet Explorer. Puoi continuare a utilizzare versioni precedenti del protocollo, ma non è consigliabile, in quanto note come vulnerabili. Se hai implementazioni server per versioni precedenti del protocollo WebSocket, ti consigliamo di eseguirne l'upgrade alla versione più recente.

Casi d'uso

Utilizza WebSocket ogni volta che hai bisogno di una latenza veramente bassa, quasi in tempo reale tra il client e il server. Tieni presente che ciò potrebbe comportare un ripensamento del modo in cui crei le tue applicazioni lato server concentrandoti su tecnologie come le code di eventi. Ecco alcuni casi d'uso di esempio:

  • Giochi online multiplayer
  • Applicazioni di chat
  • Titolo sport in diretta
  • Aggiornamento in tempo reale dei social stream

Demo

Riferimenti