WebSocket en TypeScript : techniques avancées pour les applications temps réel
WebSocket fournit une connexion bidirectionnelle persistante entre le client et le serveur, ce qui le rend idéal pour les applications temps réel : chats, notifications, édition collaborative et jeux en ligne. Dans cet article, nous explorerons des techniques avancées de travail avec WebSocket en TypeScript, allant au-delà d'un simple serveur echo.
1. Architecture avec gestion des salles (Rooms)
Dans les applications réelles, la diffusion globale à tous les clients est rarement utilisée. Il est beaucoup plus courant de diviser logiquement les clients en salles (par exemple, salles de chat ou sessions de jeu). Voyons une implémentation en TypeScript utilisant la bibliothèque ws.
import { WebSocketServer, WebSocket } from 'ws';
interface Room { id: string; clients: Set<WebSocket>;}
class RoomManager { private rooms: Map<string, Room> = new Map();
createRoom(roomId: string): void { if (!this.rooms.has(roomId)) { this.rooms.set(roomId, { id: roomId, clients: new Set() }); } }
joinRoom(roomId: string, client: WebSocket): void { this.createRoom(roomId); this.rooms.get(roomId)!.clients.add(client); client.send(JSON.stringify({ type: 'room_joined', roomId })); }
leaveRoom(roomId: string, client: WebSocket): void { const room = this.rooms.get(roomId); if (room) { room.clients.delete(client); if (room.clients.size === 0) { this.rooms.delete(roomId); } } }
broadcastToRoom(roomId: string, message: object, sender?: WebSocket): void { const room = this.rooms.get(roomId); if (room) { const data = JSON.stringify(message); room.clients.forEach(client => { if (client.readyState === WebSocket.OPEN && client !== sender) { client.send(data); } }); } }}
const wss = new WebSocketServer({ port: 8080 });const roomManager = new RoomManager();
wss.on('connection', (ws: WebSocket) => { ws.on('message', (raw: Buffer) => { try { const msg = JSON.parse(raw.toString()); switch (msg.type) { case 'join': roomManager.joinRoom(msg.roomId, ws); break; case 'leave': roomManager.leaveRoom(msg.roomId, ws); break; case 'message': roomManager.broadcastToRoom(msg.roomId, { type: 'chat', user: msg.user, text: msg.text }, ws); break; } } catch (e) { ws.send(JSON.stringify({ type: 'error', message: 'Invalid JSON' })); } });
ws.on('close', () => { // Nettoyage : suppression du client de toutes les salles roomManager.leaveRoom('*', ws); });});Ce code illustre une gestion sécurisée des salles : création à la demande, nettoyage automatique des salles vides et exclusion de l'expéditeur de la diffusion.
2. Authentification et gestion d'état
Les connexions WebSocket doivent être authentifiées. Une approche avancée consiste à vérifier le jeton lors de l'établissement de la connexion et à maintenir une session.
import { IncomingMessage } from 'http';import { WebSocketServer, WebSocket } from 'ws';
interface AuthenticatedSocket extends WebSocket { userId?: string; username?: string;}
const wss = new WebSocketServer({ port: 8080, verifyClient: (info: { req: IncomingMessage }, callback: (res: boolean) => void) => { const token = extractToken(info.req.url || ''); if (token && validateToken(token)) { callback(true); } else { callback(false); } }});
function extractToken(url: string): string | null { const params = new URLSearchParams(url.split('?')[1] || ''); return params.get('token');}
function validateToken(token: string): boolean { // Une véritable vérification JWT ou autre jeton doit être effectuée ici return token.length > 10;}
wss.on('co