Passa al contenuto principale

Guida all'implementazione del protocollo WebSocket (RFC 6455)

Questo documento è una guida tecnica dettagliata e un riferimento di implementazione per RFC 6455, includendo la descrizione del protocollo, esempi di codice e best practice. Per le traduzioni ufficiali dei capitoli RFC, consultare i rispettivi documenti dei capitoli.

RFC 6455 - Il protocollo WebSocket

Internet Engineering Task Force (IETF)
Request for Comments: 6455
Categoria: Standards Track

Autori:
I. Fette (Google Inc.)
A. Melnikov (Isode Ltd.)

Data di pubblicazione: Dicembre 2011


Stato di questo memorandum

Questo è un documento Internet Standards Track.

Questo documento è un prodotto dell'Internet Engineering Task Force (IETF) e rappresenta il consenso della comunità IETF.

Copyright (c) 2011 IETF Trust and the persons identified as the document authors. All rights reserved.


Sommario

Il protocollo WebSocket abilita la comunicazione full-duplex (Full-Duplex Communication) tra client e server su una singola connessione TCP. Il protocollo WebSocket è progettato per essere implementato nei browser Web e nei server Web, ma può essere utilizzato da qualsiasi applicazione client o server.

Il protocollo WebSocket è un protocollo indipendente basato su TCP. La sua unica relazione con HTTP è che il suo handshake (Handshake) viene interpretato dai server HTTP come una richiesta di upgrade (Upgrade Request).

Per design, il protocollo WebSocket mira a funzionare sull'infrastruttura HTTP esistente, quindi utilizza le porte HTTP 80 e 443 e supporta proxy HTTP e intermediari, anche se ciò comporta una certa complessità.


Indice


Terminologia di base WebSocket

Concetti fondamentali

Termine ingleseTraduzione italianaDescrizione
WebSocket ProtocolProtocollo WebSocketProtocollo che abilita la comunicazione full-duplex su una singola connessione TCP
Full-Duplex CommunicationComunicazione full-duplexComunicazione bidirezionale simultanea
Opening HandshakeOpening HandshakeProcesso di upgrade HTTP per stabilire una connessione WebSocket
Closing HandshakeClosing HandshakeProcesso per chiudere correttamente una connessione WebSocket
FrameFrameUnità di base della trasmissione dati WebSocket
MessageMessaggioDati applicativi completi composti da uno o più frame
ClientClientParte che avvia la connessione WebSocket (solitamente un browser)
ServerServerParte che accetta la connessione WebSocket
EndpointEndpointClient o server
ConnectionConnessioneConnessione WebSocket tra client e server

Relativi all'handshake

Termine ingleseTraduzione italianaDescrizione
Upgrade RequestRichiesta di upgradeRichiesta HTTP per passare a WebSocket
Sec-WebSocket-KeyChiave WebSocketChiave casuale inviata dal client
Sec-WebSocket-AcceptAccept WebSocketChiave di verifica restituita dal server
Sec-WebSocket-ProtocolSottoprotocollo WebSocketSottoprotocollo di livello applicazione negoziato
Sec-WebSocket-ExtensionsEstensioni WebSocketEstensioni di protocollo negoziate
Sec-WebSocket-VersionVersione WebSocketNumero di versione del protocollo (attualmente 13)
OriginOrigineProvenienza della pagina Web che avvia la connessione

Relativi alla struttura del frame

Termine ingleseTraduzione italianaDescrizione
FINFIN / Bit di fineIndica che questo è l'ultimo frame di un messaggio
RSVRSV / Bit riservatiBit flag riservati per le estensioni
OpcodeOpcode / Codice operazioneDefinisce il tipo di frame
MaskMascheraI dati da client a server devono essere mascherati
Masking-keyChiave di mascheramentoChiave a 32 bit per il mascheramento dei dati
Payload LengthLunghezza del payloadLunghezza dei dati
Payload DataDati del payloadDati effettivamente trasmessi
Extension DataDati di estensioneDati aggiuntivi negoziati dalle estensioni
Application DataDati applicativiDati effettivi del livello applicazione

Tipi di frame

Termine ingleseTraduzione italianaOpcodeDescrizione
Continuation FrameFrame di continuazione0x0Frame successivi di un messaggio
Text FrameFrame di testo0x1Dati testuali codificati in UTF-8
Binary FrameFrame binario0x2Dati binari
Close FrameFrame di chiusura0x8Frame di controllo per chiudere la connessione
Ping FrameFrame Ping0x9Richiesta di rilevamento heartbeat
Pong FrameFrame Pong0xARisposta di rilevamento heartbeat
Control FrameFrame di controllo0x8-0xFFrame per controllare la connessione
Data FrameFrame di dati0x1-0x2Frame per trasmettere dati applicativi

Relativi alla chiusura

Termine ingleseTraduzione italianaDescrizione
Close CodeCodice di chiusuraCodice numerico che indica il motivo della chiusura della connessione
Close ReasonMotivo di chiusuraDescrizione testuale della chiusura
Normal ClosureChiusura normaleCodice 1000, chiusura corretta dopo il completamento
Going AwayAllontanamentoCodice 1001, endpoint che si allontana (es. navigazione della pagina)
Protocol ErrorErrore di protocolloCodice 1002, violazione del protocollo
Unsupported DataDati non supportatiCodice 1003, tipo di dati inaccettabile ricevuto
Invalid Frame Payload DataDati del payload del frame non validiCodice 1007, dati incoerenti
Policy ViolationViolazione della policyCodice 1008, violazione della policy
Message Too BigMessaggio troppo grandeCodice 1009, messaggio troppo grande da elaborare
TLS Handshake FailureFallimento dell'handshake TLSCodice 1015, handshake TLS fallito

Spiegazione della struttura del documento

Sezione 1: Introduzione e panoramica

  • Background e requisiti di WebSocket
  • Obiettivi e principi di progettazione del protocollo
  • Relazione con HTTP/TCP
  • Modello di sicurezza e filosofia di progettazione

Sezioni 2-3: Requisiti di base

  • Requisiti di conformità e definizioni terminologiche
  • Formato URI WebSocket (ws:// e wss://)

Sezione 4: Opening Handshake (nucleo)

  • Come i client avviano l'handshake
  • Come i server rispondono all'handshake
  • Negoziazione di sottoprotocolli ed estensioni
  • Supporto di più versioni

Sezione 5: Framing dei dati (nucleo)

  • Descrizione dettagliata della struttura del frame
  • Meccanismo di mascheramento
  • Frammentazione (trasmissione di messaggi grandi in più frame)
  • Frame di controllo e frame di dati

Sezioni 6-7: Trasmissione dati e chiusura connessione

  • Regole per inviare e ricevere messaggi
  • Flusso di chiusura normale
  • Gestione delle eccezioni
  • Codici di stato di chiusura

Sezioni 8-10: Estensioni, errori e sicurezza

  • Meccanismo di estensione
  • Gestione degli errori
  • Considerazioni dettagliate sulla sicurezza

Flusso di stabilimento della connessione WebSocket

1. Il client avvia una richiesta di upgrade HTTP

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat

2. Il server risponde con un upgrade

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

3. Connessione stabilita, inizia la comunicazione bidirezionale

Client ←→ Server
↓ ↓
Text/Binary Frames
Ping/Pong Frames
Close Frame

4. Chiudere la connessione

Client → Server: Close Frame (Code: 1000)
Server → Client: Close Frame (Code: 1000)
Chiudere la connessione TCP

Calcolo della chiave di handshake

Metodo di calcolo per Sec-WebSocket-Accept

// 1. Il client genera una Sec-WebSocket-Key casuale (numero casuale di 16 byte codificato in Base64)
const clientKey = "dGhlIHNhbXBsZSBub25jZQ==";

// 2. Stringa magica (GUID fisso definito da RFC 6455)
const magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

// 3. Concatenare e calcolare l'hash SHA-1
const concatenated = clientKey + magicString;
const hash = SHA1(concatenated);

// 4. La codifica Base64 fornisce Sec-WebSocket-Accept
const serverAccept = Base64Encode(hash);
// Risultato: "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="

Questo meccanismo garantisce:

  • Il server comprende il protocollo WebSocket (non solo un normale server HTTP)
  • Previene che i proxy di caching restituiscano risposte errate
  • Fornisce una verifica di base dell'handshake

Struttura del frame WebSocket nel dettaglio

 0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+

Descrizioni dei campi

FIN (1 bit):

  • 0 = altri frame seguono
  • 1 = questo è l'ultimo (o unico) frame del messaggio

RSV1, RSV2, RSV3 (1 bit ciascuno):

  • Riservati per le estensioni
  • Devono essere 0 se nessuna estensione è negoziata

Opcode (4 bit):

  • 0x0 = Continuation (frame di continuazione)
  • 0x1 = Text (frame di testo, UTF-8)
  • 0x2 = Binary (frame binario)
  • 0x8 = Close (chiusura)
  • 0x9 = Ping
  • 0xA = Pong
  • 0x3-0x7, 0xB-0xF = Riservato

MASK (1 bit):

  • Client a server: deve essere 1
  • Server a client: deve essere 0

Payload Length (7 bit, 7+16 bit, o 7+64 bit):

  • 0-125: lunghezza effettiva
  • 126: i successivi 16 bit sono la lunghezza effettiva
  • 127: i successivi 64 bit sono la lunghezza effettiva

Masking-key (0 o 4 byte):

  • Se MASK=1, contiene una chiave di mascheramento a 32 bit

Payload Data:

  • Extension data + Application data

Spiegazione del meccanismo di mascheramento

Perché è necessario il mascheramento?

Motivi di sicurezza: Previene attacchi di tipo Cache Poisoning. Alcuni proxy intermedi potrebbero memorizzare nella cache in modo errato i frame WebSocket, il mascheramento garantisce che i dati siano imprevedibili.

Algoritmo di mascheramento

// Quando il client invia dati
function maskData(data, maskingKey) {
const masked = new Uint8Array(data.length);
for (let i = 0; i < data.length; i++) {
masked[i] = data[i] ^ maskingKey[i % 4];
}
return masked;
}

// Quando il server riceve dati (stesso algoritmo per la decodifica)
function unmaskData(maskedData, maskingKey) {
return maskData(maskedData, maskingKey); // XOR è autoinverso
}

Punti chiave:

  • Utilizza l'operazione XOR (^)
  • La lunghezza della chiave è di 4 byte
  • Uso ciclico della chiave
  • Client→Server: deve essere mascherato
  • Server→Client: vietato mascherare

Frammentazione dei messaggi (Fragmentation)

I messaggi grandi possono essere inviati in più frame:

Esempio: Invio di un grande messaggio testuale

Frame 1: FIN=0, Opcode=0x1 (Text), Payload="Hello "
Frame 2: FIN=0, Opcode=0x0 (Continuation), Payload="World"
Frame 3: FIN=1, Opcode=0x0 (Continuation), Payload="!"

Messaggio finale: "Hello World!"

Regole:

  • Primo frame: FIN=0, Opcode=tipo di dati (0x1 o 0x2)
  • Frame intermedi: FIN=0, Opcode=0x0 (Continuation)
  • Ultimo frame: FIN=1, Opcode=0x0 (Continuation)
  • I frame di controllo non possono essere frammentati e possono essere inseriti tra i frame di dati frammentati

Frame di controllo nel dettaglio

Frame Close (0x8)

Struttura:
+--------+--------+------------------+
| Code | Reason |
| (2 byte) | (testo UTF-8) |
+--------+--------+------------------+

Esempio:
Close Code: 1000 (Chiusura normale)
Close Reason: "Going away"

Codici di chiusura comunemente usati:

  • 1000: Normal Closure (Chiusura normale)
  • 1001: Going Away (Endpoint che si allontana, es. navigazione della pagina)
  • 1002: Protocol Error (Errore di protocollo)
  • 1003: Unsupported Data (Tipo di dati non supportato)
  • 1006: Abnormal Closure (Chiusura anomala, nessun frame Close inviato)
  • 1009: Message Too Big (Messaggio troppo grande)
  • 1011: Internal Server Error (Errore interno del server)

Frame Ping (0x9)

  • Può essere inviato dal client o dal server
  • Utilizzato per il rilevamento di heartbeat (mantenere la connessione attiva)
  • Può trasportare dati applicativi (massimo 125 byte)
  • Il destinatario deve rispondere con un frame Pong

Frame Pong (0xA)

  • Utilizzato per rispondere a un frame Ping
  • Deve contenere lo stesso payload del frame Ping
  • Può anche essere inviato in modo proattivo (heartbeat unidirezionale)

Flusso di chiusura

Chiusura normale (Clean Close)

1. L'iniziatore invia un frame Close
Client → Server: Close Frame (Code: 1000)

2. Il destinatario risponde con un frame Close
Server → Client: Close Frame (Code: 1000)

3. L'iniziatore chiude la connessione TCP
Client: Chiudere la connessione TCP

4. Il destinatario chiude anche la connessione TCP
Server: Chiudere la connessione TCP

Chiusura anomala

  • Connessione TCP improvvisamente interrotta (nessun frame Close)
  • Dati di frame non validi ricevuti
  • Timeout senza risposta
  • Violazione della regola di protocollo

Codice di stato: 1006 (Abnormal Closure) - Questo codice non viene inviato nei frame Close, utilizzato solo per la segnalazione


Formato URI WebSocket

ws:// (non crittografato)

ws://example.com/socket
ws://example.com:8080/chat
ws://192.168.1.1/data
  • Porta predefinita: 80
  • Equivalente al livello di sicurezza http://

wss:// (crittografato TLS)

wss://example.com/socket
wss://secure.example.com:443/chat
  • Porta predefinita: 443
  • Equivalente al livello di sicurezza https://
  • Fortemente raccomandato utilizzare wss:// in produzione

Sottoprotocollo (Subprotocol)

WebSocket è un protocollo di livello trasporto, i sottoprotocolli definiscono il formato dei messaggi del livello applicazione:

Richiesta del client:
Sec-WebSocket-Protocol: chat, superchat

Risposta del server:
Sec-WebSocket-Protocol: chat

La connessione stabilita utilizza il sottoprotocollo "chat"

Sottoprotocolli comuni:

  • STOMP: Simple Text Oriented Messaging Protocol
  • MQTT: Message Queuing Telemetry Transport
  • WAMP: Web Application Messaging Protocol
  • Protocolli applicativi personalizzati

Estensioni (Extensions)

Le estensioni possono migliorare le funzionalità WebSocket:

Richiesta del client:
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

Risposta del server:
Sec-WebSocket-Extensions: permessage-deflate

Estensioni comunemente usate:

  • permessage-deflate: Compressione dei messaggi (RFC 7692)
  • permessage-bzip2: Compressione Bzip2
  • Estensioni personalizzate

Punti chiave delle considerazioni sulla sicurezza

1. Validazione dell'origine (Origin Validation)

// Validazione dell'origine lato server
const allowedOrigins = ['https://example.com', 'https://app.example.com'];
const origin = request.headers['origin'];

if (!allowedOrigins.includes(origin)) {
// Rifiutare la connessione
response.status(403).send('Forbidden');
}

2. Crittografia TLS

  • Deve utilizzare wss:// in ambiente di produzione
  • Previene attacchi man-in-the-middle
  • Protegge la riservatezza e l'integrità dei dati

3. Autenticazione e autorizzazione

Metodo 1: Autenticazione durante l'handshake tramite cookie
GET /socket HTTP/1.1
Cookie: session=abc123

Metodo 2: Passare il token tramite il sottoprotocollo
ws://example.com/socket?token=jwt_token

Metodo 3: Informazioni di autenticazione nel primo messaggio dopo la connessione

4. Limitazione della frequenza

  • Limitare il numero di connessioni (prevenzione DoS)
  • Limitare la dimensione dei messaggi
  • Limitare la frequenza dei messaggi

5. Validazione dell'input

  • Validare la codifica UTF-8 (frame di testo)
  • Validare il formato dei messaggi
  • Prevenire attacchi di injection

Best practice per l'implementazione

Client (browser)

// 1. Creare una connessione WebSocket
const ws = new WebSocket('wss://example.com/socket', ['chat']);

// 2. Registrare i listener di eventi
ws.addEventListener('open', (event) => {
console.log('Connessione stabilita');
ws.send('Hello Server!');
});

ws.addEventListener('message', (event) => {
console.log('Messaggio ricevuto:', event.data);
});

ws.addEventListener('error', (event) => {
console.error('Errore WebSocket:', event);
});

ws.addEventListener('close', (event) => {
console.log('Connessione chiusa', event.code, event.reason);
// Implementare la logica di riconnessione
if (event.code !== 1000) {
setTimeout(() => reconnect(), 5000);
}
});

// 3. Inviare dati
ws.send('Messaggio testuale');
ws.send(new Blob(['Dati binari']));
ws.send(new ArrayBuffer(8));

// 4. Chiudere la connessione
ws.close(1000, 'Chiusura normale');

Lato server (esempio Node.js)

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws, request) => {
// Validare l'origine
const origin = request.headers.origin;
// ...logica di validazione

// Rilevamento heartbeat
ws.isAlive = true;
ws.on('pong', () => { ws.isAlive = true; });

// Ricevere messaggi
ws.on('message', (data) => {
console.log('Ricevuto:', data);

// Trasmettere a tutti i client
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});

// Gestione degli errori
ws.on('error', (error) => {
console.error('Errore:', error);
});

// Chiusura
ws.on('close', (code, reason) => {
console.log('Connessione chiusa:', code, reason);
});
});

// Rilevamento heartbeat
const interval = setInterval(() => {
wss.clients.forEach((ws) => {
if (ws.isAlive === false) {
return ws.terminate();
}
ws.isAlive = false;
ws.ping();
});
}, 30000);

Scenari applicativi comuni

1. Applicazioni di chat in tempo reale

  • Messaggistica istantanea
  • Visualizzazione dello stato online
  • Indicatore di digitazione

2. Strumenti di collaborazione in tempo reale

  • Editing collaborativo di documenti (come Google Docs)
  • Condivisione di lavagna
  • Editor di codice collaborativo

3. Flussi di dati in tempo reale

  • Quotazioni azionarie
  • Punteggi sportivi
  • Dati IoT

4. Giochi online

  • Giochi multiplayer online
  • Sincronizzazione dello stato di gioco in tempo reale
  • Interazione a bassa latenza

5. Notifiche push

  • Push del browser
  • Avvisi in tempo reale
  • Monitoraggio del sistema

Confronto con altre tecnologie

WebSocket vs HTTP Long Polling

CaratteristicaWebSocketLong Polling
ConnessioneConnessione persistenteRichieste HTTP ripetute
LatenzaMolto bassaPiù alta (overhead HTTP)
BidirezionaleVeramente bidirezionalePseudo-bidirezionale
RisorseBasso (una connessione)Alto (più connessioni)
ComplessitàMediaPiù semplice

WebSocket vs Server-Sent Events (SSE)

CaratteristicaWebSocketSSE
Comunicazione bidirezionale✅ Bidirezionale❌ Solo Server→Client
Formato datiBinario o testoTesto (UTF-8)
ProtocolloProtocollo indipendenteBasato su HTTP
Supporto browserAmpiamente supportatoAmpiamente supportato (tranne IE)
RiconnessioneImplementazione manualeRiconnessione automatica

Strumenti di debug

Strumenti per sviluppatori del browser

  • Chrome DevTools → Network → WS
  • Visualizzare l'invio e la ricezione dei frame
  • Visualizzare lo stato della connessione

Strumenti da riga di comando

# wscat (Node.js)
npm install -g wscat
wscat -c ws://echo.websocket.org

# websocat (Rust)
websocat ws://echo.websocket.org

Strumenti di test online

  • websocket.org Echo Test
  • Hoppscotch (precedentemente Postwoman)
  • Firecamp

Risorse di riferimento

Standard RFC correlati

  • RFC 6455: Protocollo WebSocket (questo documento)
  • RFC 7692: Estensione di compressione WebSocket
  • RFC 8441: Bootstrapping WebSocket su HTTP/2
  • RFC 8307: URI ben noto per WebSocket

Librerie di implementazione consigliate

JavaScript (browser):

  • API WebSocket nativa

Node.js:

  • ws (leggero)
  • Socket.IO (ricco di funzionalità, fallback automatico)
  • uWebSockets.js (alte prestazioni)

Python:

  • websockets (asyncio)
  • ws4py
  • Tornado

Java:

  • Java WebSocket API (JSR 356)
  • Netty
  • Spring WebSocket

Go:

  • gorilla/websocket
  • nhooyr/websocket

Prossimi passi di apprendimento

  1. Fondamenti: Comprendere l'handshake WebSocket e la struttura del frame
  2. Pratica: Implementare un'applicazione di chat semplice
  3. Avanzato: Imparare sottoprotocolli ed estensioni
  4. Ottimizzazione: Implementare heartbeat, riconnessione, gestione degli errori
  5. Sicurezza: Approfondire le considerazioni sulla sicurezza e le best practice

Il protocollo WebSocket è la pietra angolare della comunicazione in tempo reale sul Web:

  • Precisione: Implementazione precisa della comunicazione bidirezionale
  • Trasmissione: Specifica del protocollo chiara
  • Eleganza: Design e implementazione eleganti

Valori fondamentali:

  1. 🔄 Vera comunicazione full-duplex
  2. ⚡ Trasmissione dati in tempo reale a bassa latenza
  3. 📦 Protocollo di frame leggero
  4. 🔐 Meccanismi di sicurezza integrati

Migliori scenari di utilizzo:

  • Chat in tempo reale
  • Editing collaborativo
  • Giochi in tempo reale
  • Flussi di dati in tempo reale
  • Notifiche push

RFC correlati:

  • RFC 6455: Protocollo WebSocket (questo documento)
  • RFC 7692: Estensione di compressione WebSocket
  • RFC 8441: Bootstrapping WebSocket su HTTP/2

Torna all'elenco RFC