Guide d'implémentation du protocole WebSocket (RFC 6455)
Ce document est un guide technique détaillé et une référence d'implémentation pour RFC 6455, incluant la description du protocole, des exemples de code et les meilleures pratiques. Pour les traductions officielles des chapitres RFC, consultez les documents de chapitre respectifs.
RFC 6455 - Le protocole WebSocket
Internet Engineering Task Force (IETF)
Request for Comments: 6455
Catégorie: Standards Track
Auteurs:
I. Fette (Google Inc.)
A. Melnikov (Isode Ltd.)
Date de publication: Décembre 2011
Statut de ce mémorandum
Ceci est un document Internet Standards Track.
Ce document est un produit de l'Internet Engineering Task Force (IETF) et représente le consensus de la communauté IETF.
Notice de copyright
Copyright (c) 2011 IETF Trust and the persons identified as the document authors. All rights reserved.
Résumé
Le protocole WebSocket permet une communication full-duplex (Full-Duplex Communication) entre client et serveur sur une connexion TCP unique. Le protocole WebSocket est conçu pour être implémenté dans les navigateurs Web et les serveurs Web, mais il peut être utilisé par n'importe quelle application cliente ou serveur.
Le protocole WebSocket est un protocole indépendant basé sur TCP. Sa seule relation avec HTTP est que son handshake (Handshake) est interprété par les serveurs HTTP comme une requête de mise à niveau (Upgrade Request).
Par conception, le protocole WebSocket vise à fonctionner sur l'infrastructure HTTP existante, il utilise donc les ports HTTP 80 et 443 et prend en charge les proxies HTTP et les intermédiaires, même si cela implique une certaine complexité.
Table des matières
- 1. Introduction
- 2. Exigences de conformité (Conformance Requirements)
- 3. URI WebSocket
- 4. Opening Handshake
- 5. Tramage de données (Data Framing)
- 6. Envoi et réception de données (Sending and Receiving Data)
- 7. Fermeture de connexion (Closing the Connection)
- 7.1 Définitions
- 7.1.1 Fermer la connexion WebSocket
- 7.1.2 Démarrer le handshake de fermeture WebSocket
- 7.1.3 Le handshake de fermeture WebSocket a été démarré
- 7.1.4 La connexion WebSocket est fermée
- 7.1.5 Code de fermeture de connexion WebSocket
- 7.1.6 Raison de fermeture de connexion WebSocket
- 7.1.7 Échec de la connexion WebSocket
- 7.2 Fermeture anormale de la connexion WebSocket
- 7.3 Fermeture normale de la connexion
- 7.4 Codes de statut
- 7.1 Définitions
- 8. Gestion des erreurs (Error Handling)
- 9. Extensions
- 10. Considérations de sécurité (Security Considerations)
- 11. Considérations IANA (IANA Considerations)
- 12. Utilisation du protocole WebSocket à partir d'autres spécifications
- 13. Remerciements
- 14. Références
Terminologie de base WebSocket
Concepts fondamentaux
| Terme anglais | Traduction française | Description |
|---|---|---|
| WebSocket Protocol | Protocole WebSocket | Protocole permettant une communication full-duplex sur une connexion TCP unique |
| Full-Duplex Communication | Communication full-duplex | Communication bidirectionnelle simultanée |
| Opening Handshake | Opening Handshake | Processus de mise à niveau HTTP pour établir une connexion WebSocket |
| Closing Handshake | Closing Handshake | Processus pour fermer proprement une connexion WebSocket |
| Frame | Frame / Trame | Unité de base de transmission de données WebSocket |
| Message | Message | Données d'application complètes composées d'une ou plusieurs frames |
| Client | Client | Partie qui initie la connexion WebSocket (généralement un navigateur) |
| Server | Serveur | Partie qui accepte la connexion WebSocket |
| Endpoint | Point de terminaison | Client ou serveur |
| Connection | Connexion | Connexion WebSocket entre client et serveur |
Relatif au handshake
| Terme anglais | Traduction française | Description |
|---|---|---|
| Upgrade Request | Requête de mise à niveau | Requête HTTP pour passer à WebSocket |
| Sec-WebSocket-Key | Clé WebSocket | Clé aléatoire envoyée par le client |
| Sec-WebSocket-Accept | Accept WebSocket | Clé de vérification retournée par le serveur |
| Sec-WebSocket-Protocol | Sous-protocole WebSocket | Sous-protocole de couche application négocié |
| Sec-WebSocket-Extensions | Extensions WebSocket | Extensions de protocole négociées |
| Sec-WebSocket-Version | Version WebSocket | Numéro de version du protocole (actuellement 13) |
| Origin | Origine | Provenance de la page Web qui initie la connexion |
Relatif à la structure de frame
| Terme anglais | Traduction française | Description |
|---|---|---|
| FIN | FIN / Bit de fin | Indique qu'il s'agit de la dernière frame d'un message |
| RSV | RSV / Bits réservés | Bits de drapeau réservés pour les extensions |
| Opcode | Opcode / Code d'opération | Définit le type de frame |
| Mask | Masque | Les données client vers serveur doivent être masquées |
| Masking-key | Clé de masquage | Clé 32 bits pour le masquage des données |
| Payload Length | Longueur de charge utile | Longueur des données |
| Payload Data | Données de charge utile | Données réellement transmises |
| Extension Data | Données d'extension | Données supplémentaires négociées par les extensions |
| Application Data | Données d'application | Données réelles de la couche application |
Types de frames
| Terme anglais | Traduction française | Opcode | Description |
|---|---|---|---|
| Continuation Frame | Frame de continuation | 0x0 | Frames suivantes d'un message |
| Text Frame | Frame de texte | 0x1 | Données texte encodées en UTF-8 |
| Binary Frame | Frame binaire | 0x2 | Données binaires |
| Close Frame | Frame de fermeture | 0x8 | Frame de contrôle pour fermer la connexion |
| Ping Frame | Frame Ping | 0x9 | Requête de détection de heartbeat |
| Pong Frame | Frame Pong | 0xA | Réponse de détection de heartbeat |
| Control Frame | Frame de contrôle | 0x8-0xF | Frames pour contrôler la connexion |
| Data Frame | Frame de données | 0x1-0x2 | Frames pour transmettre les données d'application |
Relatif à la fermeture
| Terme anglais | Traduction française | Description |
|---|---|---|
| Close Code | Code de fermeture | Code numérique indiquant la raison de fermeture de connexion |
| Close Reason | Raison de fermeture | Description textuelle de la fermeture |
| Normal Closure | Fermeture normale | Code 1000, fermeture propre après accomplissement |
| Going Away | Départ | Code 1001, point de terminaison qui part (ex. navigation de page) |
| Protocol Error | Erreur de protocole | Code 1002, violation de protocole |
| Unsupported Data | Données non supportées | Code 1003, type de données inacceptable reçu |
| Invalid Frame Payload Data | Données de charge utile de frame invalides | Code 1007, données incohérentes |
| Policy Violation | Violation de politique | Code 1008, violation de politique |
| Message Too Big | Message trop grand | Code 1009, message trop grand pour être traité |
| TLS Handshake Failure | Échec du handshake TLS | Code 1015, échec du handshake TLS |
Explication de la structure du document
Section 1: Introduction et aperçu
- Contexte et besoins de WebSocket
- Objectifs et principes de conception du protocole
- Relation avec HTTP/TCP
- Modèle de sécurité et philosophie de conception
Sections 2-3: Exigences de base
- Exigences de conformité et définitions terminologiques
- Format d'URI WebSocket (ws:// et wss://)
Section 4: Opening Handshake (noyau)
- Comment les clients initient le handshake
- Comment les serveurs répondent au handshake
- Négociation des sous-protocoles et extensions
- Support de plusieurs versions
Section 5: Tramage de données (noyau)
- Description détaillée de la structure de frame
- Mécanisme de masquage
- Fragmentation (transmission de grands messages en plusieurs frames)
- Frames de contrôle et frames de données
Sections 6-7: Transmission de données et fermeture de connexion
- Règles pour envoyer et recevoir des messages
- Flux de fermeture normal
- Gestion des exceptions
- Codes de statut de fermeture
Sections 8-10: Extensions, erreurs et sécurité
- Mécanisme d'extension
- Gestion des erreurs
- Considérations de sécurité détaillées
Flux d'établissement de connexion WebSocket
1. Le client initie une requête de mise à niveau 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. Le serveur répond avec une mise à niveau
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
3. Connexion établie, communication bidirectionnelle commence
Client ←→ Serveur
↓ ↓
Text/Binary Frames
Ping/Pong Frames
Close Frame
4. Fermer la connexion
Client → Serveur: Close Frame (Code: 1000)
Serveur → Client: Close Frame (Code: 1000)
Fermer la connexion TCP
Calcul de la clé de handshake
Méthode de calcul pour Sec-WebSocket-Accept
// 1. Le client génère une Sec-WebSocket-Key aléatoire (nombre aléatoire de 16 octets encodé en Base64)
const clientKey = "dGhlIHNhbXBsZSBub25jZQ==";
// 2. Chaîne magique (GUID fixe défini par RFC 6455)
const magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
// 3. Concaténer et calculer le hachage SHA-1
const concatenated = clientKey + magicString;
const hash = SHA1(concatenated);
// 4. L'encodage Base64 donne Sec-WebSocket-Accept
const serverAccept = Base64Encode(hash);
// Résultat: "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
Ce mécanisme garantit:
- Le serveur comprend le protocole WebSocket (pas seulement un serveur HTTP normal)
- Empêche les proxies de mise en cache de retourner des réponses incorrectes
- Fournit une vérification de base du handshake
Structure de frame WebSocket en détail
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 ... |
+---------------------------------------------------------------+
Descriptions des champs
FIN (1 bit):
- 0 = d'autres frames suivent
- 1 = c'est la dernière (ou unique) frame du message
RSV1, RSV2, RSV3 (1 bit chacun):
- Réservés pour les extensions
- Doivent être 0 si aucune extension n'est négociée
Opcode (4 bits):
- 0x0 = Continuation (frame de continuation)
- 0x1 = Text (frame de texte, UTF-8)
- 0x2 = Binary (frame binaire)
- 0x8 = Close (fermeture)
- 0x9 = Ping
- 0xA = Pong
- 0x3-0x7, 0xB-0xF = Réservé
MASK (1 bit):
- Client vers serveur: doit être 1
- Serveur vers client: doit être 0
Payload Length (7 bits, 7+16 bits, ou 7+64 bits):
- 0-125: longueur réelle
- 126: les 16 bits suivants sont la longueur réelle
- 127: les 64 bits suivants sont la longueur réelle
Masking-key (0 ou 4 octets):
- Si MASK=1, contient une clé de masquage 32 bits
Payload Data:
- Extension data + Application data
Explication du mécanisme de masquage
Pourquoi le masquage est-il nécessaire?
Raisons de sécurité: Prévient les attaques de type Cache Poisoning. Certains proxies intermédiaires pourraient mettre en cache incorrectement les frames WebSocket, le masquage garantit que les données sont imprévisibles.
Algorithme de masquage
// Lors de l'envoi de données par le client
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;
}
// Lors de la réception de données par le serveur (même algorithme pour le décodage)
function unmaskData(maskedData, maskingKey) {
return maskData(maskedData, maskingKey); // XOR est auto-inverse
}
Points clés:
- Utilise l'opération XOR (^)
- La longueur de la clé est de 4 octets
- Utilisation cyclique de la clé
- Client→Serveur: doit être masqué
- Serveur→Client: interdit de masquer
Fragmentation de message (Fragmentation)
Les grands messages peuvent être envoyés en plusieurs frames:
Exemple: Envoi d'un grand message texte
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="!"
Message final: "Hello World!"
Règles:
- Première frame: FIN=0, Opcode=type de données (0x1 ou 0x2)
- Frames intermédiaires: FIN=0, Opcode=0x0 (Continuation)
- Dernière frame: FIN=1, Opcode=0x0 (Continuation)
- Les frames de contrôle ne peuvent pas être fragmentées et peuvent être insérées entre les frames de données fragmentées
Frames de contrôle en détail
Frame Close (0x8)
Structure:
+--------+--------+------------------+
| Code | Reason |
| (2 octets) | (texte UTF-8) |
+--------+--------+------------------+
Exemple:
Close Code: 1000 (Fermeture normale)
Close Reason: "Going away"
Codes de fermeture couramment utilisés:
- 1000: Normal Closure (Fermeture normale)
- 1001: Going Away (Point de terminaison qui part, ex. navigation de page)
- 1002: Protocol Error (Erreur de protocole)
- 1003: Unsupported Data (Type de données non supporté)
- 1006: Abnormal Closure (Fermeture anormale, aucune frame Close envoyée)
- 1009: Message Too Big (Message trop grand)
- 1011: Internal Server Error (Erreur interne du serveur)
Frame Ping (0x9)
- Peut être envoyée par le client ou le serveur
- Utilisée pour la détection de heartbeat (maintien de la connexion active)
- Peut transporter des données d'application (maximum 125 octets)
- Le destinataire doit répondre avec une frame Pong
Frame Pong (0xA)
- Utilisée pour répondre à une frame Ping
- Doit contenir la même charge utile que la frame Ping
- Peut également être envoyée de manière proactive (heartbeat unidirectionnel)
Flux de fermeture
Fermeture normale (Clean Close)
1. L'initiateur envoie une frame Close
Client → Serveur: Close Frame (Code: 1000)
2. Le destinataire répond avec une frame Close
Serveur → Client: Close Frame (Code: 1000)
3. L'initiateur ferme la connexion TCP
Client: Fermer la connexion TCP
4. Le destinataire ferme également la connexion TCP
Serveur: Fermer la connexion TCP
Fermeture anormale
- Connexion TCP soudainement interrompue (pas de frame Close)
- Données de frame invalides reçues
- Timeout sans réponse
- Violation de règle de protocole
Code de statut: 1006 (Abnormal Closure) - Ce code n'est pas envoyé dans les frames Close, utilisé uniquement pour le rapport
Format d'URI WebSocket
ws:// (non chiffré)
ws://example.com/socket
ws://example.com:8080/chat
ws://192.168.1.1/data
- Port par défaut: 80
- Équivalent au niveau de sécurité http://
wss:// (chiffré TLS)
wss://example.com/socket
wss://secure.example.com:443/chat
- Port par défaut: 443
- Équivalent au niveau de sécurité https://
- Fortement recommandé d'utiliser wss:// en production
Sous-protocole (Subprotocol)
WebSocket est un protocole de couche transport, les sous-protocoles définissent le format de message de la couche application:
Requête du client:
Sec-WebSocket-Protocol: chat, superchat
Réponse du serveur:
Sec-WebSocket-Protocol: chat
La connexion établie utilise le sous-protocole "chat"
Sous-protocoles courants:
- STOMP: Simple Text Oriented Messaging Protocol
- MQTT: Message Queuing Telemetry Transport
- WAMP: Web Application Messaging Protocol
- Protocoles d'application personnalisés
Extensions
Les extensions peuvent améliorer les fonctionnalités WebSocket:
Requête du client:
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Réponse du serveur:
Sec-WebSocket-Extensions: permessage-deflate
Extensions couramment utilisées:
- permessage-deflate: Compression de message (RFC 7692)
- permessage-bzip2: Compression Bzip2
- Extensions personnalisées
Points clés des considérations de sécurité
1. Validation d'origine (Origin Validation)
// Validation d'origine côté serveur
const allowedOrigins = ['https://example.com', 'https://app.example.com'];
const origin = request.headers['origin'];
if (!allowedOrigins.includes(origin)) {
// Refuser la connexion
response.status(403).send('Forbidden');
}
2. Chiffrement TLS
- Doit utiliser wss:// en environnement de production
- Prévient les attaques de l'homme du milieu
- Protège la confidentialité et l'intégrité des données
3. Authentification et autorisation
Méthode 1: Authentification pendant le handshake via les cookies
GET /socket HTTP/1.1
Cookie: session=abc123
Méthode 2: Passer le token via le sous-protocole
ws://example.com/socket?token=jwt_token
Méthode 3: Informations d'authentification dans le premier message après connexion
4. Limitation de débit
- Limiter le nombre de connexions (prévention DoS)
- Limiter la taille des messages
- Limiter la fréquence des messages
5. Validation d'entrée
- Valider l'encodage UTF-8 (frames de texte)
- Valider le format des messages
- Prévenir les attaques par injection
Meilleures pratiques d'implémentation
Client (navigateur)
// 1. Créer une connexion WebSocket
const ws = new WebSocket('wss://example.com/socket', ['chat']);
// 2. Enregistrer les écouteurs d'événements
ws.addEventListener('open', (event) => {
console.log('Connexion établie');
ws.send('Hello Server!');
});
ws.addEventListener('message', (event) => {
console.log('Message reçu:', event.data);
});
ws.addEventListener('error', (event) => {
console.error('Erreur WebSocket:', event);
});
ws.addEventListener('close', (event) => {
console.log('Connexion fermée', event.code, event.reason);
// Implémenter la logique de reconnexion
if (event.code !== 1000) {
setTimeout(() => reconnect(), 5000);
}
});
// 3. Envoyer des données
ws.send('Message texte');
ws.send(new Blob(['Données binaires']));
ws.send(new ArrayBuffer(8));
// 4. Fermer la connexion
ws.close(1000, 'Fermeture normale');
Côté serveur (exemple Node.js)
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws, request) => {
// Valider l'origine
const origin = request.headers.origin;
// ...logique de validation
// Détection de heartbeat
ws.isAlive = true;
ws.on('pong', () => { ws.isAlive = true; });
// Recevoir les messages
ws.on('message', (data) => {
console.log('Reçu:', data);
// Diffuser à tous les clients
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
// Gestion des erreurs
ws.on('error', (error) => {
console.error('Erreur:', error);
});
// Fermeture
ws.on('close', (code, reason) => {
console.log('Connexion fermée:', code, reason);
});
});
// Détection de heartbeat
const interval = setInterval(() => {
wss.clients.forEach((ws) => {
if (ws.isAlive === false) {
return ws.terminate();
}
ws.isAlive = false;
ws.ping();
});
}, 30000);
Scénarios d'application courants
1. Applications de chat en temps réel
- Messagerie instantanée
- Affichage du statut en ligne
- Indicateur de saisie
2. Outils de collaboration en temps réel
- Édition collaborative de documents (comme Google Docs)
- Partage de tableau blanc
- Éditeur de code collaboratif
3. Flux de données en temps réel
- Cours boursiers
- Scores sportifs
- Données IoT
4. Jeux en ligne
- Jeux multijoueurs en ligne
- Synchronisation d'état de jeu en temps réel
- Interaction à faible latence
5. Notifications push
- Push navigateur
- Alertes en temps réel
- Surveillance système
Comparaison avec d'autres technologies
WebSocket vs HTTP Long Polling
| Caractéristique | WebSocket | Long Polling |
|---|---|---|
| Connexion | Connexion persistante | Requêtes HTTP répétées |
| Latence | Très faible | Plus élevée (overhead HTTP) |
| Bidirectionnel | Vraiment bidirectionnel | Pseudo-bidirectionnel |
| Ressources | Faible (une connexion) | Élevé (plusieurs connexions) |
| Complexité | Moyenne | Plus simple |
WebSocket vs Server-Sent Events (SSE)
| Caractéristique | WebSocket | SSE |
|---|---|---|
| Communication bidirectionnelle | ✅ Bidirectionnel | ❌ Seulement Serveur→Client |
| Format de données | Binaire ou texte | Texte (UTF-8) |
| Protocole | Protocole indépendant | Basé sur HTTP |
| Support navigateur | Largement supporté | Largement supporté (sauf IE) |
| Reconnexion | Implémentation manuelle | Reconnexion automatique |
Outils de débogage
Outils de développement du navigateur
- Chrome DevTools → Network → WS
- Afficher l'envoi et la réception de frames
- Afficher l'état de la connexion
Outils en ligne de commande
# wscat (Node.js)
npm install -g wscat
wscat -c ws://echo.websocket.org
# websocat (Rust)
websocat ws://echo.websocket.org
Outils de test en ligne
- websocket.org Echo Test
- Hoppscotch (anciennement Postwoman)
- Firecamp
Ressources de référence
Standards RFC connexes
- RFC 6455: Protocole WebSocket (ce document)
- RFC 7692: Extension de compression WebSocket
- RFC 8441: Bootstrapping WebSocket sur HTTP/2
- RFC 8307: URI bien connu pour WebSocket
Bibliothèques d'implémentation recommandées
JavaScript (navigateur):
- API WebSocket native
Node.js:
- ws (léger)
- Socket.IO (riche en fonctionnalités, dégradation automatique)
- uWebSockets.js (haute performance)
Python:
- websockets (asyncio)
- ws4py
- Tornado
Java:
- Java WebSocket API (JSR 356)
- Netty
- Spring WebSocket
Go:
- gorilla/websocket
- nhooyr/websocket
Étapes d'apprentissage suivantes
- Bases: Comprendre le handshake WebSocket et la structure de frame
- Pratique: Implémenter une application de chat simple
- Avancé: Apprendre les sous-protocoles et extensions
- Optimisation: Implémenter heartbeat, reconnexion, gestion des erreurs
- Sécurité: Approfondir les considérations de sécurité et meilleures pratiques
Résumé
Le protocole WebSocket est la pierre angulaire de la communication en temps réel sur le Web:
- Précision: Implémentation précise de la communication bidirectionnelle
- Transmission: Spécification de protocole claire
- Élégance: Conception et implémentation élégantes
Valeurs fondamentales:
- 🔄 Véritable communication full-duplex
- ⚡ Transmission de données en temps réel à faible latence
- 📦 Protocole de frame léger
- 🔐 Mécanismes de sécurité intégrés
Meilleurs scénarios d'utilisation:
- Chat en temps réel
- Édition collaborative
- Jeux en temps réel
- Flux de données en temps réel
- Notifications push
RFCs connexes:
- RFC 6455: Protocole WebSocket (ce document)
- RFC 7692: Extension de compression WebSocket
- RFC 8441: Bootstrapping WebSocket sur HTTP/2