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?

Rodolfo Palma
la source

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.

Ivo Wetzel
la source
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

Epeli
la source
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é

Matic Kogovšek
la source
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 .

Lucio Paiva
la source
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');

DécodeurNT
la source
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();
PHPpensée
la source
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 ();
Suman Barick
la source
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)
    })
abhaygarg12493
la source
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'});
risqué
la source
6

io.sockets.sockets [socket.id] .emit (...) a fonctionné pour moi dans la v0.9

aljosa
la source
Bienvenue dans Stack Overflow. Cette réponse ne semble pas ajouter grand-chose aux réponses existantes. Une fois que vous aurez plus de réputation , vous pourrez commenter les publications d'autres personnes . Cela semble mieux adapté pour un commentaire.
jerry
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");
Kamuran Sönecek
la source
0

Socket.IO vous permet d '«espace de noms» vos sockets, ce qui signifie essentiellement l'attribution de différents points de terminaison ou chemins.

Cela pourrait aider: http://socket.io/docs/rooms-and-namespaces/

Igor T.
la source