5. Data Framing (Daten-Framing)
Das WebSocket-Protokoll verwendet Frames zur Datenübertragung. Dieses Kapitel definiert das Format und die Verarbeitungsregeln für WebSocket-Frames.
5.1 Overview (Überblick)
Sobald eine WebSocket-Verbindung hergestellt ist, können Client und Server bidirektional Daten übertragen. Daten werden in Form einer Reihe von Frames übertragen.
Schlüsselkonzepte:
- Frame: Die grundlegende Übertragungseinheit, bestehend aus einem Header und einer Payload
- Message (Nachricht): Daten auf Anwendungsebene, die aus einem oder mehreren Frames bestehen können
- Fragmentation (Fragmentierung): Große Nachrichten können in mehreren Frames gesendet werden
5.2 Base Framing Protocol (Basis-Framing-Protokoll)
Frame-Struktur
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 ... |
+---------------------------------------------------------------+
Feldbeschreibungen
FIN (1 Bit)
0: Dies ist nicht der letzte Frame der Nachricht (weitere Frames folgen)1: Dies ist der letzte Frame der Nachricht (oder der einzige Frame)
Beispiel für Nachrichtenfragmentierung:
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="!"
Vollständige Nachricht: "Hello World!"
RSV1, RSV2, RSV3 (jeweils 1 Bit)
- Für Erweiterungen reserviert
- MUSS 0 sein, wenn keine Erweiterung ausgehandelt wurde
- MUSS Verbindung schließen, wenn ein Nicht-Null-Wert ohne definierte Erweiterung empfangen wird
Opcode (4 Bits)
Definiert den Frame-Typ:
| Opcode | Typ | Beschreibung |
|---|---|---|
0x0 | Continuation | Fortsetzungs-Frame (nachfolgende Frames einer fragmentierten Nachricht) |
0x1 | Text | Text-Frame (UTF-8-kodiert) |
0x2 | Binary | Binär-Frame |
0x3-0x7 | - | Reserviert (Daten-Frames) |
0x8 | Close | Schließ-Frame |
0x9 | Ping | Ping-Frame |
0xA | Pong | Pong-Frame |
0xB-0xF | - | Reserviert (Kontroll-Frames) |
MASK (1 Bit)
- Client zu Server: MUSS 1 sein
- Server zu Client: MUSS 0 sein
Wenn MASK=1, muss Payload Data mit Masking-key maskiert werden.
Payload Length (7 Bits, 7+16 Bits oder 7+64 Bits)
Kodierung der Payload-Länge:
- 0-125: Dies ist die tatsächliche Länge
- 126: Die folgenden 16 Bits (2 Bytes) sind die tatsächliche Länge (Netzwerk-Byte-Reihenfolge)
- 127: Die folgenden 64 Bits (8 Bytes) sind die tatsächliche Länge (Netzwerk-Byte-Reihenfolge)
Beispiel:
Payload-Länge = 100 Bytes
→ Payload len = 100 (direkte Kodierung)
Payload-Länge = 1000 Bytes
→ Payload len = 126
→ Extended payload length = 1000 (16-Bit)
Payload-Länge = 100000 Bytes
→ Payload len = 127
→ Extended payload length = 100000 (64-Bit)
Masking-key (0 oder 4 Bytes)
Wenn MASK=1, enthält es 32 Bits (4 Bytes) Maskierungsschlüssel.
Payload Data (x+y Bytes)
Payload-Daten = Extension Data + Application Data
- Extension Data: Länge x, durch Erweiterungsverhandlung bestimmt, Standard 0
- Application Data: Länge y, tatsächliche Anwendungsdaten
5.3 Client-to-Server Masking (Client-zu-Server-Maskierung)
Warum ist Maskierung erforderlich?
Sicherheitsgrund: Verhinderung von Cache-Poisoning-Angriffen. Einige zwischengeschaltete Proxys könnten WebSocket-Frames fälschlicherweise cachen; Maskierung gewährleistet Datenunvorhersagbarkeit.
Maskierungsalgorithmus
Clients MÜSSEN folgenden Algorithmus verwenden, um alle an den Server gesendeten Frames zu maskieren:
1. Erzeuge einen 32-Bit-Zufalls-Maskierungsschlüssel
2. Platziere den Maskierungsschlüssel im Masking-key-Feld des Frame-Headers
3. Wende die Maske auf jedes Byte der Payload Data an:
transformed-octet-i = original-octet-i XOR masking-key[i MOD 4]
Algorithmus-Implementierung (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;
}
// Beispiel
const data = Buffer.from('Hello');
const maskingKey = Buffer.from([0x37, 0xfa, 0x21, 0x3d]);
const masked = maskData(data, maskingKey);
// Dekodierung (mit demselben Algorithmus)
const unmasked = maskData(masked, maskingKey); // 'Hello'
Wichtige Punkte:
- XOR ist selbst-invers:
(A XOR B) XOR B = A - Server verwendet denselben Algorithmus zur Dekodierung
- Muss für jeden gesendeten Frame einen neuen Zufallsschlüssel verwenden
5.4 Fragmentation (Fragmentierung)
Große Nachrichten können in mehreren Frames gesendet werden.
Fragmentierungsregeln
- Erster Frame: FIN=0, Opcode=Datentyp (0x1 oder 0x2)
- Mittlere Frames: FIN=0, Opcode=0x0 (Continuation)
- Letzter Frame: FIN=1, Opcode=0x0 (Continuation)
Fragmentierungsbeispiel
Senden der Nachricht "Hello World!" in drei Frames:
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 = "!"
Fragmentierungsbeschränkungen
- Kontroll-Frames (Close, Ping, Pong) DÜRFEN NICHT fragmentiert werden
- Kontroll-Frames können zwischen fragmentierten Daten-Frames eingefügt werden
- Fragmente müssen in Reihenfolge gesendet und empfangen werden
5.5 Control Frames (Kontroll-Frames)
Kontroll-Frames werden verwendet, um den Verbindungsstatus zu kommunizieren. Opcode-Bereich: 0x8-0xF.
5.5.1 Close (Schließ-Frame)
- Opcode:
0x8 - Kann Schließcode und Grund enthalten
- Siehe Kapitel 7 für Details
5.5.2 Ping (Ping-Frame)
- Opcode:
0x9 - Zweck: Heartbeat-Erkennung, Prüfung ob Verbindung aktiv ist
- Kann Anwendungsdaten tragen (max. 125 Bytes)
- Empfänger MUSS mit Pong-Frame antworten
Client → Server: Ping (Opcode=0x9)
Server → Client: Pong (Opcode=0xA, gleiche Payload)
5.5.3 Pong (Pong-Frame)
- Opcode:
0xA - Zweck: Antwort auf Ping-Frame
- MUSS dieselbe Payload wie Ping-Frame enthalten
- Kann auch proaktiv gesendet werden (unaufgeforderter Heartbeat)
Kontroll-Frame-Regeln
- Maximale Payload-Länge: 125 Bytes
- DÜRFEN NICHT fragmentiert werden
- Können zwischen fragmentierten Daten-Frames eingefügt werden
5.6 Data Frames (Daten-Frames)
Daten-Frames übertragen Anwendungs- oder Erweiterungsdaten. Opcode-Bereich: 0x0-0x2, 0x3-0x7 reserviert.
Text Frame (Text-Frame)
- Opcode:
0x1 - Payload MUSS gültiger UTF-8-kodierter Text sein
- MUSS Verbindung schließen, wenn ungültiges UTF-8 empfangen wird
Binary Frame (Binär-Frame)
- Opcode:
0x2 - Payload kann beliebige Binärdaten sein
- Anwendungsschicht ist für Interpretation verantwortlich
5.7 Examples (Beispiele)
Einzelner unmaskierter Text-Frame
0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f
│ │ └─────────┬────────────┘
│ │ └─ "Hello" (5 Bytes)
│ └─ Payload len = 5
└─ FIN=1, Opcode=0x1 (Text)
Einzelner maskierter Text-Frame
0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58
│ │ └─────┬────────┘ └──────┬───────────┘
│ │ │ └─ Maskiertes "Hello"
│ │ └─ Maskierungsschlüssel
│ └─ MASK=1, Payload len = 5
└─ FIN=1, Opcode=0x1 (Text)
Fragmentierte Nachricht
Frame 1: 0x01 0x03 0x48 0x65 0x6c // FIN=0, Text, "Hel"
Frame 2: 0x80 0x02 0x6c 0x6f // FIN=1, Continuation, "lo"
Vollständige Nachricht: "Hello"
Ping-Frame
0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f
│ │ └─────────┬────────────┘
│ │ └─ "Hello" (optionale Daten)
│ └─ Payload len = 5
└─ FIN=1, Opcode=0x9 (Ping)
5.8 Extensibility (Erweiterbarkeit)
Das Protokoll kann erweitert werden durch:
- Opcode: 0x3-0x7 und 0xB-0xF für zukünftige Verwendung reserviert
- RSV-Bits: Für Erweiterungsverwendung reserviert
- Extension Data: Erweiterungen können Daten vor Payload hinzufügen
Erweiterungen müssen über Handshake ausgehandelt werden (Sec-WebSocket-Extensions).
Referenzlinks
- Vorheriges Kapitel: 4. Opening Handshake (Eröffnungs-Handshake)
- Nächstes Kapitel: 6. Sending and Receiving Data (Senden und Empfangen von Daten)
- Detaillierte Erklärung: WebSocket-Frame-Strukturdetails