Comprendre le protocole WebSocket de zéro et construire un système de chat complet : connexion, utilisateurs en mémoire, messages privés et groupes — sans base de données.
Le chat global est le cas le plus simple : un message envoyé par un utilisateur est diffusé à tous les autres connectés. C'est le broadcast pur.
chatOn ajoute un case dans handleMessage :
case 'chat':
handleChat(socket, msg)
breakfunction handleChat(socket, msg) {
const content = (msg.content || '').trim()
if (!content) return
const message = {
type: 'chat',
from: socket.username,
content,
at: new Date().toISOString(),
}
// Diffuser à tous — y compris l'expéditeur pour confirmer l'envoi
broadcast(message)
}L'expéditeur reçoit aussi le message via le broadcast global — c'est la confirmation que le serveur l'a bien reçu et redistribué. Le client peut ainsi afficher ses propres messages dans l'interface de la même façon que ceux des autres.
function sendChat(content) {
ws.send(JSON.stringify({ type: 'chat', content }))
}
ws.onmessage = (event) => {
const msg = JSON.parse(event.data)
switch (msg.type) {
case 'chat':
console.log(`[${msg.at.slice(11, 19)}] ${msg.from} : ${msg.content}`)
break
case 'system':
console.log(`--- ${msg.content} ---`)
break
case 'registered':
console.log(msg.content)
console.log('En ligne :', msg.users.join(', '))
break
case 'error':
console.error('Erreur :', msg.content)
break
}
}Deux onglets, alice et bob inscrits.
Alice envoie :
ws.send(JSON.stringify({ type: 'chat', content: 'Bonjour tout le monde !' }))Les deux onglets reçoivent :
[12:34:01] alice : Bonjour tout le monde !À ce stade, tous les messages serveur → client ont la même structure :
{
type: string, // 'chat' | 'system' | 'registered' | 'error' | 'list' | ...
from?: string, // username de l'expéditeur (si applicable)
content: string, // contenu lisible
at?: string, // timestamp ISO (si applicable)
}Garder cette cohérence facilite le traitement côté client — un seul switch (msg.type) couvre tout.
Sans validation, un client peut envoyer un message de 10 Mo. Simple à bloquer :
function handleChat(socket, msg) {
const content = (msg.content || '').trim()
if (!content) return
if (content.length > 500) {
socket.send(JSON.stringify({ type: 'error', content: 'Message trop long (500 caractères max)' }))
return
}
broadcast({
type: 'chat',
from: socket.username,
content,
at: new Date().toISOString(),
})
}Si on veut envoyer les derniers messages aux nouveaux arrivants :
const history = [] // tableau des N derniers messages
function handleChat(socket, msg) {
const content = (msg.content || '').trim()
if (!content || content.length > 500) return
const message = {
type: 'chat',
from: socket.username,
content,
at: new Date().toISOString(),
}
history.push(message)
if (history.length > 50) history.shift() // garder les 50 derniers
broadcast(message)
}Et dans registerUser, après l'inscription :
// Envoyer l'historique au nouveau connecté
history.forEach((m) => socket.send(JSON.stringify(m)))Le nouvel utilisateur voit les 50 derniers messages dès sa connexion — sans base de données.
Le broadcast global fonctionne. Chapitre suivant : messages privés, envoyés à un seul destinataire.