207

I'm working with socket.io and node.js and until now it seems pretty good, but I don't know how to send a message from the server to an specific client, something like this:

client.send(message, receiverSessionId)

But neither the .send() nor the .broadcast() methods seem to supply my need.

What I have found as a possible solution, is that the .broadcast() method accepts as a second parameter an array of SessionIds to which not send the message, so I could pass an array with all the SessionIds connected at that moment to the server, except the one I wish send the message, but I feel there must be a better solution.

Any ideas?

Lucio Paiva
  • 13,507
  • 6
  • 71
  • 90
Rodolfo Palma
  • 2,521
  • 3
  • 13
  • 11

12 Answers12

188

Ivo Wetzel's answer doesn't seem to be valid in Socket.io 0.9 anymore.

In short you must now save the socket.id and use io.sockets.socket(savedSocketId).emit(...) to send messages to it.

This is how I got this working in clustered Node.js server:

First you need to set Redis store as the store so that messages can go cross processes:

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

});

I omitted the clustering code here, because it makes this more cluttered, but it's trivial to add. Just add everything to the worker code. More docs here http://nodejs.org/api/cluster.html

Epeli
  • 16,564
  • 10
  • 63
  • 76
  • 4
    Thanks it was helpful. I just had to use an array instead: `io.of('/mynamespace').sockets[socketID].emit(...)` (don't know if it's because I'm using a namespace) – Adrien Schuler Jun 25 '12 at 15:53
  • on clustered environment, how do I make sure that the correct process that the socket belongs to is sending the messsage ? – Gal Ben-Haim Jan 17 '13 at 12:20
  • How about a sticky session courtesy of NGINX or HAProxy @Gal Ben-Haim? – matanster Jul 21 '13 at 10:22
  • var holder = new socketio.RedisStore; ^ TypeError: undefined is not a function at Object. (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) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Function.Module.runMain (module.js:501:10) at startup (node.js:129:16) at node.js:814:3 – Lucas Bertollo Nov 28 '15 at 13:56
  • 2
    ```io.sockets.socket(socket_id)``` is removed in socket.io 1.0. https://github.com/socketio/socket.io/issues/1618#issuecomment-46151246 – ImMathan Mar 02 '17 at 15:00
  • Is this the correct way to let socket.io and redis work together for more efficient memory handling as well? – Spencer Bigum Mar 02 '18 at 17:59
114

each socket joins a room with a socket id for a name, so you can just

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

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

Cheers

Matic Kogovšek
  • 1,166
  • 1
  • 7
  • 3
  • 7
    This is the best answer and works w/ newer versions of socket.io. There's a nice cheat sheet here: http://stackoverflow.com/questions/10058226/send-response-to-all-clients-except-sender-socket-io – blented Jul 14 '15 at 23:26
  • 5
    note that this is a 'broadcast' type of emiting an event. so if you try to set a callback to this, you'll have an error. if you need to send an event to a specific socket with a callback, then use @PHPthinking's answer and use ``io.sockets.connected[socketid].emit();``. Tested with 1.4.6. – tbutcaru May 25 '16 at 14:48
  • But I got this error: io.to["JgCoFX9AiCND_ZhdAAAC"].emit("socketFromServe‌​r", info); ^ TypeError: Cannot read property 'emit' of undefined – Raz Jan 11 '18 at 08:58
  • This worked for me, but had to use io.to(id).emit("keyword", data). – DORRITO Jan 17 '21 at 01:39
95

Well you have to grab the client for that (surprise), you can either go the simple way:

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

Which may break, I doubt it, but it's always a possibility that io.clients might get changed, so use the above with caution

Or you keep track of the clients yourself, therefore you add them to your own clients object in the connection listener and remove them in the disconnect listener.

I would use the latter one, since depending on your application you might want to have more state on the clients anyway, so something like clients[id] = {conn: clientConnect, data: {...}} might do the job.

Dev01
  • 10,909
  • 17
  • 64
  • 109
Ivo Wetzel
  • 44,463
  • 14
  • 89
  • 109
  • 9
    Ivo, can you point to a more complete example or elaborate a bit? I'm eager to understand this approach, but I'm not sure I recognize the variables/objects you're using in this example. In clients[id] = {conn: clientConnect, data: {...}}, is clients[id] part of the io object as seen in io.clients[sessionID] above? Also what is the clientConnect object? Thanks. – AndrewHenderson Dec 31 '12 at 19:28
  • @ivo-wetzel hi, could you yo helo me on this topic? http://stackoverflow.com/questions/38817680/nodejs-could-not-detect-online-soket – mahdi pishguy Aug 07 '16 at 19:56
90

The simplest, most elegant way

verified working with socket.io v3.1.1

It's as easy as:

client.emit("your message");

And that's it. Ok, but how does it work?

Minimal working example

Here's an example of a simple client-server interaction where each client regularly receives a message containing a sequence number. There is a unique sequence for each client and that's where the "I need to send a message to a particular client" comes into play.

Server

server.js

const
    {Server} = require("socket.io"),
    server = new Server(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);

The server starts listening on port 8000 for incoming connections. As soon as a new connection is established, that client is added to a map that keeps track of its sequence number. The server also listens for the disconnect event to remove the client from the map when it leaves.

Each and every second, a timer is fired. When it does, the server walks through the map and sends a message to every client with their current sequence number, incrementing it right after. That's all that is to it. Easy peasy.

Client

The client part is even simpler. It just connects to the server and listens for the seq-num message, printing it to the console every time it arrives.

client.js

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

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

Running the example

Install the required libraries:

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

Run the server:

node server

Open other terminal windows and spawn as many clients as you want by running:

node client

I have also prepared a gist with the full code here.

Lucio Paiva
  • 13,507
  • 6
  • 71
  • 90
  • Is port 8000 the norm for socketio in production? – Red Apr 29 '16 at 23:24
  • 1
    Sorry I took so long to reply back, @ingo. 8000 is a common port used by the Node community when testing both websockets or HTTP servers (3000 is also common). Of course you could use it in production, but I'm not sure how common that is... anyway, you can just use any port really, as long as your gateways/load balancers/etc are prepared for that. – Lucio Paiva Aug 03 '16 at 02:40
  • I am Python/PHP programmer and I am new to sockets and node, question is, why do we need to increment seq number of same user each second? is that just for demo? – Umair Ayub Sep 11 '18 at 10:59
  • 1
    @Umair it's just for demo purposes, no real need to increment a sequence number. It was just to show that you can keep sending stuff to each client and they will receive it. – Lucio Paiva Sep 11 '18 at 12:46
  • I don't see in this example how a specific client sends a message only and only to another client. Each client only knows their own sequence number as you named and created it for demo, and there is no map from this sequence numbers to a socket id that is needed to send a message direct to the client with that socket id. – sçuçu Oct 09 '18 at 14:01
  • @Işık there's no way to send a message from a client directly to another, as the websocket protocol won't allow that. Moreover, the question is about the server sending to a client. If you need to communicate between two clients, I suggest you send a message from client A to server and then have the server relay it to client B. There are several examples of chat apps on the internet showing how to do that, but you can also ask a specific question in SO if you need help. – Lucio Paiva Oct 09 '18 at 15:33
  • @LucioPaiva Hi, hope you are good. i know it has been a while since you answered this question, but your answer is the most that made sense to me. Can you please have a look at my question.https://stackoverflow.com/questions/65377025/react-native-nodejs-socket-io – kd12345 Dec 20 '20 at 07:10
41

In 1.0 you should use:

io.sockets.connected[socketid].emit();
Gonzalo.-
  • 11,500
  • 5
  • 44
  • 74
PHPthinking
  • 417
  • 4
  • 3
  • 1
    Yes!!!, previously io.sockets.sockets[socketid].emit() worked, but this gave me undefined object errors in newer version of socket.io. Changing to io.sockets.connected works. – Fraggle Mar 29 '15 at 19:17
  • For those using TypeScript, this is currently the 'canonical' API for this according to the typings. – Avi Cherry Jun 26 '15 at 01:07
  • But I receive an error: io.sockets.connected["JgCoFX9AiCND_ZhdAAAC"].emit("socketFromServer", info); ^ TypeError: Cannot read property 'emit' of undefined – Raz Jan 11 '18 at 08:55
  • 2
    that is probably due to the fact that the api updated and there is no more an object like `io.sockets.connected["something"]` and its `emit` method. – sçuçu Oct 09 '18 at 14:15
38

You can use

//send message only to sender-client

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

//or you can send to all listeners including the sender

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

//send to all listeners except the sender

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

//or you can send it to a room

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

DecoderNT
  • 984
  • 8
  • 18
  • Worked for me, I also had to add "const socket = require('socket.io')(http);" in my server.js file. – iPzard Jan 01 '19 at 21:36
  • Hi, hope you are good. i know it has been a while since you answered this question, but your answer is the most that made sense to me. Can you please have a look at my question.https://stackoverflow.com/questions/65377025/react-native-nodejs-socket-io – kd12345 Dec 20 '20 at 07:13
14

Whatever version we are using if we just console.log() the "io" object that we use in our server side nodejs code, [e.g. io.on('connection', function(socket) {...});], we can see that "io" is just an json object and there are many child objects where the socket id and socket objects are stored.

I am using socket.io version 1.3.5, btw.

If we look in the io object, it contains,

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

here we can see the socketids "B5AC9w0sYmOGWe4fAAAA" etc. So, we can do,

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

Again, on further inspection we can see segments like,

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

So, we can retrieve a socket from here by doing

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

Also, under engine we have,

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

So, we can also write,

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

So, I guess we can achieve our goal in any of the 3 ways I listed above,

  1. io.sockets.connected[socketid].emit(); OR
  2. io.eio.clients[socketid].emit(); OR
  3. io.engine.clients[socketid].emit();
Suman Barick
  • 3,101
  • 1
  • 15
  • 30
  • But I got this error: io.eio.clients["JgCoFX9AiCND_ZhdAAAC"].emit("socketFromServer", info); ^ TypeError: Cannot read property 'emit' of undefined – Raz Jan 11 '18 at 08:54
  • Currently (with version 2.1.1) this only worked for sockets.connected, but not the other two. – James Aug 16 '18 at 17:11
  • Helpful answer, made me understand how the socket client-server structure works much better. Thanks Suman – beyondtdr Dec 29 '20 at 16:54
12

You can do this

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

And on client, it is very easy to handle.

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)
    })
Atilla Ozgur
  • 13,569
  • 3
  • 44
  • 65
abhaygarg12493
  • 1,475
  • 1
  • 18
  • 34
7

As of version 1.4.5, be sure you provide a properly prefixed socketId in io.to(). I was taking the socketId the Client logged to debug and it was without prefix so I ended up searching forever till I found out! So you might have to do it like this if the Id you have is not prefixed:

io.to('/#' + socketId).emit('myevent', {foo: 'bar'});
risuch
  • 370
  • 5
  • 12
6

io.sockets.sockets[socket.id].emit(...) worked for me in v0.9

aljosa
  • 61
  • 1
  • 1
  • Welcome to Stack Overflow. This answer doesn't appear to add much relatve to the existing answers. Once you have more [reputation](http://stackoverflow.com/help/whats-reputation), you'll be able to [comment on other peoples' posts](http://stackoverflow.com/help/privileges/comment). This seems better suited for a comment. – jerry Feb 01 '14 at 13:02
4

Also you can keep clients refferences. But this makes your memmory busy.

Create an empty object and set your clients into it.

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

then call your specific client by id from the object

myClientList[specificId].emit("blabla","somedata");
Kamuran Sönecek
  • 3,088
  • 2
  • 24
  • 49
1

Socket.IO allows you to “namespace” your sockets, which essentially means assigning different endpoints or paths.

This might help: http://socket.io/docs/rooms-and-namespaces/

Igor
  • 2,506
  • 1
  • 22
  • 40