Aller au contenu principal

5. Data Framing (Tramage des données)

Le protocole WebSocket utilise des trames pour transmettre des données. Ce chapitre définit le format et les règles de traitement des trames WebSocket.

5.1 Overview (Vue d'ensemble)

Une fois qu'une connexion WebSocket est établie, le client et le serveur peuvent transmettre des données de manière bidirectionnelle. Les données sont transmises sous forme d'une série de trames.

Concepts clés :

  • Trame (Frame) : L'unité de transmission de base, contenant un en-tête et une charge utile
  • Message : Données au niveau de l'application qui peuvent être composées d'une ou plusieurs trames
  • Fragmentation : Les messages volumineux peuvent être envoyés en plusieurs trames

5.2 Base Framing Protocol (Protocole de tramage de base)

Structure de trame

 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 ... |
+---------------------------------------------------------------+

Description des champs

FIN (1 bit)

  • 0 : Ce n'est pas la dernière trame du message (d'autres trames suivent)
  • 1 : C'est la dernière trame du message (ou l'unique trame)
Exemple de fragmentation de message :
Trame 1: FIN=0, Opcode=0x1 (Text), Data="Hello "
Trame 2: FIN=0, Opcode=0x0 (Continuation), Data="World"
Trame 3: FIN=1, Opcode=0x0 (Continuation), Data="!"

Message complet : "Hello World!"

RSV1, RSV2, RSV3 (1 bit chacun)

  • Réservés pour les extensions
  • DOIT être 0 si aucune extension n'est négociée
  • DOIT fermer la connexion si une valeur non nulle est reçue sans extension définie

Opcode (4 bits)

Définit le type de trame :

OpcodeTypeDescription
0x0ContinuationTrame de continuation (trames suivantes d'un message fragmenté)
0x1TextTrame de texte (encodé UTF-8)
0x2BinaryTrame binaire
0x3-0x7-Réservé (trames de données)
0x8CloseTrame de fermeture
0x9PingTrame Ping
0xAPongTrame Pong
0xB-0xF-Réservé (trames de contrôle)

MASK (1 bit)

  • Client vers serveur : DOIT être 1
  • Serveur vers client : DOIT être 0

Si MASK=1, Payload Data doit être masqué en utilisant Masking-key.

Payload Length (7 bits, 7+16 bits, ou 7+64 bits)

Encodage de la longueur de la charge utile :

  • 0-125 : C'est la longueur réelle
  • 126 : Les 16 bits suivants (2 octets) sont la longueur réelle (ordre des octets réseau)
  • 127 : Les 64 bits suivants (8 octets) sont la longueur réelle (ordre des octets réseau)
Exemple :
Longueur de charge utile = 100 octets
→ Payload len = 100 (encodage direct)

Longueur de charge utile = 1000 octets
→ Payload len = 126
→ Extended payload length = 1000 (16 bits)

Longueur de charge utile = 100000 octets
→ Payload len = 127
→ Extended payload length = 100000 (64 bits)

Masking-key (0 ou 4 octets)

Si MASK=1, contient 32 bits (4 octets) de clé de masquage.

Payload Data (x+y octets)

Données de charge utile = Extension Data + Application Data

  • Extension Data : Longueur x, déterminée par la négociation d'extension, par défaut 0
  • Application Data : Longueur y, données d'application réelles

5.3 Client-to-Server Masking (Masquage client vers serveur)

Pourquoi le masquage est-il requis ?

Raison de sécurité : Prévenir les attaques par empoisonnement du cache. Certains proxys intermédiaires pourraient mettre en cache de manière incorrecte les trames WebSocket ; le masquage garantit l'imprévisibilité des données.

Algorithme de masquage

Les clients DOIVENT utiliser l'algorithme suivant pour masquer toutes les trames envoyées au serveur :

1. Générer une clé de masquage aléatoire de 32 bits
2. Placer la clé de masquage dans le champ Masking-key de l'en-tête de trame
3. Appliquer le masque à chaque octet de Payload Data :

transformed-octet-i = original-octet-i XOR masking-key[i MOD 4]

Implémentation de l'algorithme (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;
}

// Exemple
const data = Buffer.from('Hello');
const maskingKey = Buffer.from([0x37, 0xfa, 0x21, 0x3d]);
const masked = maskData(data, maskingKey);

// Décodage (utilisant le même algorithme)
const unmasked = maskData(masked, maskingKey); // 'Hello'

Points clés :

  • XOR est auto-inverse : (A XOR B) XOR B = A
  • Le serveur utilise le même algorithme pour décoder
  • Doit utiliser une nouvelle clé aléatoire pour chaque trame envoyée

5.4 Fragmentation

Les messages volumineux peuvent être envoyés en plusieurs trames.

Règles de fragmentation

  1. Première trame : FIN=0, Opcode=type de données (0x1 ou 0x2)
  2. Trames intermédiaires : FIN=0, Opcode=0x0 (Continuation)
  3. Dernière trame : FIN=1, Opcode=0x0 (Continuation)

Exemple de fragmentation

Envoi du message "Hello World!" en trois trames :

Trame 1:
FIN = 0
Opcode = 0x1 (Text)
Payload = "Hello "

Trame 2:
FIN = 0
Opcode = 0x0 (Continuation)
Payload = "World"

Trame 3:
FIN = 1
Opcode = 0x0 (Continuation)
Payload = "!"

Contraintes de fragmentation

  • Les trames de contrôle (Close, Ping, Pong) NE DOIVENT PAS être fragmentées
  • Les trames de contrôle peuvent être insérées entre les trames de données fragmentées
  • Les fragments doivent être envoyés et reçus dans l'ordre

5.5 Control Frames (Trames de contrôle)

Les trames de contrôle sont utilisées pour communiquer l'état de la connexion. Plage d'Opcode : 0x8-0xF.

5.5.1 Close (Trame de fermeture)

  • Opcode : 0x8
  • Peut inclure un code de fermeture et une raison
  • Voir le chapitre 7 pour plus de détails

5.5.2 Ping (Trame Ping)

  • Opcode : 0x9
  • Objectif : Détection de pulsation, vérifier si la connexion est active
  • Peut transporter des données d'application (max 125 octets)
  • Le destinataire DOIT répondre avec une trame Pong
Client → Serveur: Ping (Opcode=0x9)
Serveur → Client: Pong (Opcode=0xA, même Payload)

5.5.3 Pong (Trame Pong)

  • Opcode : 0xA
  • Objectif : Répondre à la trame Ping
  • DOIT contenir le même Payload que la trame Ping
  • Peut également être envoyé de manière proactive (pulsation non sollicitée)

Règles des trames de contrôle

  1. Longueur maximale de Payload : 125 octets
  2. NE DOIVENT PAS être fragmentées
  3. Peuvent être insérées entre les trames de données fragmentées

5.6 Data Frames (Trames de données)

Les trames de données transmettent des données d'application ou d'extension. Plage d'Opcode : 0x0-0x2, 0x3-0x7 réservés.

Text Frame (Trame de texte)

  • Opcode : 0x1
  • Le Payload DOIT être du texte encodé UTF-8 valide
  • DOIT fermer la connexion si UTF-8 invalide reçu

Binary Frame (Trame binaire)

  • Opcode : 0x2
  • Le Payload peut être des données binaires arbitraires
  • La couche application est responsable de l'interprétation

5.7 Examples (Exemples)

Trame de texte unique non masquée

0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f
│ │ └─────────┬────────────┘
│ │ └─ "Hello" (5 octets)
│ └─ Payload len = 5
└─ FIN=1, Opcode=0x1 (Text)

Trame de texte unique masquée

0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58
│ │ └─────┬────────┘ └──────┬───────────┘
│ │ │ └─ "Hello" masqué
│ │ └─ Clé de masquage
│ └─ MASK=1, Payload len = 5
└─ FIN=1, Opcode=0x1 (Text)

Message fragmenté

Trame 1: 0x01 0x03 0x48 0x65 0x6c   // FIN=0, Text, "Hel"
Trame 2: 0x80 0x02 0x6c 0x6f // FIN=1, Continuation, "lo"

Message complet : "Hello"

Trame Ping

0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f
│ │ └─────────┬────────────┘
│ │ └─ "Hello" (données optionnelles)
│ └─ Payload len = 5
└─ FIN=1, Opcode=0x9 (Ping)

5.8 Extensibility (Extensibilité)

Le protocole peut être étendu par :

  1. Opcode : 0x3-0x7 et 0xB-0xF réservés pour une utilisation future
  2. Bits RSV : Réservés pour une utilisation par les extensions
  3. Extension Data : Les extensions peuvent ajouter des données avant Payload

Les extensions doivent être négociées via la poignée de main (Sec-WebSocket-Extensions).

Liens de référence