Contactez-nous

Envoi et réception de messages en temps réel

Apprenez les mecanismes d'envoi (unicast, broadcast) et de reception de messages temps reel avec WebSockets en Node.js, via les bibliotheques ws et Socket.IO.

Le coeur de l'interaction : L'échange de messages

Une fois qu'un serveur WebSocket est en place et qu'un client s'y est connecté avec succès, l'étape suivante, et la plus fondamentale, est l'échange de messages. C'est ce flux bidirectionnel d'informations qui permet de construire des applications temps réel interactives. Le serveur doit pouvoir envoyer des informations aux clients (soit à un client spécifique, soit à plusieurs), et les clients doivent pouvoir envoyer des informations au serveur.

Les mécanismes précis pour envoyer et recevoir des messages varient légèrement selon la bibliothèque utilisée (`ws` ou `Socket.IO`), mais les concepts de base restent les mêmes. Il s'agit d'utiliser les objets représentant la connexion pour invoquer des méthodes d'envoi et d'enregistrer des écouteurs pour réagir aux messages entrants.

Communication avec `ws` : Manipulation directe

Avec la bibliothèque `ws`, la communication est assez directe et de bas niveau.

Recevoir un message d'un client :

Lorsqu'un client envoie un message, l'événement `'message'` est déclenché sur l'objet `wsClient` représentant la connexion de ce client spécifique. Le message reçu est généralement un objet `Buffer` (pour les données binaires ou par défaut) ou une chaîne de caractères.

// Dans le gestionnaire wss.on('connection', (wsClient) => { ... });

wsClient.on('message', (message) => {
  // 'message' est souvent un Buffer.
  console.log('Type de message reçu:', typeof message, Buffer.isBuffer(message));
  
  try {
    // Si vous attendez du texte (ex: JSON stringifié)
    const messageText = message.toString('utf8'); 
    console.log('Message texte reçu:', messageText);

    // Si c'est du JSON, vous devez le parser
    // const jsonData = JSON.parse(messageText);
    // console.log('Données JSON reçues:', jsonData);

    // Traitez le message reçu...

  } catch (error) {
    console.error('Erreur lors du traitement du message:', error);
    // Gérer l'erreur (ex: format de message invalide)
  }
});

Envoyer un message à un client spécifique :

Utilisez la méthode `send()` sur l'objet `wsClient` correspondant au destinataire.

// Dans le gestionnaire wss.on('connection', (wsClient) => { ... });

// Envoyer une chaîne simple
wsClient.send('Message privé pour vous !');

// Envoyer des données JSON (doit être stringifié)
const data = { type: 'info', payload: 'Connexion réussie' };
wsClient.send(JSON.stringify(data));

// Envoyer des données binaires (Buffer)
// const binaryData = Buffer.from([...]);
// wsClient.send(binaryData);

Envoyer un message à tous les clients connectés (Broadcasting) :

La bibliothèque `ws` ne fournit pas de méthode de diffusion intégrée. Vous devez itérer manuellement sur l'ensemble des clients connectés, accessible via `wss.clients` (qui est un `Set`), et envoyer le message à chacun d'eux, en vérifiant leur état de connexion.

// Fonction pour diffuser un message à tous (peut être appelée depuis un .on('message'))
function broadcast(data, senderClient) {
  const messageToSend = (typeof data === 'string') ? data : JSON.stringify(data);

  console.log(`Diffusion du message : ${messageToSend}`);
  wss.clients.forEach((client) => {
    // Optionnel: Ne pas renvoyer à l'expéditeur
    // if (client === senderClient) return;

    // Vérifier si le client est prêt à recevoir des messages
    if (client.readyState === WebSocket.OPEN) {
      client.send(messageToSend);
    } else {
      console.warn(`Client non prêt (readyState: ${client.readyState}), message non envoyé à ce client.`);
    }
  });
}

// Exemple d'utilisation dans le .on('message')
wsClient.on('message', (message) => {
  const messageText = message.toString();
  console.log('Message reçu:', messageText);
  // Diffuser le message reçu à tous
  broadcast(`Nouveau message : ${messageText}`, wsClient);
});

Communication avec `Socket.IO` : Modèle événementiel

`Socket.IO` utilise un modèle basé sur des événements nommés personnalisés, ce qui le rend plus structuré et flexible.

Recevoir un message (événement) d'un client :

Utilisez la méthode `socket.on('nomEvenement', callback)` pour enregistrer un écouteur pour un événement spécifique envoyé par le client. Le callback reçoit les données envoyées par le client (qui peuvent être de n'importe quel type sérialisable en JSON, `Socket.IO` gère la sérialisation/désérialisation). Il peut aussi recevoir une fonction d'accusé de réception (acknowledgement).

// Dans le gestionnaire io.on('connection', (socket) => { ... });

// Ecouter l'événement 'chat message'
socket.on('chat message', (msg, ackCallback) => {
  console.log(`Message '${msg}' reçu de ${socket.id}`);
  // Traiter le message...
  
  // Envoyer un accusé de réception au client (optionnel)
  if (ackCallback) {
    ackCallback({ status: 'ok', messageRecu: msg });
  }
});

// Ecouter un autre événement avec des données structurées
socket.on('update profile', (profileData) => {
  console.log(`Données de profil reçues de ${socket.id}:`, profileData);
  // Mettre à jour le profil utilisateur...
});

Envoyer un message (événement) à un client spécifique :

Utilisez la méthode `socket.emit('nomEvenement', data)` sur l'objet `socket` représentant le client destinataire.

// Dans le gestionnaire io.on('connection', (socket) => { ... });

// Envoyer un événement 'notification' uniquement à ce client
socket.emit('notification', { type: 'succes', message: 'Profil mis à jour !' });

Envoyer un message à tous les clients connectés (Broadcasting) :

`Socket.IO` facilite grandement la diffusion :

  • A tous (y compris l'expéditeur) : Utilisez `io.emit('nomEvenement', data)` sur l'instance principale du serveur `io`.
  • A tous sauf l'expéditeur : Utilisez `socket.broadcast.emit('nomEvenement', data)` sur l'objet `socket` de l'expéditeur.
// Dans le gestionnaire io.on('connection', (socket) => { ... });

socket.on('chat message', (msg) => {
  console.log(`Message '${msg}' reçu de ${socket.id}`);
  
  // Option A: Diffuser à tous, y compris l'expéditeur
  // io.emit('chat message', { user: socket.id, text: msg }); 

  // Option B: Diffuser à tous les autres
  socket.broadcast.emit('chat message', { user: socket.id, text: msg }); 
});

// Envoyer un message système à tous lors d'une nouvelle connexion
io.emit('info systeme', 'Un nouvel utilisateur est arrivé !');

Envoyer un message à une "Room" (Salle) :

Si vous avez organisé les clients en salles avec `socket.join('nomRoom')`, vous pouvez cibler une salle spécifique avec `io.to('nomRoom').emit(...)`.

// Envoyer un message uniquement aux clients dans la room 'nouvelles'
io.to('nouvelles').emit('breaking news', { titre: '...', contenu: '...' });

Gestion des formats de données (JSON, Binaire)

Le type de données que vous échangez est important.

  • JSON : C'est le format le plus courant pour les applications web. Les objets JavaScript sont facilement sérialisables/désérialisables en JSON.
    • Avec `ws`, vous devez explicitement utiliser `JSON.stringify()` avant `send()` et `JSON.parse()` (dans un `try...catch`) après réception d'un message textuel.
    • Avec `Socket.IO`, la sérialisation/désérialisation JSON est gérée automatiquement lorsque vous passez des objets ou des tableaux à `emit()` ou les recevez dans un `on()`.
  • Texte simple : Peut être envoyé directement avec les deux bibliothèques.
  • Binaire : Les WebSockets supportent l'envoi de données binaires (comme des `Buffer` Node.js, `ArrayBuffer`, `Blob` côté client).
    • Avec `ws`, vous pouvez passer directement un `Buffer` à `send()`.
    • Avec `Socket.IO`, vous pouvez également émettre des données binaires, qui seront gérées de manière appropriée par la bibliothèque côté client et serveur.

Le choix du format dépend de l'application. JSON est idéal pour les données structurées, tandis que le binaire est plus efficace pour les fichiers, les flux audio/vidéo ou les données de jeux.

En maîtrisant ces mécanismes d'envoi et de réception, ainsi que la gestion des formats de données, vous pouvez construire la logique de communication au coeur de vos applications Node.js temps réel, que vous optiez pour la flexibilité de bas niveau de `ws` ou les fonctionnalités riches de `Socket.IO`.