WebSockets et Socket.IO : Guide du Temps Réel
Mark Toledo
1 janvier 2025

Les applications web modernes nécessitent souvent une communication en temps réel : chat, notifications, mises à jour live, jeux multijoueurs. WebSocket et Socket.IO sont les technologies de choix pour ces cas d'usage.
WebSocket vs Socket.IO
WebSocket natif
WebSocket est un protocole de communication bidirectionnelle sur une connexion TCP persistante. Supporté nativement par les navigateurs.
Socket.IO
Socket.IO est une bibliothèque qui ajoute des fonctionnalités sur WebSocket :
- Fallback automatique si WebSocket non disponible
- Reconnexion automatique
- Rooms et namespaces pour organiser les connexions
- Acknowledgements pour confirmer la réception
- Broadcasting simplifié
Installation
# Backend
npm install socket.io express
# Frontend (optionnel si CDN)
npm install socket.io-client
Serveur basique
// server.js
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"]
}
});
// Servir les fichiers statiques
app.use(express.static('public'));
// Gestion des connexions
io.on('connection', (socket) => {
console.log('Utilisateur connecté:', socket.id);
// Écouter un événement
socket.on('message', (data) => {
console.log('Message reçu:', data);
// Envoyer à tous les clients
io.emit('message', {
id: socket.id,
text: data.text,
timestamp: new Date()
});
});
// Déconnexion
socket.on('disconnect', () => {
console.log('Utilisateur déconnecté:', socket.id);
});
});
httpServer.listen(3000, () => {
console.log('Serveur démarré sur http://localhost:3000');
});
Client basique
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Chat Socket.IO</title>
</head>
<body>
<div id="messages"></div>
<input type="text" id="input" placeholder="Votre message..." />
<button onclick="envoyerMessage()">Envoyer</button>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
const messagesDiv = document.getElementById('messages');
const input = document.getElementById('input');
// Recevoir les messages
socket.on('message', (data) => {
const p = document.createElement('p');
p.textContent = `${data.id}: ${data.text}`;
messagesDiv.appendChild(p);
});
// Envoyer un message
function envoyerMessage() {
const text = input.value.trim();
if (text) {
socket.emit('message', { text });
input.value = '';
}
}
// Envoyer avec Entrée
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') envoyerMessage();
});
</script>
</body>
</html>
Rooms (salons)
Les rooms permettent de grouper les connexions :
// Serveur
io.on('connection', (socket) => {
// Rejoindre une room
socket.on('rejoindre', (room) => {
socket.join(room);
console.log(`${socket.id} a rejoint ${room}`);
// Notifier la room
socket.to(room).emit('notification', {
message: `Un utilisateur a rejoint ${room}`
});
});
// Quitter une room
socket.on('quitter', (room) => {
socket.leave(room);
socket.to(room).emit('notification', {
message: 'Un utilisateur a quitté le salon'
});
});
// Message dans une room spécifique
socket.on('messageRoom', ({ room, text }) => {
io.to(room).emit('message', {
user: socket.id,
text,
room
});
});
});
// Client
socket.emit('rejoindre', 'salon-general');
socket.emit('messageRoom', {
room: 'salon-general',
text: 'Bonjour à tous !'
});
Namespaces
Les namespaces séparent la logique de l'application :
// Serveur
const chatNsp = io.of('/chat');
const notifNsp = io.of('/notifications');
chatNsp.on('connection', (socket) => {
console.log('Connexion au chat:', socket.id);
socket.on('message', (data) => {
chatNsp.emit('message', data);
});
});
notifNsp.on('connection', (socket) => {
console.log('Connexion aux notifications:', socket.id);
// Envoyer des notifications périodiques
const interval = setInterval(() => {
socket.emit('notification', {
type: 'info',
message: 'Nouvelle notification'
});
}, 30000);
socket.on('disconnect', () => clearInterval(interval));
});
// Client
const chatSocket = io('/chat');
const notifSocket = io('/notifications');
chatSocket.on('message', (data) => {
console.log('Chat:', data);
});
notifSocket.on('notification', (data) => {
console.log('Notification:', data);
});
Acknowledgements
Confirmer la réception des messages :
// Client - envoyer avec callback
socket.emit('message', { text: 'Hello' }, (response) => {
if (response.status === 'ok') {
console.log('Message délivré !');
}
});
// Serveur - répondre au callback
socket.on('message', (data, callback) => {
// Traiter le message...
saveMessage(data);
// Confirmer la réception
callback({ status: 'ok', id: data.id });
});
Exemple complet : Chat avec utilisateurs
// server.js
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer);
app.use(express.static('public'));
// Stockage des utilisateurs connectés
const utilisateurs = new Map();
io.on('connection', (socket) => {
// Connexion d'un utilisateur
socket.on('login', (pseudo) => {
utilisateurs.set(socket.id, {
id: socket.id,
pseudo,
connecteA: new Date()
});
// Notifier tout le monde
io.emit('utilisateurs', Array.from(utilisateurs.values()));
io.emit('systeme', `${pseudo} a rejoint le chat`);
});
// Message
socket.on('message', (text) => {
const user = utilisateurs.get(socket.id);
if (user) {
io.emit('message', {
id: Date.now(),
pseudo: user.pseudo,
text,
timestamp: new Date()
});
}
});
// Indicateur de frappe
socket.on('typing', (isTyping) => {
const user = utilisateurs.get(socket.id);
if (user) {
socket.broadcast.emit('typing', {
pseudo: user.pseudo,
isTyping
});
}
});
// Déconnexion
socket.on('disconnect', () => {
const user = utilisateurs.get(socket.id);
if (user) {
utilisateurs.delete(socket.id);
io.emit('utilisateurs', Array.from(utilisateurs.values()));
io.emit('systeme', `${user.pseudo} a quitté le chat`);
}
});
});
httpServer.listen(3000);
Intégration avec Vue.js
<!-- composables/useSocket.js -->
<script>
import { io } from 'socket.io-client'
import { ref, onMounted, onUnmounted } from 'vue'
export function useSocket(url = '/') {
const socket = ref(null)
const connected = ref(false)
const messages = ref([])
onMounted(() => {
socket.value = io(url)
socket.value.on('connect', () => {
connected.value = true
})
socket.value.on('disconnect', () => {
connected.value = false
})
socket.value.on('message', (msg) => {
messages.value.push(msg)
})
})
onUnmounted(() => {
socket.value?.disconnect()
})
function send(event, data) {
socket.value?.emit(event, data)
}
return { socket, connected, messages, send }
}
</script>
Bonnes pratiques
- Authentification : Vérifiez l'identité lors de la connexion
- Rate limiting : Limitez le nombre de messages par seconde
- Validation : Validez toutes les données reçues
- Gestion des erreurs : Gérez les déconnexions gracieusement
- Scaling : Utilisez Redis adapter pour plusieurs serveurs
// Exemple d'authentification
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (verifyToken(token)) {
socket.user = getUserFromToken(token);
next();
} else {
next(new Error('Authentification requise'));
}
});
Conclusion
Socket.IO simplifie considérablement le développement d'applications temps réel. Avec les rooms, namespaces et acknowledgements, vous pouvez créer des expériences interactives sophistiquées. Combinez-le avec Vue.js ou React pour des applications modernes et réactives.
