Invia un messaggio a un client specifico con socket.io e node.js


191

Sto lavorando con socket.io e node.js e fino ad ora sembra abbastanza buono, ma non so come inviare un messaggio dal server a un client specifico, qualcosa del genere:

client.send(message, receiverSessionId)

Ma né il .send()né i .broadcast()metodi sembrano soddisfare il mio bisogno.

Quello che ho trovato come possibile soluzione è che il .broadcast()metodo accetta come secondo parametro un array di SessionIds a cui non invia il messaggio, quindi potrei passare un array con tutti gli SessionId collegati in quel momento al server, tranne quello Vorrei inviare il messaggio, ma ritengo che ci debba essere una soluzione migliore.

Qualche idea?

Risposte:


95

Bene, devi afferrare il cliente per questo (sorpresa), puoi andare nel modo semplice:

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

Il che potrebbe rompersi, ne dubito, ma è sempre una possibilità che io.clientspotrebbe essere cambiata, quindi usa quanto sopra con cautela

Oppure tieni traccia dei client, quindi li aggiungi al tuo clientsoggetto nel connectionlistener e li rimuovi nel disconnectlistener.

Vorrei usare quest'ultimo, poiché a seconda della tua applicazione potresti voler avere più stato sui client, quindi qualcosa di simile clients[id] = {conn: clientConnect, data: {...}}potrebbe fare il lavoro.


6
Ivo, puoi indicare un esempio più completo o elaborare un po '? Non vedo l'ora di comprendere questo approccio, ma non sono sicuro di riconoscere le variabili / oggetti che stai usando in questo esempio. Nei client [id] = {conn: clientConnect, data: {...}}, i client [id] fanno parte dell'oggetto io come visto in io.clients [sessionID] sopra? Inoltre, qual è l'oggetto clientConnect? Grazie.
AndrewHenderson,

@ ivo-wetzel ciao, mi potresti helo su questo argomento? stackoverflow.com/questions/38817680/...
pishguy Mahdi

178

La risposta di Ivo Wetzel non sembra più essere valida in Socket.io 0.9.

In breve, ora è necessario salvare socket.ide utilizzare io.sockets.socket(savedSocketId).emit(...) per inviare messaggi ad esso.

Ecco come l'ho fatto funzionare nel server cluster Node.js:

Per prima cosa devi impostare il negozio Redis come negozio in modo che i messaggi possano passare attraverso i processi:

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);
    });
  });

});

Ho omesso il codice di clustering qui, perché lo rende più disordinato, ma è banale aggiungere. Basta aggiungere tutto al codice lavoratore. Altri documenti qui http://nodejs.org/api/cluster.html


4
Grazie è stato utile Dovevo invece usare un array: io.of('/mynamespace').sockets[socketID].emit(...)(non so se è perché sto usando uno spazio dei nomi)
Adrien Schuler,

in un ambiente cluster, come posso assicurarmi che il processo corretto a cui appartiene il socket stia inviando il messaggio?
Gal Ben-Haim,

Che ne dici di una sessione appiccicosa per gentile concessione di NGINX o HAProxy @Gal Ben-Haim?
matanster,

var var = new socketio.RedisStore; ^ TypeError: undefined non è una funzione in Object. <anonimo> (C: \ Users \ Dev \ Desktop \ nouty-server \ server.js: 108: 14) at Module._compile (module.js: 460: 26) at Object.Module._extensions..js (module.js: 478: 10) in Module.load (module.js: 355: 32) in Function.Module._load (module.js: 310: 12) in Function.Module. runMain (module.js: 501: 10) all'avvio (node.js: 129: 16) at node.js: 814: 3
Lucas Bertollo,

1
io.sockets.socket(socket_id)viene rimosso in socket.io 1.0. github.com/socketio/socket.io/issues/1618#issuecomment-46151246
ImMathan

98

ogni socket si unisce a una stanza con un ID socket per un nome, quindi puoi semplicemente

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

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

Saluti


7
Questa è la risposta migliore e funziona con le versioni più recenti di socket.io. C'è un bel foglietto qui: stackoverflow.com/questions/10058226/...
blented

4
si noti che questo è un tipo di "trasmissione" di emissione di un evento. quindi se provi a impostare un callback su questo, avrai un errore. se devi inviare un evento a un socket specifico con un callback, usa la risposta e l'uso di @ PHPthinking io.sockets.connected[socketid].emit();. Testato con 1.4.6.
tbutcaru,

Ma ho riscontrato questo errore: io.to ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServe‌ r", info); ^ TypeError: Impossibile leggere la proprietà 'emit' di undefined
Raz

85

La soluzione più semplice ed elegante

È facile come:

client.emit("your message");

E questo è tutto.

Ma come? Fammi un esempio

Ciò di cui tutti abbiamo bisogno è in realtà un esempio completo, ed è quello che segue. Questo è testato con la versione socket.io più recente (2.0.3) e utilizza anche Javascript moderno (che dovremmo usare tutti ormai).

L'esempio è composto da due parti: un server e un client. Ogni volta che un client si connette, inizia a ricevere dal server un numero di sequenza periodica. Viene avviata una nuova sequenza per ogni nuovo client, quindi il server deve tenerne traccia individualmente. È qui che entra in gioco "Devo inviare un messaggio a un determinato cliente" . Il codice è molto semplice da capire. Vediamolo.

server

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);

Il server inizia l'ascolto sulla porta 8000 per le connessioni in entrata. Quando si arriva, aggiunge quel nuovo client a una mappa in modo che possa tenere traccia del suo numero di sequenza. Ascolta anche l' disconnectevento del cliente , quando lo rimuoverà dalla mappa.

Ogni secondo, viene emesso un timer. In tal caso, il server percorre la mappa e invia un messaggio a ogni client con il suo numero di sequenza corrente. Quindi lo incrementa e memorizza il numero nella mappa. Questo è tutto. Vai tranquillo.

Cliente

La parte client è ancora più semplice. Si collega al server e ascolta il seq-nummessaggio, stampandolo sulla console ogni volta che arriva.

client.js

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

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

Eseguendo l'esempio

Installa le librerie richieste:

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

Esegui il server:

node server

Apri altre finestre del terminale e genera tutti i client che desideri eseguendo:

node client

Ho anche preparato un riassunto con il codice completo qui .


La porta 8000 è la norma per socketio in produzione?
Rosso

1
Mi dispiace di aver impiegato così tanto tempo a rispondere, @ingo. 8000 è una porta comune utilizzata dalla comunità Node durante il test di server Web o server HTTP (anche 3000 è comune). Ovviamente potresti usarlo in produzione, ma non sono sicuro di quanto sia comune ... comunque, puoi semplicemente usare qualsiasi porta, purché i tuoi gateway / bilanciamento del carico / ecc. Siano preparati per questo.
Lucio Paiva,

Sono programmatore Python / PHP e sono nuovo su socket e nodo, la domanda è: perché dobbiamo aumentare il numero di seq dello stesso utente ogni secondo? è solo per la demo?
Umair,

1
@Umair è solo a scopo dimostrativo, non è necessario incrementare un numero progressivo. Era solo per dimostrare che puoi continuare a inviare materiale a ciascun cliente e loro lo riceveranno.
Lucio Paiva,

In questo esempio non vedo come un client specifico invii un messaggio solo e solo a un altro client. Ogni client conosce solo il proprio numero di sequenza come è stato nominato e creato per la demo e non esiste alcuna mappa da questi numeri di sequenza a un ID socket necessario per inviare un messaggio direttamente al client con quell'ID socket.
Selçuk,

36

Puoi usare

// invia il messaggio solo al mittente-client

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

// oppure puoi inviare a tutti gli ascoltatori incluso il mittente

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

// invia a tutti gli ascoltatori tranne il mittente

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

// oppure puoi inviarlo a una stanza

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


Ha funzionato per me, ho anche dovuto aggiungere "const socket = require ('socket.io') (http);" nel mio file server.js.
iPzard

36

In 1.0 dovresti usare:

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

1
Sì !!!, precedentemente io.sockets.sockets [socketid] .emit () ha funzionato, ma questo mi ha dato errori di oggetto indefinito nella versione più recente di socket.io. Passando a io.sockets.connected funziona.
Fraggle,

Per coloro che usano TypeScript, questa è attualmente l'API 'canonica' per questo secondo le digitazioni.
Avi Cherry,

Ma ricevo un errore: io.sockets.connected ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServer", info); ^ TypeError: Impossibile leggere la proprietà 'emit' di undefined
Raz

ciò è probabilmente dovuto al fatto che l'API si è aggiornato e non esiste più un oggetto simile io.sockets.connected["something"]e il suo emitmetodo.
Selçuk,

12

Qualunque versione stiamo usando se semplicemente console.log () l'oggetto "io" che usiamo nel nostro codice nodejs sul lato server, [es. Io.on ('connessione', funzione (socket) {...});] , possiamo vedere che "io" è solo un oggetto json e ci sono molti oggetti figlio in cui sono memorizzati l'id socket e gli oggetti socket.

Sto usando socket.io versione 1.3.5, a proposito.

Se guardiamo nell'oggetto io, esso contiene,

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

qui possiamo vedere i socketid "B5AC9w0sYmOGWe4fAAAA" ecc. Quindi, possiamo fare,

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

Ancora una volta, su un'ulteriore ispezione possiamo vedere segmenti come,

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

Quindi, possiamo recuperare un socket da qui facendo

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

Inoltre, sotto il motore abbiamo,

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

Quindi, possiamo anche scrivere,

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

Quindi, immagino che possiamo raggiungere il nostro obiettivo in uno dei 3 modi che ho elencato sopra,

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

Ma ho riscontrato questo errore: io.eio.clients ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServer", info); ^ TypeError: Impossibile leggere la proprietà 'emit' di undefined
Raz

Attualmente (con la versione 2.1.1) ha funzionato solo con sockets.connected, ma non con gli altri due.
James,

10

Puoi farlo

Sul server.

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 "});
    }

E su client, è molto facile da gestire.

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

A partire dalla versione 1.4.5, assicurarsi di fornire un socketId con prefisso corretto in io.to (). Stavo prendendo il socketId il Client ha effettuato il debug ed era senza prefisso, quindi ho finito per cercare per sempre fino a quando non l'ho scoperto! Quindi potresti doverlo fare in questo modo se l'id che hai non ha il prefisso:

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

6

io.sockets.sockets [socket.id] .emit (...) ha funzionato per me in v0.9


Benvenuto in Stack Overflow. Questa risposta non sembra aggiungere molto relatve alle risposte esistenti. Una volta che hai più reputazione , sarai in grado di commentare i post di altre persone . Questo sembra più adatto per un commento.
jerry,

2

Inoltre puoi mantenere le referenze dei clienti. Ma questo rende occupato il tuo ricordo.

Crea un oggetto vuoto e inseriscici i tuoi clienti.

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];
  });

quindi chiama il tuo client specifico per ID dall'oggetto

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.