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


191

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

client.send(message, receiverSessionId)

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

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

Des idées?

Réponses:


95

Eh bien, vous devez saisir le client pour cela (surprise), vous pouvez soit aller de la manière simple:

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

Ce qui peut casser, j'en doute, mais c'est toujours une possibilité qui io.clientspourrait être modifiée, alors utilisez ce qui précède avec prudence

Ou vous gardez une trace des clients vous-même, donc vous les ajoutez à votre propre clientsobjet dans l' connectionauditeur et les supprimez dans l' disconnectauditeur.

J'utiliserais ce dernier, car en fonction de votre application, vous voudrez peut-être avoir plus d'état sur les clients de toute façon, donc quelque chose comme clients[id] = {conn: clientConnect, data: {...}}ça pourrait faire le travail.


6
Ivo, pouvez-vous citer un exemple plus complet ou élaborer un peu? J'ai hâte 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: {...}}, les clients [id] font-ils partie de l'objet io comme vu dans io.clients [sessionID] ci-dessus? Quel est également l'objet clientConnect? Merci.
AndrewHenderson

@ ivo-wetzel salut, pourriez-vous me helo sur ce sujet? stackoverflow.com/questions/38817680/…
mahdi pishguy

178

La réponse d'Ivo Wetzel ne semble plus être valide dans Socket.io 0.9.

En bref, vous devez maintenant enregistrer le socket.idet l'utiliser io.sockets.socket(savedSocketId).emit(...) pour lui envoyer des messages.

Voici comment cela fonctionne sur le serveur Node.js en cluster:

Vous devez d'abord définir le magasin Redis comme magasin afin que les messages puissent traverser 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);


// In this example we have one master client socket 
// that receives messages from others.

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

  // Promote this socket as master
  socket.on("I'm the master", function() {

    // Save the socket id to Redis so that all processes can access it.
    client.set("mastersocket", socket.id, function(err) {
      if (err) throw err;
      console.log("Master socket is now" + socket.id);
    });
  });

  socket.on("message to master", function(msg) {

    // Fetch the socket id from Redis
    client.get("mastersocket", function(err, socketId) {
      if (err) throw err;
      io.sockets.socket(socketId).emit(msg);
    });
  });

});

J'ai omis le code de clustering ici, car cela rend cela plus encombré, mais c'est trivial à ajouter. Ajoutez simplement tout au code de travail. Plus de documentation ici http://nodejs.org/api/cluster.html


4
Merci, c'était utile. Je devais juste utiliser 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)
Adrien Schuler

sur un environnement en cluster, comment puis-je m'assurer que le processus correct auquel appartient le socket envoie le message?
Gal Ben-Haim

Que diriez-vous d'une session collante gracieuseté de NGINX ou HAProxy @Gal Ben-Haim?
matanster le

var holder = nouveau socketio.RedisStore; ^ TypeError: undefined n'est pas une fonction sur Object. <anonymous> (C: \ Users \ Dev \ Desktop \ nouty-server \ server.js: 108: 14) sur Module._compile (module.js: 460: 26) at Object.Module._extensions..js (module.js: 478: 10) à Module.load (module.js: 355: 32) à Function.Module._load (module.js: 310: 12) à Function.Module. runMain (module.js: 501: 10) au démarrage (node.js: 129: 16) à node.js: 814: 3
Lucas Bertollo

1
io.sockets.socket(socket_id)est supprimé dans socket.io 1.0. github.com/socketio/socket.io/issues/1618#issuecomment-46151246
ImMathan

98

chaque socket rejoint une pièce avec un identifiant de socket pour un nom, vous pouvez donc simplement

io.to(socket#id).emit('hey')

docs: http://socket.io/docs/rooms-and-namespaces/#default-room

À votre santé


7
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/…
blented le

4
notez qu'il s'agit d'un type d'émission d'événement. donc si vous essayez de définir un rappel à ceci, vous aurez une erreur. si vous avez besoin d'envoyer un événement à une socket spécifique avec un rappel, utilisez la réponse de @ PHPthinking et utilisez io.sockets.connected[socketid].emit();. Testé avec 1.4.6.
tbutcaru

Mais j'ai eu cette erreur: io.to ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServe‌ r", info); ^ TypeError: Impossible de lire la propriété 'emit' d'undefined
Raz

85

La solution la plus simple et la plus élégante

C'est aussi simple que:

client.emit("your message");

Et c'est tout.

Mais comment? Donne moi un exemple

Ce dont nous avons tous besoin, c'est en fait un exemple complet, et c'est ce qui suit. Ceci est testé avec la version la plus récente de socket.io (2.0.3) et utilise également du Javascript moderne (que nous devrions tous utiliser maintenant).

L'exemple est composé de deux parties: un serveur et un client. Chaque fois qu'un client se connecte, il commence à recevoir du serveur un numéro de séquence périodique. Une nouvelle séquence est lancée pour chaque nouveau client, le serveur doit donc les suivre individuellement. C'est là qu'entre en jeu le "J'ai besoin d'envoyer un message à un client particulier" . Le code est très simple à comprendre. Voyons ça.

Serveur

server.js

const
    io = require("socket.io"),
    server = io.listen(8000);

let
    sequenceNumberByClient = new Map();

// event fired every time a new client connects:
server.on("connection", (socket) => {
    console.info(`Client connected [id=${socket.id}]`);
    // initialize this client's sequence number
    sequenceNumberByClient.set(socket, 1);

    // when socket disconnects, remove it from the list:
    socket.on("disconnect", () => {
        sequenceNumberByClient.delete(socket);
        console.info(`Client gone [id=${socket.id}]`);
    });
});

// sends each client its current sequence number
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 les connexions entrantes. Quand on arrive, il ajoute ce nouveau client à une carte afin qu'il puisse garder une trace de son numéro de séquence. Il écoute également l' disconnectévénement de ce client , quand il le supprimera de la carte.

Chaque seconde, une minuterie est déclenchée. Lorsque c'est le cas, le serveur parcourt la carte et envoie un message à chaque client avec son numéro de séquence actuel. Il l'incrémente ensuite et enregistre le numéro dans la carte. C'est tout ce qu'il y a à faire. Peasy facile.

Client

La partie client est encore plus simple. Il se connecte simplement au serveur et écoute le seq-nummessage, l'imprimant sur 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écuter l'exemple

Installez les bibliothèques requises:

npm install socket.io
npm install socket.io-client

Exécutez le serveur:

node server

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

node client

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


Le port 8000 est-il la norme pour socketio en production?
Rouge

1
Désolé, j'ai mis si longtemps à répondre, @ingo. 8000 est un port commun utilisé par la communauté Node lors du test des Websockets ou des serveurs HTTP (3000 est également courant). Bien sûr, vous pouvez l'utiliser en production, mais je ne sais pas à quel point c'est courant ... de toute façon, vous pouvez vraiment utiliser n'importe quel port, tant que vos passerelles / équilibreurs de charge / etc. sont préparés pour cela.
Lucio Paiva

Je suis programmeur Python / PHP et je suis nouveau dans les sockets et les nœuds, la question est, pourquoi avons-nous besoin d'incrémenter le numéro séquentiel du même utilisateur chaque seconde? est-ce juste pour la démo?
Umair

1
@Umair c'est juste à des fins de démonstration, pas vraiment besoin d'incrémenter un numéro de séquence. C'était juste pour montrer que vous pouvez continuer à envoyer des trucs à chaque client et qu'ils le recevront.
Lucio Paiva

Je ne vois pas dans cet exemple comment un client spécifique envoie un message uniquement et uniquement à un autre client. Chaque client ne connaît que son propre numéro de séquence tel que vous l'avez nommé et créé pour la démonstration, et il n'y a pas de mappage de ces numéros de séquence vers un ID de socket nécessaire pour envoyer un message directement au client avec cet ID de socket.
Selçuk

36

Vous pouvez utiliser

// envoyer le message uniquement à l'expéditeur-client

socket.emit('message', 'check this');

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

io.emit('message', 'check this');

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

socket.broadcast.emit('message', 'this is a message');

// ou vous pouvez l'envoyer dans une salle

socket.broadcast.to('chatroom').emit('message', 'this is the message to all');


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

36

Dans la version 1.0, vous devez utiliser:

io.sockets.connected[socketid].emit();

1
Oui !!!, auparavant io.sockets.sockets [socketid] .emit () fonctionnait, mais cela me donnait des erreurs d'objet non définies dans la nouvelle version de socket.io. Le passage à io.sockets.connected fonctionne.
Fraggle

Pour ceux qui utilisent TypeScript, il s'agit actuellement de l'API «canonique» pour cela en fonction des typages.
Avi Cherry

Mais je reçois une erreur: io.sockets.connected ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServer", info); ^ TypeError: Impossible de lire la propriété 'emit' d'undefined
Raz

cela est probablement dû au fait que l'API a été mise à jour et qu'il n'y a plus d'objet comme io.sockets.connected["something"]et sa emitméthode.
Selçuk

12

Quelle que soit la version que nous utilisons si nous utilisons simplement console.log () l'objet "io" que nous utilisons dans notre code nodejs côté serveur, [par exemple io.on ('connection', function (socket) {...});] , nous pouvons voir que "io" est juste un objet json et il y a de nombreux objets enfants où les objets id et socket sont stockés.

J'utilise socket.io version 1.3.5, btw.

Si nous regardons dans l'objet io, il contient,

 sockets:
  { name: '/',
    server: [Circular],
    sockets: [ [Object], [Object] ],
    connected:
     { B5AC9w0sYmOGWe4fAAAA: [Object],
       'hWzf97fmU-TIwwzWAAAB': [Object] },

ici nous pouvons voir les socketids "B5AC9w0sYmOGWe4fAAAA" etc. Donc, nous pouvons faire,

io.sockets.connected[socketid].emit();

Encore une fois, après une inspection plus approfondie, nous pouvons voir des segments comme,

 eio:
  { clients:
     { B5AC9w0sYmOGWe4fAAAA: [Object],
       'hWzf97fmU-TIwwzWAAAB': [Object] },

Ainsi, nous pouvons récupérer une socket à partir d'ici en faisant

io.eio.clients[socketid].emit();

Aussi, sous le moteur, nous avons,

engine:
 { clients:
    { B5AC9w0sYmOGWe4fAAAA: [Object],
      'hWzf97fmU-TIwwzWAAAB': [Object] },

Donc, on peut aussi écrire,

io.engine.clients[socketid].emit();

Donc, je suppose que nous pouvons atteindre notre objectif de l'une des 3 façons que j'ai énumérées ci-dessus,

  1. io.sockets.connected [socketid] .emit (); OU
  2. io.eio.clients [socketid] .emit (); OU
  3. io.engine.clients [socketid] .emit ();

Mais j'ai eu cette erreur: io.eio.clients ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServer", info); ^ TypeError: Impossible de lire la propriété 'emit' d'undefined
Raz

Actuellement (avec la version 2.1.1) cela ne fonctionnait que pour sockets.connected, mais pas pour les deux autres.
James

10

Tu peux le faire

Sur le serveur.

global.io=require("socket.io")(server);

io.on("connection",function(client){
    console.log("client is ",client.id);
    //This is handle by current connected client 
    client.emit('messages',{hello:'world'})
    //This is handle by every client
    io.sockets.emit("data",{data:"This is handle by every client"})
    app1.saveSession(client.id)

    client.on("disconnect",function(){
        app1.deleteSession(client.id)
        console.log("client disconnected",client.id);
    })

})

    //And this is handle by particular client 
    var socketId=req.query.id
    if(io.sockets.connected[socketId]!=null) {
        io.sockets.connected[socketId].emit('particular User', {data: "Event response by particular user "});
    }

Et sur le client, c'est très facile à manipuler.

var socket=io.connect("http://localhost:8080/")
    socket.on("messages",function(data){
        console.log("message is ",data);
        //alert(data)
    })
    socket.on("data",function(data){
        console.log("data is ",data);
        //alert(data)
    })

    socket.on("particular User",function(data){
        console.log("data from server ",data);
        //alert(data)
    })

7

À partir de la version 1.4.5, assurez-vous de fournir un socketId correctement préfixé dans io.to (). Je prenais le socketId que le client avait connecté pour déboguer et il était sans préfixe, alors j'ai fini par chercher pour toujours jusqu'à ce que je le découvre! Vous devrez peut-être le faire comme ceci si l'identifiant que vous avez n'est pas préfixé:

io.to('/#' + socketId).emit('myevent', {foo: 'bar'});


2

Vous pouvez également conserver les références des clients. Mais cela rend votre mémoire occupée.

Créez un objet vide et placez-y vos clients.

const myClientList = {};

  server.on("connection", (socket) => {
    console.info(`Client connected [id=${socket.id}]`);
     myClientList[socket.id] = socket;   
  });
  socket.on("disconnect", (socket) => {
    delete myClientList[socket.id];
  });

puis appelez votre client spécifique par identifiant depuis l'objet

myClientList[specificId].emit("blabla","somedata");

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.