5. Data Framing (Incorniciamento dei dati)
Il protocollo WebSocket utilizza frame per trasmettere dati. Questo capitolo definisce il formato e le regole di elaborazione per i frame WebSocket.
5.1 Overview (Panoramica)
Una volta stabilita una connessione WebSocket, il client e il server possono trasmettere dati bidirezionalmente. I dati vengono trasmessi sotto forma di una serie di frame.
Concetti chiave:
- Frame: L'unità di trasmissione di base, contenente un'intestazione e un payload
- Message (Messaggio): Dati a livello di applicazione che possono consistere in uno o più frame
- Fragmentation (Frammentazione): I messaggi di grandi dimensioni possono essere inviati in più frame
5.2 Base Framing Protocol (Protocollo di incorniciamento di base)
Struttura del frame
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 ... |
+---------------------------------------------------------------+
Descrizione dei campi
FIN (1 bit)
0: Questo non è l'ultimo frame del messaggio (seguono altri frame)1: Questo è l'ultimo frame del messaggio (o l'unico frame)
Esempio di frammentazione del messaggio:
Frame 1: FIN=0, Opcode=0x1 (Text), Data="Hello "
Frame 2: FIN=0, Opcode=0x0 (Continuation), Data="World"
Frame 3: FIN=1, Opcode=0x0 (Continuation), Data="!"
Messaggio completo: "Hello World!"
RSV1, RSV2, RSV3 (1 bit ciascuno)
- Riservati per le estensioni
- DEVE essere 0 se non è stata negoziata alcuna estensione
- DEVE chiudere la connessione se viene ricevuto un valore diverso da zero senza estensione definita
Opcode (4 bit)
Definisce il tipo di frame:
| Opcode | Tipo | Descrizione |
|---|---|---|
0x0 | Continuation | Frame di continuazione (frame successivi di un messaggio frammentato) |
0x1 | Text | Frame di testo (codificato UTF-8) |
0x2 | Binary | Frame binario |
0x3-0x7 | - | Riservato (frame di dati) |
0x8 | Close | Frame di chiusura |
0x9 | Ping | Frame Ping |
0xA | Pong | Frame Pong |
0xB-0xF | - | Riservato (frame di controllo) |
MASK (1 bit)
- Client verso server: DEVE essere 1
- Server verso client: DEVE essere 0
Se MASK=1, Payload Data deve essere mascherato utilizzando Masking-key.
Payload Length (7 bit, 7+16 bit o 7+64 bit)
Codifica della lunghezza del payload:
- 0-125: Questa è la lunghezza effettiva
- 126: I successivi 16 bit (2 byte) sono la lunghezza effettiva (ordine dei byte di rete)
- 127: I successivi 64 bit (8 byte) sono la lunghezza effettiva (ordine dei byte di rete)
Esempio:
Lunghezza del payload = 100 byte
→ Payload len = 100 (codifica diretta)
Lunghezza del payload = 1000 byte
→ Payload len = 126
→ Extended payload length = 1000 (16 bit)
Lunghezza del payload = 100000 byte
→ Payload len = 127
→ Extended payload length = 100000 (64 bit)
Masking-key (0 o 4 byte)
Se MASK=1, contiene 32 bit (4 byte) di chiave di mascheramento.
Payload Data (x+y byte)
Dati del payload = Extension Data + Application Data
- Extension Data: Lunghezza x, determinata dalla negoziazione dell'estensione, predefinito 0
- Application Data: Lunghezza y, dati effettivi dell'applicazione
5.3 Client-to-Server Masking (Mascheramento client-server)
Perché il mascheramento è richiesto?
Motivo di sicurezza: Prevenire attacchi di avvelenamento della cache. Alcuni proxy intermedi potrebbero memorizzare nella cache in modo errato i frame WebSocket; il mascheramento garantisce l'imprevedibilità dei dati.
Algoritmo di mascheramento
I client DEVONO utilizzare il seguente algoritmo per mascherare tutti i frame inviati al server:
1. Genera una chiave di mascheramento casuale a 32 bit
2. Posiziona la chiave di mascheramento nel campo Masking-key dell'intestazione del frame
3. Applica la maschera a ogni byte di Payload Data:
transformed-octet-i = original-octet-i XOR masking-key[i MOD 4]
Implementazione dell'algoritmo (JavaScript):
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;
}
// Esempio
const data = Buffer.from('Hello');
const maskingKey = Buffer.from([0x37, 0xfa, 0x21, 0x3d]);
const masked = maskData(data, maskingKey);
// Decodifica (utilizzando lo stesso algoritmo)
const unmasked = maskData(masked, maskingKey); // 'Hello'
Punti chiave:
- XOR è auto-inverso:
(A XOR B) XOR B = A - Il server utilizza lo stesso algoritmo per decodificare
- Deve utilizzare una nuova chiave casuale per ogni frame inviato
5.4 Fragmentation (Frammentazione)
I messaggi di grandi dimensioni possono essere inviati in più frame.
Regole di frammentazione
- 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)
Esempio di frammentazione
Invio del messaggio "Hello World!" in tre frame:
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 = "!"
Vincoli di frammentazione
- I frame di controllo (Close, Ping, Pong) NON DEVONO essere frammentati
- I frame di controllo possono essere inseriti tra i frame di dati frammentati
- I frammenti devono essere inviati e ricevuti in ordine
5.5 Control Frames (Frame di controllo)
I frame di controllo vengono utilizzati per comunicare lo stato della connessione. Intervallo Opcode: 0x8-0xF.
5.5.1 Close (Frame di chiusura)
- Opcode:
0x8 - Può includere codice di chiusura e motivo
- Vedere il capitolo 7 per i dettagli
5.5.2 Ping (Frame Ping)
- Opcode:
0x9 - Scopo: Rilevamento del battito cardiaco, verificare se la connessione è attiva
- Può trasportare dati dell'applicazione (max 125 byte)
- Il destinatario DEVE rispondere con un frame Pong
Client → Server: Ping (Opcode=0x9)
Server → Client: Pong (Opcode=0xA, stesso Payload)
5.5.3 Pong (Frame Pong)
- Opcode:
0xA - Scopo: Rispondere al frame Ping
- DEVE contenere lo stesso Payload del frame Ping
- Può anche essere inviato in modo proattivo (battito cardiaco non sollecitato)
Regole dei frame di controllo
- Lunghezza massima del Payload: 125 byte
- NON DEVONO essere frammentati
- Possono essere inseriti tra i frame di dati frammentati
5.6 Data Frames (Frame di dati)
I frame di dati trasmettono dati di applicazione o di estensione. Intervallo Opcode: 0x0-0x2, 0x3-0x7 riservati.
Text Frame (Frame di testo)
- Opcode:
0x1 - Il Payload DEVE essere testo codificato UTF-8 valido
- DEVE chiudere la connessione se viene ricevuto UTF-8 non valido
Binary Frame (Frame binario)
- Opcode:
0x2 - Il Payload può essere dati binari arbitrari
- Il livello applicazione è responsabile dell'interpretazione
5.7 Examples (Esempi)
Frame di testo singolo non mascherato
0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f
│ │ └─────────┬────────────┘
│ │ └─ "Hello" (5 byte)
│ └─ Payload len = 5
└─ FIN=1, Opcode=0x1 (Text)
Frame di testo singolo mascherato
0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58
│ │ └─────┬────────┘ └──────┬───────────┘
│ │ │ └─ "Hello" mascherato
│ │ └─ Chiave di mascheramento
│ └─ MASK=1, Payload len = 5
└─ FIN=1, Opcode=0x1 (Text)
Messaggio frammentato
Frame 1: 0x01 0x03 0x48 0x65 0x6c // FIN=0, Text, "Hel"
Frame 2: 0x80 0x02 0x6c 0x6f // FIN=1, Continuation, "lo"
Messaggio completo: "Hello"
Frame Ping
0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f
│ │ └─────────┬────────────┘
│ │ └─ "Hello" (dati opzionali)
│ └─ Payload len = 5
└─ FIN=1, Opcode=0x9 (Ping)
5.8 Extensibility (Estensibilità)
Il protocollo può essere esteso tramite:
- Opcode: 0x3-0x7 e 0xB-0xF riservati per uso futuro
- Bit RSV: Riservati per uso delle estensioni
- Extension Data: Le estensioni possono aggiungere dati prima del Payload
Le estensioni devono essere negoziate tramite handshake (Sec-WebSocket-Extensions).
Link di riferimento
- Capitolo precedente: 4. Opening Handshake (Handshake di apertura)
- Capitolo successivo: 6. Sending and Receiving Data (Invio e ricezione di dati)
- Spiegazione dettagliata: Dettagli della struttura del frame WebSocket