Pourquoi WebSockets avec Express ?
Les WebSockets sont essentiels pour les applications qui nécessitent une communication bidirectionnelle en temps réel, comme des chatbots, des jeux en ligne, ou des applications de collaboration en temps réel. Avec un contexte réel où la performance et la latence jouent un rôle crucial, le choix d'Express pour servir des WebSockets est une option solide. Un cas concret serait une application de chat où les messages doivent être transmis instantanément à tous les utilisateurs en même temps.
Prerequis
- Connaissances en JavaScript et Node.js
- Familiarité avec Express.js
- Compréhension de bases du TCP/IP
- Installation de Node.js v14 ou supérieur et npm (Node Package Manager)
Pour installer les dépendances nécessaires :
npm install express ws
Concepts fondamentaux
Concept 1: WebSockets vs HTTP
WebSockets est un protocole basé sur HTTP, mais il permet une communication persistante entre le client et le serveur. Ce qui en fait différent de HTTP est qu'il établit une connexion bidirectionnelle dès la première requête.
// Serveur WebSocket avec Express
const express = require('express');
const WebSocket = require('ws');
const app = express();
app.use(express.static('public'));
const wss = new WebSocket.Server({ server: app.listen(3000) });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('Welcome to the WebSocket server!');
});
Concept 2: Émission et Réception de Messages
Les messages peuvent être émis du serveur au client en utilisant la méthode send(). Les clients peuvent également envoyer des messages au serveur qui seront reçus par le gestionnaire d'événements message.
// Client WebSocket
const socket = new WebSocket('ws://localhost:3000');
socket.onopen = function open() {
console.log('Connected to server');
socket.send('Hello Server!');
};
socket.onmessage = function incoming(data) {
console.log('Received from server:', data);
};
Concept 3: Gestion des Connexions
Pour gérer les connexions, le serveur WebSocket peut utiliser des événements comme connection, close, et error.
wss.on('connection', function connection(ws) {
ws.on('close', function close() {
console.log('Connection closed');
});
ws.on('error', function error(err) {
console.error('Error:', err);
});
});
Mise en pratique : projet fil rouge
Mini-Projet: Gestionnaire de Tâches en temps Réel
Étape 1: Initialisation du Projet
Créez un nouveau répertoire pour votre projet et initialisez-le avec npm.
mkdir task-manager
cd task-manager
npm init -y
Installez les dépendances nécessaires :
npm install express ws body-parser
Étape 2: Structure du Projet
Créez les fichiers suivants :
server.js: le serveur principalpublic/index.html: la page HTML pour afficher les tâchespublic/script.js: le script JavaScript pour gérer l'interface utilisateur et les communications WebSocket
Étape 3: Serveur Express
// server.js
const express = require('express');
const WebSocket = require('ws');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(express.static('public'));
const wss = new WebSocket.Server({ server: app.listen(3000) });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(data) {
console.log('Received:', data);
broadcast(data);
});
});
function broadcast(message) {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
}
app.get('/', (req, res) => {
res.sendFile(__dirname + '/public/index.html');
});
Étape 4: Interface Utilisateur
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Task Manager</title>
</head>
<body>
<h1>Task Manager</h1>
<input type="text" id="taskInput" placeholder="Add a new task">
<button onclick="addTask()">Add Task</button>
<script src="/script.js"></script>
</body>
</html>
Étape 5: Script JavaScript pour l'Interface Utilisateur
// public/script.js
const socket = new WebSocket('ws://localhost:3000');
socket.onopen = function open() {
console.log('Connected to server');
};
function addTask() {
const input = document.getElementById('taskInput');
const taskText = input.value;
if (taskText) {
socket.send(JSON.stringify({ type: 'add', task: taskText }));
input.value = '';
}
}
socket.onmessage = function incoming(data) {
const message = JSON.parse(data);
const tasksList = document.getElementById('tasks');
if (message.type === 'add') {
const li = document.createElement('li');
li.textContent = message.task;
tasksList.appendChild(li);
}
};
Étape 6: Lancer le Serveur
node server.js
Allez sur http://localhost:3000 dans votre navigateur pour voir l'application en action. Vous devriez pouvoir ajouter des tâches et les voir apparaître instantanément pour tous les utilisateurs.
Erreurs frequentes et debugging
1. Connexion Refusée
Message d'erreur :
Error: connect ECONNREFUSED 127.0.0.1:3000
Cause : Le serveur n'est pas en cours d'exécution.
// server.js (correction)
const app = express();
app.use(express.static('public'));
const wss = new WebSocket.Server({ server: app.listen(3000) });
2. Erreur de Broadcast
Message d'erreur :
TypeError: Cannot read property 'readyState' of undefined
Cause : wss.clients peut être vide.
// server.js (correction)
function broadcast(message) {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
}
3. Erreur de Réception
Message d'erreur :
SyntaxError: Unexpected token o in JSON at position 1
Cause : La données reçues ne sont pas au format JSON.
// server.js (correction)
ws.on('message', function incoming(data) {
try {
const message = JSON.parse(data);
console.log('Received:', message);
broadcast(JSON.stringify(message));
} catch (e) {
console.error('Invalid JSON:', data);
}
});
Pour aller plus loin
1. Authentification et Sécurité
Ajoutez une couche d'authentification pour sécuriser les connexions WebSocket.
2. Gestion des Salles de Discussion
Créez des salles de discussion distinctes pour gérer la communication entre différents groupes d'utilisateurs.
Concept de Namespace et Room dans Socket.IO
3. Optimisation des Connexions
Utilisez des techniques comme la compression et l'authentification basée sur les cookies pour améliorer les performances des connexions WebSocket.
Concept de Compression dans WebSocket
Défi Pratique :
Créez un mini-projet où vous intégrez la gestion des salles de discussion avec des WebSockets et Express. Ajoutez une fonctionnalité pour envoyer des messages spécifiques à chaque salle.