Mise en place d'un serveur WebSocket simple et connexion client
Introduction au Monde des WebSockets
Bonjour et bienvenue dans ce module de "Maîtriser les WebSockets et les Architectures Temps Réel pour des Applications Web Dynamiques" ! Aujourd'hui, nous allons plonger au cœur de la technologie WebSocket en mettant concrètement en place un serveur et un client simples. Cette leçon est fondamentale car elle pose les bases pratiques de toute application temps réel construite sur cette puissante spécification.
Notre objectif est de comprendre le fonctionnement bidirectionnel et persistant des WebSockets, en partant de zéro pour construire une petite application de chat qui illustrera parfaitement la communication en temps réel entre plusieurs clients et un serveur.
Qu'est-ce qu'un WebSocket ?
Avant de manipuler le code, faisons un rapide rappel. Les WebSockets sont une technologie de communication avancée qui permet une communication bidirectionnelle (full-duplex) sur une seule connexion TCP. Contrairement au protocole HTTP, qui est stateless (sans état) et fonctionne sur un modèle requête-réponse, les WebSockets maintiennent une connexion persistante entre le client et le serveur une fois établie.
Imaginez une conversation téléphonique : une fois l'appel établi, vous pouvez parler et entendre l'autre personne simultanément, sans avoir à raccrocher et rappeler pour chaque échange. C'est l'essence même du WebSocket.
Pourquoi les WebSockets ? Les Avantages Clés
Les WebSockets offrent des avantages significatifs par rapport aux approches de simulation du temps réel (comme le long polling ou le server-sent events pour la communication unidirectionnelle) :
- Communication Bidirectionnelle Complète (Full-Duplex) : Le client et le serveur peuvent envoyer des messages à tout moment, indépendamment l'un de l'autre, sans avoir besoin d'une nouvelle requête.
- Faible Latence : Une fois la connexion établie, les en-têtes sont beaucoup plus légers qu'en HTTP, ce qui réduit la latence et rend les échanges quasi instantanés.
- Efficacité et Économie de Ressources : Une seule connexion TCP est maintenue, ce qui réduit la surcharge liée à l'établissement et à la fermeture de multiples connexions HTTP.
- Idéal pour les Applications Temps Réel : Parfaitement adapté aux :
- Applications de chat
- Jeux en ligne multijoueurs
- Tableaux de bord en temps réel
- Notifications push
- Outils de collaboration
Prérequis
Pour suivre cette leçon et mettre en place notre exemple, vous aurez besoin de :
- Node.js et npm (Node Package Manager) : Installés sur votre machine. Nous utiliserons Node.js pour créer notre serveur WebSocket.
- Un éditeur de texte : Comme VS Code, Sublime Text, etc.
- Un navigateur web moderne : (Chrome, Firefox, Edge, Safari) pour le client WebSocket.
- Connaissances de base en JavaScript et HTML : Pour comprendre le code client et serveur.
Mise en place du serveur WebSocket
Nous allons utiliser Node.js pour notre serveur et la bibliothèque ws, qui est une implémentation WebSocket rapide et simple.
Choix de la technologie : Node.js et ws
Node.js est particulièrement bien adapté aux applications temps réel grâce à son modèle d'E/S non bloquant. La bibliothèque ws est l'une des implémentations de serveur et de client WebSocket les plus populaires pour Node.js, connue pour sa performance et sa simplicité.
Installation des dépendances
- Créez un nouveau dossier pour votre projet, par exemple
websocket-chat. - Naviguez dans ce dossier via votre terminal.
- Initialisez un nouveau projet Node.js :
mkdir websocket-chat cd websocket-chat npm init -y - Installez la bibliothèque
ws:npm install ws
Code du serveur (server.js)
Créez un fichier nommé server.js dans votre dossier de projet et ajoutez le code suivant :
// server.js
const { WebSocketServer } = require('ws');
// Crée un serveur WebSocket sur le port 8080
const wss = new WebSocketServer({ port: 8080 });
console.log('Serveur WebSocket démarré sur le port 8080');
// Événement déclenché lorsqu'un client se connecte
wss.on('connection', ws => {
console.log('Un nouveau client est connecté !');
// Événement déclenché lorsqu'un message est reçu du client
ws.on('message', message => {
const decodedMessage = message.toString(); // Les messages sont des Buffers par défaut
console.log(`Message reçu du client : ${decodedMessage}`);
// Diffuser le message à TOUS les clients connectés
wss.clients.forEach(client => {
if (client.readyState === ws.OPEN) { // Vérifie si la connexion est ouverte
client.send(decodedMessage);
}
});
});
// Événement déclenché lorsque la connexion est fermée par le client
ws.on('close', () => {
console.log('Un client s\'est déconnecté.');
});
// Événement déclenché en cas d'erreur de connexion
ws.on('error', error => {
console.error('Erreur WebSocket : ', error);
});
// Envoie un message de bienvenue au client juste après la connexion
ws.send('Bienvenue sur le serveur WebSocket !');
});
Explication du code serveur
const { WebSocketServer } = require('ws');: Importe la classeWebSocketServerde la bibliothèquews. C'est elle qui nous permettra de créer et gérer le serveur.const wss = new WebSocketServer({ port: 8080 });: Instancie un nouveau serveur WebSocket qui écoutera les connexions entrantes sur le port8080.wss.on('connection', ws => { ... });: C'est l'événement le plus important. Il est déclenché chaque fois qu'un nouveau client établit une connexion WebSocket avec le serveur. La variablewsdans le callback représente l'objet WebSocket de la connexion spécifique de ce client.ws.on('message', message => { ... });: Cet événement est déclenché lorsque le serveur reçoit un message d'un client. Lemessageest unBufferpar défaut, d'où l'utilisation demessage.toString()pour le convertir en chaîne de caractères.wss.clients.forEach(client => { ... });: Pour notre application de chat, nous voulons que chaque message envoyé par un client soit reçu par tous les autres clients (et lui-même).wss.clientsest unSetcontenant tous les objets WebSocket des clients actuellement connectés. Nous itérons sur ceSetet utilisonsclient.send(decodedMessage)pour envoyer le message à chaque client. Nous vérifionsclient.readyState === ws.OPENpour s'assurer que la connexion est toujours active.ws.on('close', () => { ... });: Cet événement est déclenché lorsque la connexion d'un client est fermée (soit par le client, soit en cas de problème réseau).ws.on('error', error => { ... });: Gère les erreurs spécifiques à la connexion du client.ws.send('Bienvenue...');: Ceci envoie un message uniquement au client qui vient de se connecter, juste après l'établissement de la connexion.
Mise en place du client WebSocket
Notre client sera une simple page HTML avec du JavaScript pour interagir avec le serveur.
Création de la page HTML (index.html)
Créez un fichier nommé index.html dans le même dossier que server.js et ajoutez le code suivant :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Client WebSocket Simple</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
#messages { border: 1px solid #ccc; padding: 10px; min-height: 200px; max-height: 400px; overflow-y: scroll; margin-bottom: 10px; }
.message { margin-bottom: 5px; }
.server { color: gray; font-style: italic; }
.client { color: blue; }
input[type="text"] { width: 300px; padding: 8px; margin-right: 5px; }
button { padding: 8px 15px; cursor: pointer; }
</style>
</head>
<body>
<h1>Chat WebSocket Simple</h1>
<div id="messages"></div>
<input type="text" id="messageInput" placeholder="Tapez votre message ici...">
<button id="sendButton">Envoyer</button>
<script>
const messagesDiv = document.getElementById('messages');
const messageInput = document.getElementById('messageInput');
const sendButton = document.getElementById('sendButton');
// Création de la connexion WebSocket
// L'URL 'ws://localhost:8080' doit correspondre à l'adresse et au port de votre serveur
const socket = new WebSocket('ws://localhost:8080');
// Événement déclenché lorsque la connexion est établie
socket.onopen = function(event) {
appendMessage('Connexion WebSocket établie !', 'server');
console.log('Connexion ouverte:', event);
};
// Événement déclenché lorsqu'un message est reçu du serveur
socket.onmessage = function(event) {
appendMessage(`Message reçu: ${event.data}`, 'client');
console.log('Message reçu:', event.data);
};
// Événement déclenché lorsque la connexion est fermée
socket.onclose = function(event) {
appendMessage('Connexion WebSocket fermée.', 'server');
console.log('Connexion fermée:', event);
};
// Événement déclenché en cas d'erreur
socket.onerror = function(error) {
appendMessage(`Erreur WebSocket: ${error.message}`, 'server');
console.error('Erreur WebSocket:', error);
};
// Fonction pour envoyer un message au serveur
sendButton.onclick = function() {
const message = messageInput.value;
if (message) {
socket.send(message); // Envoie le message au serveur
messageInput.value = ''; // Efface l'input après l'envoi
}
};
// Permet d'envoyer le message en appuyant sur 'Entrée'
messageInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendButton.click();
}
});
// Fonction utilitaire pour ajouter des messages au DOM
function appendMessage(text, type) {
const messageElement = document.createElement('div');
messageElement.classList.add('message', type);
messageElement.textContent = text;
messagesDiv.appendChild(messageElement);
messagesDiv.scrollTop = messagesDiv.scrollHeight; // Scroll vers le bas
}
</script>
</body>
</html>
Explication du code client
<style>: Un peu de CSS pour rendre l'interface plus agréable.<div id="messages"></div>: C'est ici que tous les messages (envoyés et reçus) seront affichés.<input type="text" id="messageInput">et<button id="sendButton">: L'interface utilisateur pour taper et envoyer des messages.const socket = new WebSocket('ws://localhost:8080');: C'est la ligne clé. Elle crée une nouvelle instance deWebSocketet tente de se connecter au serveur WebSocket surws://localhost:8080. L'URL doit utiliser le préfixews://(ouwss://pour une connexion sécurisée).socket.onopen = function(event) { ... };: Cet événement est déclenché une fois que la connexion WebSocket avec le serveur est établie avec succès.socket.onmessage = function(event) { ... };: Déclenché lorsque le client reçoit un message du serveur. Le message est contenu dansevent.data.socket.onclose = function(event) { ... };: Déclenché lorsque la connexion est fermée, soit par le serveur, soit par le client, soit en cas de problème.socket.onerror = function(error) { ... };: Gère les erreurs survenant pendant la durée de vie de la connexion WebSocket.sendButton.onclick = function() { ... };: Lorsque le bouton "Envoyer" est cliqué, il prend le texte de l'input et l'envoie au serveur viasocket.send(message).appendMessage(text, type): Une fonction utilitaire pour ajouter les messages dans ladivd'affichage, avec une classe pour les différencier (serveur ou client).
Comment ça marche : Le Handshake WebSocket
La mise en place d'une connexion WebSocket ne se fait pas directement. Elle commence par un handshake (poignée de main) HTTP.
-
Requête d'Upgrade du Client : Le client envoie une requête HTTP standard au serveur, mais avec des en-têtes spéciaux :
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Version: 13Upgrade: websocketetConnection: Upgrade: Indiquent au serveur que le client souhaite "upgrader" (passer à) le protocole de communication vers WebSocket.Sec-WebSocket-Key: Une clé utilisée par le serveur pour prouver qu'il comprend le protocole WebSocket.Sec-WebSocket-Version: La version du protocole WebSocket que le client souhaite utiliser.
-
Réponse d'Upgrade du Serveur : Si le serveur supporte WebSockets et accepte la requête, il répond avec une réponse HTTP spécifique :
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9KuZygjjmkjfdDk=101 Switching Protocols: Ce code de statut indique que le serveur est prêt à changer de protocole.Sec-WebSocket-Accept: Le serveur calcule cette valeur en utilisant laSec-WebSocket-Keydu client et une chaîne magique, puis la renvoie. Cela confirme au client que le serveur parle bien WebSocket.
Une fois que cette "poignée de main" HTTP est terminée avec succès, la connexion HTTP est "détournée" et transformée en une connexion WebSocket persistante et bidirectionnelle. À partir de ce moment, tous les messages échangés sont encadrés par le protocole WebSocket et ne contiennent plus les en-têtes HTTP lourds.
Test de l'application
Suivez ces étapes pour tester votre serveur et client WebSocket :
-
Démarrez le serveur : Ouvrez votre terminal, naviguez vers le dossier
websocket-chatet exécutez :node server.jsVous devriez voir le message
Serveur WebSocket démarré sur le port 8080. Laissez ce terminal ouvert. -
Ouvrez le client dans votre navigateur : Ouvrez le fichier
index.htmldirectement dans votre navigateur web. Vous pouvez le faire en double-cliquant dessus ou en faisant un clic droit -> "Ouvrir avec..." votre navigateur préféré. -
Interagissez :
- Dès que
index.htmlest chargé, le client tentera de se connecter au serveur. Vous devriez voir un messageUn nouveau client est connecté !dans votre terminal etConnexion WebSocket établie !suivi deMessage reçu: Bienvenue sur le serveur WebSocket !dans la page web. - Tapez un message dans la zone de texte et cliquez sur "Envoyer" (ou appuyez sur Entrée). Le message devrait apparaître dans la section des messages de la page web.
- Testez la diffusion : Ouvrez
index.htmldans un deuxième onglet ou un autre navigateur. Connectez-vous également. Envoyez un message depuis le premier onglet, et vous verrez qu'il apparaît instantanément dans le deuxième onglet, et vice-versa ! Cela démontre la capacité de diffusion de notre serveur WebSocket.
- Dès que
Félicitations ! Vous avez mis en place votre premier serveur et client WebSocket fonctionnels.
Conclusion et Prochaines Étapes
Dans cette leçon, nous avons appris à :
- Comprendre les fondamentaux des WebSockets et leurs avantages pour les applications temps réel.
- Mettre en place un serveur WebSocket simple avec Node.js et la bibliothèque
ws. - Créer un client WebSocket en HTML et JavaScript pour interagir avec le serveur.
- Saisir le concept du handshake WebSocket.
- Tester une application de chat temps réel simple.
C'est un jalon important dans votre parcours pour maîtriser les architectures temps réel. La simplicité et la puissance de cette approche ouvrent la porte à une multitude d'applications dynamiques.
Pour aller plus loin, nous explorerons dans les prochaines leçons des sujets comme :
- Sécurisation des WebSockets (WSS) : Utilisation de SSL/TLS pour des connexions sécurisées.
- Gestion des états et des utilisateurs : Comment gérer les sessions, l'authentification et les données spécifiques aux utilisateurs.
- Utilisation de frameworks avancés : Comme Socket.IO, qui offre des fonctionnalités de reconnexion automatique, de fallback sur d'autres transports, et de gestion de "rooms" (salons de discussion).
- Déploiement d'applications WebSocket : Comment mettre en production votre application temps réel.
Préparez-vous à construire des expériences utilisateur encore plus riches et interactives !