Il problema: connessioni client-server e server-client a bassa latenza
Il web è stato ampiamente costruito attorno al cosiddetto paradigma di richiesta/risposta di HTTP. Un client carica una pagina web e poi non succede 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, il che richiedeva 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 nel momento stesso in cui sa che sono disponibili nuovi dati esistono da un po' di tempo. Vengono chiamate "Push" o "Comet". Uno degli hack più comuni per creare l'illusione di una connessione avviata dal server è chiamato long polling. Con il polling prolungato, il client apre una connessione HTTP al server, che la mantiene aperta fino all'invio della risposta. Ogni volta che il server ha effettivamente nuovi dati, invia la risposta (altre tecniche coinvolgono Flash, richieste XHR multipart e i cosiddetti htmlfile). Il long polling e le altre tecniche funzionano abbastanza bene. Le usi ogni giorno in applicazioni come la chat di Gmail.
Tuttavia, tutte queste soluzioni alternative condividono un problema: comportano l'overhead di HTTP, il che non le rende adatte per le applicazioni a bassa latenza. Pensa ai giochi sparatutto in prima persona multiplayer nel browser o a qualsiasi altro gioco online con un componente in tempo reale.
Introduzione a WebSocket: portare i socket sul web
La specifica WebSocket definisce un'API che stabilisce connessioni "socket" tra un browser web e un server. In parole semplici: esiste una connessione permanente tra il client e il server ed entrambe le parti possono iniziare a inviare dati in qualsiasi momento.
Per iniziare
Per aprire una connessione WebSocket, basta chiamare il costruttore WebSocket:
var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);
Osserva il valore ws:. Questo è il nuovo schema URL per le connessioni WebSocket. Esiste anche wss: per la connessione WebSocket sicura, nello stesso modo in cui https: viene utilizzato per le connessioni HTTP sicure.
Se colleghi immediatamente alcuni gestori di eventi alla connessione, puoi sapere quando la connessione viene aperta, quando riceve messaggi in arrivo o quando si verifica un errore.
Il secondo argomento accetta protocolli secondari facoltativi. Può essere una stringa o un array di stringhe. Ogni stringa deve rappresentare un nome di sub-protocollo e il server accetta solo uno dei sub-protocolli passati nell'array. Il sub-protocollo accettato può essere determinato accedendo alla proprietà protocol dell'oggetto WebSocket.
I nomi dei sottoprotocolli devono essere tra quelli registrati nel registro IANA. Al momento, esiste un solo nome di sottoprotocollo (soap) registrato a febbraio 2012.
// 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 abbiamo una connessione al server (quando viene attivato l'evento open), possiamo iniziare a inviare dati al server utilizzando il metodo send('your message') sull'oggetto di connessione. In precedenza supportava solo le stringhe, ma nell'ultima specifica può inviare anche 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);
Allo stesso modo, 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 nell'ultima specifica. I frame binari possono essere ricevuti in formato Blob o ArrayBuffer. Per specificare il formato del file binario ricevuto, imposta la proprietà binaryType dell'oggetto WebSocket su "blob" o "arraybuffer". Il formato predefinito è "blob". Non è necessario allineare il parametro binaryType durante l'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 funzionalità aggiunta di recente a WebSocket sono le estensioni. Utilizzando le estensioni, sarà possibile inviare frame compressi, multiplexati e così via. Puoi trovare le estensioni accettate dal server esaminando la proprietà extensions dell'oggetto WebSocket dopo l'evento open. A febbraio 2012 non esiste ancora una specifica delle estensioni pubblicata ufficialmente.
// Determining accepted extensions
console.log(connection.extensions);
Comunicazione multiorigine
Essendo un protocollo moderno, la comunicazione tra origini è integrata direttamente in WebSocket. Anche se devi comunque assicurarti di comunicare solo con client e server di cui ti fidi, WebSocket consente la comunicazione tra parti su qualsiasi dominio. Il server decide se rendere disponibile il proprio servizio a tutti i client o solo a quelli che risiedono in un insieme di domini ben definiti.
Server proxy
Ogni nuova tecnologia porta con sé una nuova serie di problemi. Nel caso di WebSocket, si tratta della compatibilità con i server proxy che mediano le connessioni HTTP nella maggior parte delle reti aziendali. Il protocollo WebSocket utilizza il sistema di upgrade HTTP (normalmente utilizzato per HTTP/SSL) per eseguire l'upgrade di una connessione HTTP a una connessione WebSocket. Alcuni server proxy non accettano questa operazione e interrompono la connessione. Pertanto, anche se un determinato client utilizza il protocollo WebSocket, potrebbe non essere possibile stabilire una connessione. Questo rende la sezione successiva ancora più importante :)
Utilizza i WebSocket oggi stesso
WebSocket è ancora una tecnologia giovane e non è completamente implementata in tutti i browser. Tuttavia, puoi utilizzare WebSocket oggi con librerie che utilizzano uno dei fallback menzionati sopra ogni volta che WebSocket non è disponibile. Una libreria che è diventata molto popolare in questo dominio è socket.io, che include un'implementazione del protocollo lato client e lato server e include i fallback (socket.io non supporta ancora i messaggi binari a 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 della richiesta HTTP aggiuntiva, l'overhead sarà sempre maggiore rispetto a WebSocket puro.
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 di richiesta/risposta HTTP, spesso non gestiscono bene un numero elevato di connessioni WebSocket aperte. Mantenere aperto un numero elevato di connessioni contemporaneamente richiede un'architettura che riceva un'elevata concorrenza a un costo di prestazioni ridotto. Queste architetture sono generalmente progettate attorno al threading o al cosiddetto I/O non bloccante.
Implementazioni lato server
- Node.js
- Java
- Ruby
- Python
- Erlang
- C++
- .NET
Versioni del protocollo
Il protocollo di rete (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, inclusa la messaggistica binaria. Inoltre, Firefox sarà compatibile con la versione 11 e Internet Explorer con la versione 10. Puoi comunque utilizzare versioni precedenti del protocollo, ma non è consigliato perché sono note per essere vulnerabili. Se hai implementazioni server per versioni precedenti del protocollo WebSocket, ti consigliamo di eseguire l'upgrade all'ultima versione.
Casi d'uso
Utilizza WebSocket ogni volta che hai bisogno di una connessione a bassa latenza, quasi in tempo reale, tra il client e il server. Tieni presente che questo potrebbe comportare un ripensamento del modo in cui crei le tue applicazioni lato server, con una nuova attenzione a tecnologie come le code di eventi. Ecco alcuni casi d'uso di esempio:
- Giochi multiplayer online
- Applicazioni di chat
- Ticker sportivo in diretta
- Stream social aggiornati in tempo reale
Demo
- Plink
- Dipingi con me
- Pixelatr
- Tratteggiata
- Cruciverba online multiplayer di massa
- Server di ping (utilizzato negli esempi precedenti)
- Esempio di HTML5demos