Aller au contenu principal

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.

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


Terminologie de base WebSocket

Concepts fondamentaux

Terme anglaisTraduction françaiseDescription
WebSocket ProtocolProtocole WebSocketProtocole permettant une communication full-duplex sur une connexion TCP unique
Full-Duplex CommunicationCommunication full-duplexCommunication bidirectionnelle simultanée
Opening HandshakeOpening HandshakeProcessus de mise à niveau HTTP pour établir une connexion WebSocket
Closing HandshakeClosing HandshakeProcessus pour fermer proprement une connexion WebSocket
FrameFrame / TrameUnité de base de transmission de données WebSocket
MessageMessageDonnées d'application complètes composées d'une ou plusieurs frames
ClientClientPartie qui initie la connexion WebSocket (généralement un navigateur)
ServerServeurPartie qui accepte la connexion WebSocket
EndpointPoint de terminaisonClient ou serveur
ConnectionConnexionConnexion WebSocket entre client et serveur

Relatif au handshake

Terme anglaisTraduction françaiseDescription
Upgrade RequestRequête de mise à niveauRequête HTTP pour passer à WebSocket
Sec-WebSocket-KeyClé WebSocketClé aléatoire envoyée par le client
Sec-WebSocket-AcceptAccept WebSocketClé de vérification retournée par le serveur
Sec-WebSocket-ProtocolSous-protocole WebSocketSous-protocole de couche application négocié
Sec-WebSocket-ExtensionsExtensions WebSocketExtensions de protocole négociées
Sec-WebSocket-VersionVersion WebSocketNuméro de version du protocole (actuellement 13)
OriginOrigineProvenance de la page Web qui initie la connexion

Relatif à la structure de frame

Terme anglaisTraduction françaiseDescription
FINFIN / Bit de finIndique qu'il s'agit de la dernière frame d'un message
RSVRSV / Bits réservésBits de drapeau réservés pour les extensions
OpcodeOpcode / Code d'opérationDéfinit le type de frame
MaskMasqueLes données client vers serveur doivent être masquées
Masking-keyClé de masquageClé 32 bits pour le masquage des données
Payload LengthLongueur de charge utileLongueur des données
Payload DataDonnées de charge utileDonnées réellement transmises
Extension DataDonnées d'extensionDonnées supplémentaires négociées par les extensions
Application DataDonnées d'applicationDonnées réelles de la couche application

Types de frames

Terme anglaisTraduction françaiseOpcodeDescription
Continuation FrameFrame de continuation0x0Frames suivantes d'un message
Text FrameFrame de texte0x1Données texte encodées en UTF-8
Binary FrameFrame binaire0x2Données binaires
Close FrameFrame de fermeture0x8Frame de contrôle pour fermer la connexion
Ping FrameFrame Ping0x9Requête de détection de heartbeat
Pong FrameFrame Pong0xARéponse de détection de heartbeat
Control FrameFrame de contrôle0x8-0xFFrames pour contrôler la connexion
Data FrameFrame de données0x1-0x2Frames pour transmettre les données d'application

Relatif à la fermeture

Terme anglaisTraduction françaiseDescription
Close CodeCode de fermetureCode numérique indiquant la raison de fermeture de connexion
Close ReasonRaison de fermetureDescription textuelle de la fermeture
Normal ClosureFermeture normaleCode 1000, fermeture propre après accomplissement
Going AwayDépartCode 1001, point de terminaison qui part (ex. navigation de page)
Protocol ErrorErreur de protocoleCode 1002, violation de protocole
Unsupported DataDonnées non supportéesCode 1003, type de données inacceptable reçu
Invalid Frame Payload DataDonnées de charge utile de frame invalidesCode 1007, données incohérentes
Policy ViolationViolation de politiqueCode 1008, violation de politique
Message Too BigMessage trop grandCode 1009, message trop grand pour être traité
TLS Handshake FailureÉchec du handshake TLSCode 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éristiqueWebSocketLong Polling
ConnexionConnexion persistanteRequêtes HTTP répétées
LatenceTrès faiblePlus élevée (overhead HTTP)
BidirectionnelVraiment bidirectionnelPseudo-bidirectionnel
RessourcesFaible (une connexion)Élevé (plusieurs connexions)
ComplexitéMoyennePlus simple

WebSocket vs Server-Sent Events (SSE)

CaractéristiqueWebSocketSSE
Communication bidirectionnelle✅ Bidirectionnel❌ Seulement Serveur→Client
Format de donnéesBinaire ou texteTexte (UTF-8)
ProtocoleProtocole indépendantBasé sur HTTP
Support navigateurLargement supportéLargement supporté (sauf IE)
ReconnexionImplémentation manuelleReconnexion 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

  1. Bases: Comprendre le handshake WebSocket et la structure de frame
  2. Pratique: Implémenter une application de chat simple
  3. Avancé: Apprendre les sous-protocoles et extensions
  4. Optimisation: Implémenter heartbeat, reconnexion, gestion des erreurs
  5. 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:

  1. 🔄 Véritable communication full-duplex
  2. ⚡ Transmission de données en temps réel à faible latence
  3. 📦 Protocole de frame léger
  4. 🔐 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

Retour à la liste des RFC