228 votes

Envoyer un message à un client spécifique avec socket.io et node.js

Je travaille avec socket.io et node.js et jusqu'à présent, ça semble assez bien, mais je ne sais pas comment envoyer un message du serveur à un client spécifique, quelque chose comme ceci :

client.send(message, receiverSessionId)

Mais ni les méthodes .send() ni .broadcast() ne semblent répondre à mon besoin.

Ce que j'ai trouvé comme solution possible, c'est que la méthode .broadcast() accepte en deuxième paramètre un tableau de SessionIds auxquels ne pas envoyer le message, donc je pourrais passer un tableau avec tous les SessionIds connectés au serveur à ce moment-là, sauf celui à qui je souhaite envoyer le message, mais je sens qu'il doit y avoir une meilleure solution.

Des idées ?

195voto

Epeli Points 5475

La réponse de Ivo Wetzel ne semble plus être valable dans Socket.io 0.9.

En bref, vous devez maintenant sauvegarder le socket.id et utiliser io.sockets.socket(savedSocketId).emit(...) pour envoyer des messages.

Voici comment j'ai fait fonctionner ceci dans un serveur Node.js en grappe :

Tout d'abord, vous devez définir Redis comme magasin afin que les messages puissent passer entre les processus :

var express = require("express");
var redis = require("redis");
var sio = require("socket.io");

var client = redis.createClient()
var app = express.createServer();
var io = sio.listen(app);

io.set("store", new sio.RedisStore);

// Dans cet exemple, nous avons un socket client maître qui reçoit des messages des autres.

io.sockets.on('connection', function(socket) {

  // Promouvoir ce socket en tant que maître
  socket.on("Je suis le maître", function() {

    // Sauvegarder l'identifiant du socket dans Redis pour que tous les processus puissent y accéder.
    client.set("mastersocket", socket.id, function(err) {
      if (err) throw err;
      console.log("Le socket maître est maintenant" + socket.id);
    });
  });

  socket.on("message au maître", function(msg) {

    // Récupérer l'identifiant du socket depuis Redis
    client.get("mastersocket", function(err, socketId) {
      if (err) throw err;
      io.sockets.socket(socketId).emit(msg);
    });
  });

});

J'ai omis le code de regroupement ici, car cela rendrait les choses plus encombrées, mais c'est trivial à ajouter. Il suffit d'ajouter tout au code du travailleur. Plus de documents ici http://nodejs.org/api/cluster.html

4 votes

Merci, c'était utile. J'ai juste utilisé un tableau à la place: io.of('/mynamespace').sockets[socketID].emit(...) (je ne sais pas si c'est parce que j'utilise un espace de noms)

0 votes

Sur un environnement clusterisé, comment m'assurer que le processus correct auquel le socket appartient envoie le message ?

0 votes

Que diriez-vous d'une session collante grâce à NGINX ou à HAProxy @Gal Ben-Haim?

133voto

Matic Kogovšek Points 1166

Pour votre commodité, chaque socket rejoint automatiquement une salle identifiée par son identifiant (voir Socket#id). Cela facilite la diffusion de messages à d'autres sockets :

io.on("connection", (socket) => {
  socket.on("say to someone", (id, msg) => {
    // envoyer un message privé au socket avec l'identifiant donné
    socket.to(id).emit("my message", msg);
  });
});

docs: https://socket.io/docs/v4/server-api/#socketjoinroom

9 votes

C'est la meilleure réponse et fonctionne avec les nouvelles versions de socket.io. Il y a une belle feuille de triche ici: stackoverflow.com/questions/10058226/…

6 votes

Notez que ceci est un type de diffusion d'événement. Donc, si vous essayez de définir un rappel sur ceci, vous aurez une erreur. Si vous avez besoin d'envoyer un événement à un socket spécifique avec un rappel, utilisez la réponse de @PHPthinking et utilisez io.sockets.connected[socketid].emit();. Testé avec 1.4.6.

0 votes

Mais j'ai reçu cette erreur : io.to["JgCoFX9AiCND_ZhdAAAC"].emit("socketFromServer", info); ^ TypeError: Impossible de lire la propriété 'emit' de non défini

100voto

Lucio Paiva Points 368

La manière la plus simple et la plus élégante

vérifié fonctionnant avec socket.io v3.1.1

C'est aussi simple que :

client.emit("votre message");

Et c'est tout. D'accord, mais comment ça fonctionne?

Exemple minimal de fonctionnement

Voici un exemple d'interaction client-serveur simple où chaque client reçoit régulièrement un message contenant un numéro de séquence. Il y a un numéro de séquence unique pour chaque client et c'est là que le "J'ai besoin d'envoyer un message à un client particulier" entre en jeu.

Serveur

server.js

const
    {Server} = require("socket.io"),
    server = new Server(8000);

let
    sequenceNumberByClient = new Map();

// événement déclenché chaque fois qu'un nouveau client se connecte :
server.on("connection", (socket) => {
    console.info(`Client connecté [id=${socket.id}]`);
    // initialise le numéro de séquence de ce client
    sequenceNumberByClient.set(socket, 1);

    // lorsque le socket se déconnecte, le supprimer de la liste :
    socket.on("disconnect", () => {
        sequenceNumberByClient.delete(socket);
        console.info(`Client parti [id=${socket.id}]`);
    });
});

// envoie à chaque client son numéro de séquence actuel
setInterval(() => {
    for (const [client, sequenceNumber] of sequenceNumberByClient.entries()) {
        client.emit("seq-num", sequenceNumber);
        sequenceNumberByClient.set(client, sequenceNumber + 1);
    }
}, 1000);

Le serveur commence à écouter sur le port 8000 pour les connexions entrantes. Dès qu'une nouvelle connexion est établie, ce client est ajouté à une carte qui suit son numéro de séquence. Le serveur écoute également l'événement déconnexion pour supprimer le client de la carte lorsqu'il part.

Chaque seconde, une minuterie est déclenchée. Lorsqu'elle est déclenchée, le serveur parcourt la carte et envoie un message à chaque client avec son numéro de séquence actuel, en l'incrémentant juste après. C'est tout. Facile comme bonjour.

Client

La partie client est encore plus simple. Il se connecte simplement au serveur et écoute le message seq-num, l'imprimant dans la console à chaque fois qu'il arrive.

client.js

const
    io = require("socket.io-client"),
    ioClient = io.connect("http://localhost:8000");

ioClient.on("seq-num", (msg) => console.info(msg));

Exécution de l'exemple

Installez les bibliothèques requises :

npm install socket.io@3.1.1 socket.io-client@3.1.1

Exécutez le serveur :

node server

Ouvrez d'autres fenêtres de terminal et lancez autant de clients que vous le souhaitez en exécutant :

node client

J'ai également préparé un gist avec le code complet ici.

0 votes

Est-ce que le port 8000 est la norme pour socketio en production?

1 votes

Désolé d'avoir pris autant de temps pour répondre, @ingo. 8000 est un port communément utilisé par la communauté Node lors de tests de websockets ou de serveurs HTTP (3000 est aussi courant). Bien sûr, vous pourriez l'utiliser en production, mais je ne suis pas sûr que ce soit courant... de toute façon, vous pouvez vraiment utiliser n'importe quel port, tant que vos passerelles/load balancers/etc sont prêts pour cela.

0 votes

Je suis programmeur en Python/PHP et je découvre les sockets et Node. Ma question est : pourquoi devons-nous incrémenter le numéro de séquence du même utilisateur chaque seconde ? Est-ce juste pour la démonstration?

97voto

Ivo Wetzel Points 27802

Vous devez attraper le client pour cela (surprise), vous pouvez aller de manière simple:

var io = io.listen(server);
io.clients[sessionID].send()

Il se peut que cela change, donc utilisez l'option ci-dessus avec prudence

Ou vous pouvez suivre les clients vous-même, en les ajoutant à votre propre objet clients dans l'auditeur connection et en les supprimant dans l'auditeur disconnect.

Je choisirais le dernier, car selon votre application, vous souhaiterez peut-être avoir plus d'état sur les clients de toute façon, donc quelque chose comme clients[id] = {conn: clientConnect, data: {...}} pourrait faire l'affaire.

9 votes

Ivo, pouvez-vous indiquer un exemple plus complet ou élaborer un peu? Je suis impatient de comprendre cette approche, mais je ne suis pas sûr de reconnaître les variables/objets que vous utilisez dans cet exemple. Dans clients[id] = {conn: clientConnect, data: {...}}, clients[id] fait-il partie de l'objet io tel que vu dans io.clients[sessionID] ci-dessus? De plus, qu'est-ce que l'objet clientConnect? Merci.

0 votes

@ivo-wetzel salut, pourriez-vous m'aider sur ce sujet? stackoverflow.com/questions/38817680/…

42voto

DecoderNT Points 579

Vous pouvez utiliser

//envoyer un message uniquement au client expéditeur

socket.emit('message', 'vérifiez ceci');

//ou vous pouvez envoyer à tous les auditeurs, y compris l'expéditeur

io.emit('message', 'vérifiez ceci');

//envoyer à tous les auditeurs sauf l'expéditeur

socket.broadcast.emit('message', 'c'est un message');

//ou vous pouvez l'envoyer à une salle

socket.broadcast.to('salle de discussion').emit('message', 'c'est le message pour tous');

1 votes

A fonctionné pour moi, j'ai également dû ajouter "const socket = require('socket.io')(http);" dans mon fichier server.js.

0 votes

Salut, j'espère que tu vas bien. Je sais que cela fait un moment depuis que tu as répondu à cette question, mais ta réponse est celle qui a le plus de sens pour moi. Pourrais-tu jeter un coup d'œil à ma question. stackoverflow.com/questions/65377025/…

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X