Un serveur WebSocket est conçu pour la réception de données en temps réel, contrairement à un serveur web classique où vous ouvrez un lien dans votre navigateur et obtenez des données (par exemple, html, xml, json, etc.). Avec un serveur classique, il établit une connexion -> transmet des données -> rompt la connexion.
Un serveur WebSocket fonctionne différemment : vous établissez une connexion avec lui, et elle reste continuellement active. Dès que les données changent, le serveur les transmet au client. Parce qu'il utilise une communication bidirectionnelle, la connexion est toujours active et n'est pas rompue.
1. Application
Pour recevoir des données en temps réel, par exemple, si vous souhaitez afficher les visiteurs de votre site web ou le statut des commandes de votre boutique en ligne en direct sur une page web séparée ou même sur un appareil distinct.
2. Comment ça marche
Un serveur WebSocket est un service distinct qui fonctionne sur un port dédié que vous attribuez pour les connexions ultérieures. Le protocole WS (serveur WebSocket) ou WSS (serveur WebSocket + SSL) vous permet de configurer la sortie de données avec une mise à jour en temps réel, par exemple via un navigateur utilisant html + JS.
Veuillez noter ! Étant donné que le service nécessite l'ouverture d'un nouveau port, la configuration et le lancement d'un tel service ne sont possibles que sur un serveur virtuel. C'est précisément pourquoi un serveur WebSocket n'est pas disponible pour les services d'hébergement mutualisé.
Imaginez si chaque client d'hébergement mutualisé voulait lancer un tel service – il faudrait ouvrir un port distinct pour chacun, ce qui compromettrait la sécurité du service lui-même. Indépendamment du fournisseur d'hébergement, le lancement et la configuration ne sont possibles que sur un serveur VPS/VDS.
3. Exemple de tâche
Supposons que nous ayons un script PHP https://domain.tld/online-orders.php qui génère des données au format JSON, et que nous voulions que ces données soient affichées en temps réel. Actuellement, pour obtenir ce résultat, une actualisation constante est nécessaire.
Veuillez noter que, si pour un PC et un navigateur les données peuvent être actualisées en augmentant la fréquence des requêtes, dans le cas de requêtes fréquentes et d'actualisations de données JSON depuis un microcontrôleur, cela le ferait simplement planter pendant 1 à 2 secondes. Et, par exemple, une requête POST/GET régulière toutes les 20 secondes n'est toujours pas vraiment en temps réel.
Par conséquent, nous avons besoin de WSS. Lançons un nouveau VPS sous Debian 12 avec une configuration minimale en ressources. Une haute performance n'est pas nécessaire pour WSS. Toute la configuration sera effectuée en tant qu'utilisateur root.
3.1 Installation des services
apt update
apt install -y curl git nodejs npm
3.2 Création du projet WebSocket
mkdir ~/websocket-server
cd ~/websocket-server
npm init -y
npm install ws node-fetch express
3.3 Créons un fichier de configuration à la racine du projet websocket-server
nano server.js
import express from "express";
import { WebSocketServer } from "ws";
import fetch from "node-fetch";
import https from "https";
import fs from "fs";
// === Paramètres ===
const PORT = 8080; // Choisissons un port pour ce serveur WebSocket
const FETCH_URL = "https://domain.tld/online-orders.php";
const FETCH_INTERVAL = 1000; // une fois toutes les 1 seconde
// === Certificats HTTPS ===
// (Si WSS est requis, un certificat SSL est nécessaire. Vous pouvez utiliser Let's Encrypt ou un certificat auto-signé)
const options = {
key: fs.readFileSync("/etc/ssl/private/your-key.pem"),
cert: fs.readFileSync("/etc/ssl/certs/your-cert.pem")
};
// === Serveur HTTP(S) + WebSocket ===
const app = express();
const server = https.createServer(options, app);
const wss = new WebSocketServer({ server });
let latestData = null;
// === Requête JSON périodique ===
async function updateData() {
try {
const res = await fetch(FETCH_URL);
const json = await res.json();
latestData = json;
// Envoyer à tous les clients connectés
wss.clients.forEach(client => {
if (client.readyState === 1) { // WebSocket.OPEN
client.send(JSON.stringify(json));
}
});
} catch (err) {
console.error("Erreur lors de la récupération des données :", err);
}
}
setInterval(updateData, FETCH_INTERVAL);
updateData(); // chargement initial
// === Connexion WebSocket ===
wss.on("connection", ws => {
console.log("Client connecté");
if (latestData) ws.send(JSON.stringify(latestData));
ws.on("close", () => console.log("Client déconnecté"));
});
server.listen(PORT, () => {
console.log(`Serveur WSS démarré sur le port ${PORT}`);
});
3.4 Configuration SSL pour WSS via Let's Encrypt
Pour le SSL, un domaine est requis. Nous utiliserons un sous-domaine, par exemple, ws.domain.tld.
apt install certbot python3-certbot-nginx
certbot certonly --standalone -d ws.domain.tld
Suivez la courte procédure, en fournissant une adresse e-mail pour les notifications. Une fois terminé, le certificat et la clé se trouveront ici :
/etc/letsencrypt/live/ws.domain.tld/fullchain.pem
/etc/letsencrypt/live/ws.domain.tld/privkey.pem
Remplacez les chemins dans le fichier server.js :
const options = {
key: fs.readFileSync("/etc/letsencrypt/live/ws.domain.tld/privkey.pem"),
cert: fs.readFileSync("/etc/letsencrypt/live/ws.domain.tld/fullchain.pem")
};
3.5 Exécution et débogage
node server.js
Nous avons reçu une erreur SyntaxError: Cannot use import statement outside a module.
Dans ce cas, vous devez modifier le fichier package.json en ajoutant la ligne "type": "module",.
Exemple de configuration correcte de package.json :
{
"name": "websocket-server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
...
Relancer :
node server.js
Serveur WSS démarré sur le port 8080
Client connecté
3.6 Configurer le démarrage automatique :
npm install -g pm2
pm2 start server.js --name websocket
pm2 save
pm2 startup
3.7 Vérifier le fonctionnement du service
pm2 list
3.8 Connexion via HTML et JS
Créez un fichier nommé wss-client.html sur votre PC avec le contenu suivant :
Vous ne pouvez pas vous connecter à un WebSocket directement via un domaine dans un navigateur comme un serveur web ; un script doit être créé. Spécifiez votre domaine à la place de ws.domain.tld.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Test WSS</title>
<style>
body {
font-family: monospace;
background: #111;
color: #0f0;
padding: 20px;
}
input, button {
background: #222;
color: #0f0;
border: 1px solid #0f0;
padding: 6px 10px;
font-family: monospace;
}
#log {
margin-top: 20px;
white-space: pre-wrap;
background: #000;
border: 1px solid #0f0;
padding: 10px;
height: 400px;
overflow-y: auto;
}
</style>
</head>
<body>
<h2>🔌 Client de test WebSocket</h2>
<label>URL du serveur :</label>
<input id="wsUrl" type="text" size="50" value="wss://ws.domain.tld:8080">
<button onclick="connect()">Connecter</button>
<button onclick="disconnect()">Déconnecter</button>
<div id="status">Statut : <b>Déconnecté</b></div>
<div id="log"></div>
<script>
let ws;
function log(msg) {
const logDiv = document.getElementById('log');
logDiv.innerText += msg + "\n";
logDiv.scrollTop = logDiv.scrollHeight;
}
function connect() {
const url = document.getElementById('wsUrl').value;
ws = new WebSocket(url);
log("🔄 Connexion à " + url + " ...");
document.getElementById('status').innerHTML = "Statut : <b>Connexion...</b>";
ws.onopen = () => {
log("✅ Connexion établie");
document.getElementById('status').innerHTML = "Statut : <b style='color:lime'>Connecté</b>";
};
ws.onmessage = event => {
try {
const json = JSON.parse(event.data);
log("📦 Reçu :\n" + JSON.stringify(json, null, 2));
} catch (e) {
log("📦 Texte :\n" + event.data);
}
};
ws.onclose = () => {
log("❌ Connexion fermée");
document.getElementById('status').innerHTML = "Statut : <b style='color:red'>Déconnecté</b>";
};
ws.onerror = err => {
log("⚠️ Erreur : " + err.message);
};
}
function disconnect() {
if (ws) {
ws.close();
ws = null;
}
}
</script>
</body>
</html>
3.9 Affichage des journaux
La connexion devrait fonctionner ; dès que les données changent dans notre script, le WebSocket les met à jour pour tous les clients connectés.
Vous pouvez consulter le journal des événements avec la commande :
pm2 logs websocket
0|websocket | 5 visites mises à jour
4. Autorisation
Dans l'exemple ci-dessus, n'importe qui peut se connecter au serveur WebSocket, alors ajoutons une autorisation basée sur un jeton.
Code server.js mis à jour :
import express from "express";
import { WebSocketServer } from "ws";
import fetch from "node-fetch";
import https from "https";
import fs from "fs";
import url from "url";
// === Paramètres ===
const PORT = 8080;
const FETCH_URL = "https://domain.tld/online-orders.php";
const FETCH_INTERVAL = 1000; // toutes les 1 seconde
const VALID_TOKENS = ["ABC123", "ESP32TOKEN", "MYSECRET"]; // liste des jetons autorisés
// === SSL ===
const options = {
key: fs.readFileSync("/etc/letsencrypt/live/ws.domain.tld/privkey.pem"),
cert: fs.readFileSync("/etc/letsencrypt/live/ws.domain.tld/fullchain.pem")
};
// === Création du serveur ===
const app = express();
const server = https.createServer(options, app);
const wss = new WebSocketServer({ server });
let latestData = null;
// === Fonction pour l'obtention périodique de JSON ===
async function updateData() {
try {
const res = await fetch(FETCH_URL);
const json = await res.json();
latestData = json;
// Envoyer à tous les clients actifs
wss.clients.forEach(client => {
if (client.readyState === 1) { // WebSocket.OPEN
client.send(JSON.stringify(json));
}
});
} catch (err) {
console.error("Erreur lors de la récupération des données :", err);
}
}
setInterval(updateData, FETCH_INTERVAL);
updateData();
// === Gestion des connexions ===
wss.on("connection", (ws, req) => {
const query = url.parse(req.url, true).query;
const token = query.token;
if (!VALID_TOKENS.includes(token)) {
console.log(`❌ Connexion rejetée avec le jeton : ${token}`);
ws.close(4001, "Invalid token");
return;
}
console.log(`✅ Client connecté avec le jeton : ${token}`);
// Envoyer le dernier état
if (latestData) ws.send(JSON.stringify(latestData));
ws.on("close", () => {
console.log(`🔌 Client (${token}) déconnecté`);
});
});
server.listen(PORT, () => {
console.log(`✅ Serveur WSS démarré sur le port ${PORT}`);
});
4.1 Connexion avec autorisation par jeton
Pour un exemple utilisant HTML + JS, changez simplement la ligne en :
<input id="wsUrl" value="wss://ws.domain.tld:8080/?token=ABC123">
5. En conclusion
La configuration est terminée ; maintenant tous les clients du serveur WebSocket reçoivent des données en temps réel en utilisant le chiffrement. Dans notre exemple, nous utilisons un script PHP comme intermédiaire entre le système (pour obtenir des données via une API) et le serveur WebSocket (pour les transmettre).
Le script PHP lui-même peut être supprimé, et une requête API peut être utilisée directement dans server.js avec une autorisation API au système, ce qui serait encore plus efficace. Cependant, l'article deviendrait alors assez volumineux, car il serait nécessaire de couvrir le stockage sécurisé de la clé API dans un fichier .env et l'installation du paquet npm install dotenv pour travailler avec les données du fichier de configuration.

