JavaScript WebSocket API
Impara la JavaScript WebSocket API: ciclo di vita della connessione, readyState, invio di dati testo, JSON e binari, riconnessione e sicurezza, con un esempio di Echo Chat eseguibile.
Introduzione alla Tecnologia WebSocket
WebSocket è un protocollo che fornisce un canale di comunicazione full-duplex (bidirezionale) tra il browser e un server su un'unica connessione TCP persistente. Una volta aperta la connessione, entrambe le parti possono inviare un messaggio in qualsiasi momento senza dover aspettare che l'altra lo richieda — qualcosa che il semplice HTTP non può fare.
Questa pagina illustra cosa risolvono i WebSocket, il ciclo di vita della connessione, l'invio di dati testo e binari, la gestione dei messaggi JSON, la riconnessione, la sicurezza e quando conviene usare una libreria al posto dell'API nativa. Tutto viene costruito attorno a un esempio di Echo Chat eseguibile.
Perché WebSocket invece di HTTP?
Con il normale HTTP (incluse la Fetch API e XMLHttpRequest), il client deve iniziare ogni scambio. Per ricevere aggiornamenti in tempo reale si deve ricorrere al polling — chiedere ripetutamente "ci sono novità?" — il che spreca larghezza di banda e aumenta la latenza. I WebSocket ribaltano questo schema: il server può inviare dati nel momento esatto in cui li ha, con un overhead per messaggio quasi nullo.
| Approccio | Direzione | Overhead | Ideale per |
|---|---|---|---|
| Richiesta/risposta HTTP | Il client chiede, il server risponde | Header completi per ogni richiesta | Richieste singole, REST API |
| Polling | Il client chiede a intervalli regolari | Molte richieste inutili | Aggiornamenti semplici a bassa frequenza |
| WebSocket | Entrambi inviano liberamente | Un solo handshake, frame minimali | Chat, feed live, giochi, dashboard |
Usa i WebSocket quando il server deve parlare per primo o i messaggi scorrono continuamente: chat, giochi multiplayer, editor collaborativi, ticker di trading e dashboard in tempo reale. Per aggiornamenti occasionali unidirezionali dal server al client, Server-Sent Events o la Push API possono essere più semplici.
Il Ciclo di Vita della Connessione WebSocket
Si apre una connessione passando un URL ws:// (non cifrato) o wss:// (cifrato) al costruttore WebSocket. La connessione attraversa poi quattro stati esposti da socket.readyState:
| Costante | Valore | Significato |
|---|---|---|
WebSocket.CONNECTING | 0 | Handshake in corso (stato iniziale) |
WebSocket.OPEN | 1 | Pronto per inviare e ricevere |
WebSocket.CLOSING | 2 | È stata richiesta la chiusura |
WebSocket.CLOSED | 3 | Connessione chiusa o apertura fallita |
Alle transizioni si reagisce con quattro eventi: open, message, error e close. La regola fondamentale è che si può chiamare socket.send() solo quando readyState è OPEN — inviare prima che scatti open genera un errore.
const socket = new WebSocket("wss://echo.websocket.events");
socket.addEventListener("open", () => {
console.log("open, readyState =", socket.readyState); // 1
socket.send("hello");
});
socket.addEventListener("message", (event) => {
console.log("received:", event.data); // "hello" (echoed back)
});
socket.addEventListener("close", (event) => {
console.log("closed, code =", event.code); // e.g. 1000
});Configurazione dell'Echo Chat WebSocket
Struttura HTML di Base
Prima di tutto, definiamo l'interfaccia utente in HTML: un'area di visualizzazione dei messaggi, un campo di input e i pulsanti di controllo.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>WebSocket Echo Chat</title>
</head>
<body>
<textarea id="chatBox" readonly style="width: 100%; height: 300px"></textarea><br />
<input type="text" id="messageInput" placeholder="Type a message..." style="width: 75%" />
<button onclick="sendMessage()">Send</button>
<button onclick="closeConnection()">Close Connection</button>
</body>
</html>Integrazione di WebSocket con JavaScript
Il JavaScript per la gestione della comunicazione WebSocket è fondamentale per abilitare le interazioni in tempo reale.
<script>
// Accessing the chat box and message input elements from the HTML.
const chatBox = document.getElementById("chatBox");
const messageInput = document.getElementById("messageInput");
// Establishing a WebSocket connection to the echo server.
const socket = new WebSocket("wss://echo.websocket.events");
// When the connection is open, display a connected message in the chat box.
socket.addEventListener("open", function (event) {
chatBox.value += "Connected to the echo server\n";
});
// Handle incoming messages by adding them to the chat box.
socket.addEventListener("message", function (event) {
chatBox.value += "Echoed back: " + event.data + "\n";
});
// Handle connection errors.
socket.addEventListener("error", function (event) {
chatBox.value += "Connection error occurred.\n";
});
// Handle connection closure.
socket.addEventListener("close", function (event) {
chatBox.value += "Connection closed. Code: " + event.code + "\n";
});
// Function to send a message when the send button is clicked.
function sendMessage() {
const message = messageInput.value; // Get the message from the input field.
if (!message) return; // If there's no message, don't do anything.
socket.send(message); // Send the message to the server.
chatBox.value += "You: " + message + "\n"; // Show the message in the chat box.
messageInput.value = ""; // Clear the message input field.
}
// Function to close the WebSocket connection.
function closeConnection() {
if (socket.readyState === WebSocket.OPEN) {
socket.close(1000, "The user closed the connection"); // Close the connection normally.
chatBox.value += "Connection closed by user\n"; // Inform the user in the chat box.
} else {
alert("Connection is not open or already closed."); // Alert if the connection can't be closed.
}
}
// Ensure the WebSocket is closed properly when the webpage is closed or reloaded.
window.addEventListener("beforeunload", function () {
if (socket.readyState === WebSocket.OPEN) {
socket.close(1000, "The page is unloading"); // Close the connection normally.
}
});
</script>Questo script configura una funzionalità di chat su una pagina web che si connette a un server tramite WebSocket. Ecco una spiegazione semplice delle sue parti:
- Accesso agli elementi HTML: Lo script recupera dalla pagina il box della chat e il campo di input dei messaggi per poter interagire con essi.
- Connessione WebSocket: Apre una connessione verso un server che rimanda indietro i messaggi. Ciò significa che tutto ciò che invii a questo server ti verrà restituito immediatamente.
- Visualizzazione dello stato della connessione: Quando la connessione viene stabilita con successo, nel box della chat appare un messaggio che conferma la connessione all'echo server.
- Gestione dei messaggi in arrivo: I messaggi che arrivano dal server vengono aggiunti al box della chat, mostrando che il server ha rimandato indietro i messaggi.
- Invio dei messaggi: È presente una funzione per inviare i messaggi digitati nel campo di input. Se c'è del testo, lo invia al server e lo visualizza nel box della chat come messaggio dell'utente.
- Chiusura della connessione: È presente anche una funzione per chiudere la connessione WebSocket quando necessario, ad esempio quando l'utente decide di chiuderla o quando la pagina viene chiusa.
Questa configurazione consente la comunicazione in tempo reale con il server e aiuta a testare e dimostrare come funzionano le applicazioni di messaggistica.
Ora mettiamo tutto insieme e vediamolo in azione:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>WebSocket Echo Chat</title>
</head>
<body>
<textarea id="chatBox" readonly style="width: 100%; height: 300px">
</textarea>
<br />
<input
type="text"
id="messageInput"
placeholder="Type a message..."
style="width: 75%"
/>
<button onclick="sendMessage()">Send</button>
<button onclick="closeConnection()">Close Connection</button>
</body>
<script>
const chatBox = document.getElementById("chatBox");
const messageInput = document.getElementById("messageInput");
const socket = new WebSocket("wss://echo.websocket.events");
socket.addEventListener("open", function (event) {
chatBox.value += "Connected to the echo server\n";
});
socket.addEventListener("message", function (event) {
chatBox.value += "Echoed back: " + event.data + "\n";
});
socket.addEventListener("error", function (event) {
chatBox.value += "Connection error occurred.\n";
});
socket.addEventListener("close", function (event) {
chatBox.value += "Connection closed. Code: " + event.code + "\n";
});
function sendMessage() {
const message = messageInput.value;
if (!message) return;
socket.send(message);
chatBox.value += "You: " + message + "\n";
messageInput.value = "";
}
function closeConnection() {
if (socket.readyState === WebSocket.OPEN) {
socket.close(1000, "The user closed the connection");
chatBox.value += "Connection closed by user\n";
} else {
alert("Connection not open or already closed.");
}
}
window.addEventListener("beforeunload", function () {
if (socket.readyState === WebSocket.OPEN) {
socket.close(1000, "The page is unloading");
}
});
</script>
</html>Nell'esempio sopra, non appena ci si connette al server, si possono iniziare a inviare messaggi e si riceverà esattamente ciò che si è digitato, rimandato indietro. Se si fa clic sul pulsante 'Close Connection', la connessione WebSocket verrà terminata e non si riceveranno più messaggi in eco.
Funzionalità e Tecniche WebSocket Avanzate
Invio di Dati Strutturati come JSON
Le applicazioni reali raramente inviano stringhe semplici — inviano object. Poiché il canale trasmette solo testo o dati binari, è necessario serializzare gli object con JSON.stringify() prima di inviarli e riconvertirli con JSON.parse() alla ricezione. Consulta Lavorare con JSON per maggiori dettagli su questo formato.
// Sending an object
const payload = { type: "chat", user: "Ann", text: "Hi!" };
socket.send(JSON.stringify(payload));
// Receiving and parsing it
socket.addEventListener("message", (event) => {
const msg = JSON.parse(event.data);
console.log(msg.user + ": " + msg.text); // "Ann: Hi!"
});Un pattern comune è un campo type che permette a una singola connessione di gestire molti tipi di messaggi — chat, presence, typing e così via — ciascuno gestito dal proprio ramo di codice.
Gestione dei Dati Binari
I WebSocket non si limitano al testo. Possono anche trasportare frame binari, utili per audio, immagini o stato di gioco. Imposta socket.binaryType per controllare come arrivano i dati binari in entrata — "blob" (il valore predefinito) o "arraybuffer".
socket.binaryType = "arraybuffer";
// Send raw bytes
const bytes = new Uint8Array([72, 73]); // "HI"
socket.send(bytes);
socket.addEventListener("message", (event) => {
if (typeof event.data === "string") {
console.log("text frame:", event.data);
} else {
const view = new Uint8Array(event.data);
console.log("binary frame, length:", view.length);
}
});Riconnessione Automatica
Le reti cadono. Il WebSocket nativo non si riconnette da solo — quando close scatta inaspettatamente, bisogna riaprire la connessione manualmente, idealmente con un back-off crescente (esponenziale) per non sovraccaricare il server.
let delay = 1000; // start at 1 second
function connect() {
const socket = new WebSocket("wss://echo.websocket.events");
socket.addEventListener("open", () => {
delay = 1000; // reset back-off after a successful connection
});
socket.addEventListener("close", () => {
setTimeout(connect, delay);
delay = Math.min(delay * 2, 30000); // cap at 30 seconds
});
}
connect();Implementare la Sicurezza WebSocket
La sicurezza è fondamentale quando si lavora con i WebSocket:
- Usare WSS: Utilizzare sempre WebSocket Secure (WSS), che cifra i dati trasmessi tra client e server.
- Autenticazione: Implementare l'autenticazione basata su token per garantire che solo gli utenti autorizzati possano stabilire connessioni WebSocket.
- Validazione: Validare correttamente tutti i dati inviati al server per prevenire vulnerabilità web comuni come XSS o SQL injection.
WebSocket e Pattern Pub/Sub
Il pattern publish/subscribe è un modello diffuso nei servizi dati in tempo reale in cui i messaggi vengono trasmessi attraverso un canale. Servizi WebSocket come PubNub offrono API che supportano il modello pub/sub, potenziando le capacità dei WebSocket attraverso la gestione delle connessioni, la cifratura dei dati e la trasmissione basata su canali.
Librerie e Framework per WebSocket
Diverse librerie JavaScript rendono più semplice e robusto il lavoro con i WebSocket:
- Socket.IO: Fornisce funzionalità aggiuntive come la riconnessione automatica, la gestione degli eventi e la gestione delle stanze.
- WebSocket-Node: Un'implementazione di server WebSocket per Node.js.
- ReconnectingWebSocket: Una piccola libreria che aggiunge funzionalità di riconnessione ai WebSocket nativi.
Conclusione
La tecnologia WebSocket è un componente fondamentale per lo sviluppo di applicazioni web interattive e in tempo reale. Integrando i WebSocket nelle tue applicazioni, abiliti un'interazione diretta e bidirezionale tra client e server. Questa guida ha illustrato il ciclo di vita della connessione, l'esempio Echo Chat, i messaggi JSON e binari, la riconnessione e la sicurezza — tutto il necessario per costruire funzionalità in tempo reale robuste.
Vedi Anche
- Fetch API — per richieste HTTP singole quando non si ha bisogno di un canale live.
- XMLHttpRequest — la vecchia API di richiesta su cui WebSocket e Fetch si basano concettualmente.
- Push API — messaggi dal server al client anche quando la pagina è chiusa.
- Lavorare con JSON — il formato standard per i messaggi WebSocket strutturati.