Send message to specific client with socket.io and node.js - javascript

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?

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

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

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.

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.

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

In 1.0 you should use:
io.sockets.connected[socketid].emit();

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,
io.sockets.connected[socketid].emit();
OR
io.eio.clients[socketid].emit();
OR
io.engine.clients[socketid].emit();

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

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

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

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

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/

Related

Socket.io—about socket disconnect

I have this scenario with socket.io:
I want to receive the data from a sever and Forward the data to webclient.But when I receive a lot of data and close the page, it console
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
...(a lot)
Here is the code:
server:
var express=require('express');
var app=express();
var net=require('net');
var http=require('http').createServer(app);
var io=require('socket.io')(http);
var net=require('net');
var nodeServer = new net.Socket();
var aSocket=null;
io.on('connection', function (socketIO) {
aSocket=socketIO;
};
nodeServer.on('data', function(data) {
if(aSocket!=null){
aSocket.emit('pushToWebClient',useData);
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
}
client:
socket.on('pushToWebClient', function (useData) {
});
I find
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
console a lot of'DISCONNECTED FROM CLIENT' but actually it should console just once in the code.
I had even console.log(aSocket.id),it console just only one.
I don't know why it is console so many times.
I haved used setMaxListeners(10) to try to avoid it .
Will it lead to a memory leak?
It appears that you are registering multiple event listeners for the same disconnect event. In this code:
nodeServer.on('data', function(data) {
if(aSocket!=null){
aSocket.emit('pushToWebClient',useData);
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
}
You appear to be registering a new disconnect event listener every time you get a data message. So, if you have multiple listeners, then each one will get called when the socket disconnects and the result is that you will log the same message multiple times all for the same socket.
You can verify this is what is happening by moving your disconnect handler into the connection handler so it is only ever attached just once for each socket.
In addition putting asocket into a global or module-level variable means that your server code would only ever work with one single client at a time. It is not clear exactly what you are trying to do when you get data on the nodeserver connection - whether you're trying to send that data to only one specific client or to all connected clients.
I try to delete the code:
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
or moving it out of nodeServer handler,
it turn into normal and never suggest me to setMaxlisener.
I think maybe it is incorrect put one API into a API
And the envent maybe not release the socket,so it console multiple times .
EDIT: I'm moving this to the top because I saw that someone already provided my solution but you were having a problem managing the data sent to the client. Your aSocket variable will be overwritten by every new client that connects to your app. If you want to send data to a specific client using your server nodeServer, you should create a global variable (an array) that keeps track of all of your client socket connections. So instead of using one global variable aSocket do the following:
var net=require('net');
var nodeServer = new net.Socket();
var clients = [];
io.on('connection', function (socketIO) {
clients.push(socketIO);
var clientNum = clients.length-1;
aSocket.on('disconnect', function () {
clients.splice(clientNum, 1);
console.log('DISCONNECTED FROM CLIENT: '+socketIO.id);
});
};
nodeServer.on('data', function(data) {
//have your data object contain an identifier for the client that caused the handler to fire
//for the sake of the answer I just use data.id
var clientID = data.id;
if(clients[clientID]!=null){
clients[clientID].emit('pushToWebClient', useData);
}
}
Let me know how it goes! My original answer is below:
Try moving
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
out of your nodeServer.on('data', ...) event listener into the io.on('connection', ...) event listener like so:
io.on('connection', function (socketIO) {
aSocket=socketIO;
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
};
socket.io is designed to keep polling for the presence of the server/client. If either the server or the client are disconnected, the remaining 'side' continues to receive polling requests and, consequently, will continuously print an error.
You can see this effect on the client side in your browser when you disconnect your server and leave the client page open. If you look at the browser's error/console log what you should see is a continuous stream of net::ERR_CONNECTION_REFUSED errors. By placing the disconnect event handler in the .on('data', ...) handler for your server, you are seeing the converse of this situation.
net:ERR_CONNECTION_REFUSED example
This is basic code for socket.io
The following example attaches socket.io to a plain Node.JS HTTP
server listening on port 3000.
var server = require('http').createServer();
var io = require('socket.io')(server);
io.on('connection', function(client){
client.on('event', function(data){});
client.on('disconnect', function(){});
});
server.listen(3000);
I think, you should try.

Node JS mqtt client doesn't received subscribed messages when broker goes down and comes up

I have created a mqtt node js client. My connection options are as follows.
mqttOptions = {
clientId: '100',
keepAlive: 1000,
clean: false,
reconnectPeriod: '1000',
will: willMessage
};
I disconnected the server and brought it up again, while the client was still running. The client had the logic to publish every 1 second. Though the client was publishing after this reconnect, it was not receiving the message. It was subscribed to its own message topic. Since I set the clean option to be false, should it not subscribe to the topics on the reconnect and start receiving them?
Below is how I'm establishing the connection.
this.client = mqtt.connect(url, mqttOptions);
and below is how I'm subscribing.
this.client.subscribe(topic);
What am I doing wrong here? Please advice.
We faced this issue with EMQ as the broker and with mqtt library for NodeJS. When it was mosquitto as broker, the client reconnects and gets all the messages it had subscribed. But, if it subscribes again, it gets n number of copies of the same message. As per the library document, it is recommended to check for connack and connack.sessionPresent for previous subscriptions.
We subscribed to all events of client and found that offline is the one that is called when the broker goes down. Then the reconnect and close gets called until the broker is up. Hence, here is how we did it. On offline, end the client forcefully and on completion of end, create a new client - the same function that was used to create client:
doConnect() {
this.client = mqtt.connect('mqtt://myhost', this.myOptionsIfAny);
this.client.on('connect', () => {
this.client.subscribe('mytopics');
this.client.on('message', (topic, message) => {
// do processing
});
this.client.on('offline', () => {
this.client.end(true, () => {
doConnect();
});
});
}
clean: 'false',
Should 'false' definitely be a string? I presume it should be a boolean.

socket.io - emit to socketid(single client) not working

I am using version 0.9.4 (I can use any version as per your guidance)
in server (app.js), I am storing socket id like this:
var socket_ids = [];
io.sockets.on('connection', function(socket) {
socket_ids.push(socket.id); #This is how I am getting socket id. My real code logic is totally different, but getting socket id using socket.id;
socket.on('createNote', function(data) {
socket.broadcast.to(socket_ids[0]).emit('onNoteCreated', data);
});
});
In short, I have 4 people opened sessions, all 4 are different users.
When p1 sends message, it should reach p2 only.
When p3 sends message, it should reach p4 only.
Keeping the if..else logics aside(I am fine with that), when I am broadcasting message using socket id, the client/browser with that socket id is not receiving that message. Also, above above broadcast line of code(other lines are custom for SO) not giving errors but client not receiving.
But these clients/browsers receiving messages when broadcasted to everything.
Thanks.
With the latest socket.io version, this is how I would do it:
var sockets = [];
io.on("connection", function(socket){
// Store whole socket object.
// NOTE: You should probably loop through sockets array
// to see if a socket with this id already exists
sockets.push(socket);
socket.on("createNote", function(data){
sockets[0].emit("onNoteCreated", data);
});
});
Hope this helps

Socket.io : Get Client sessionID at any point

I know how to retrieve the client sessionID when the user connects.
But I'd like to retrieve it at any time, for example when the client clicks on something, I would like to be able to know who clicked, what their sessionID was.
socket.sessionID doesn't work, nor does socket.handshake.sessionID
For example :
I have this express route :
.get('/result/:survey', function(req, res) {
res.redirect('/result/'+req.params.survey+'/1');
})
Just before the redirection, I'd like to make the user join a socket room, and also get their sessionID. How could I possibly do that ? According to the doc, it would be socket.join('room') but I doubt socketonly represents the connection I have with the client, and not with the other clients. Maybe I'm just having trouble understanding sockets !
As of version 1.0, you get the client's sessionid this way:
var socket = io();
socket.on('connect', function(){
var sessionid = socket.io.engine.id;
...
});
I'm not sure exactly what you mean, because "when the client clicks on something" assumes that you mean 'client-side' (where you can use socket.socket.sessionid) but "store it in an array of clients" assumes you mean 'server-side' (where you can access it as socket.id).
Client-side:
var socket = io.connect();
socket.on('connect', function() {
var sessionid = socket.socket.sessionid;
...
});
Server-side:
io.sockets.on('connection', function(socket) {
var sessionid = socket.id;
...
});
I store client id's in server side event handlers simply with:
// server.js
io.sockets.on('connection', function (client) {
client.on('event_name', function (_) {
var clientId = this.id;
}
}
I'm using socket.io version 2.0.4 and you can access the client id at any point in time by using this.id in a handler. For example:
socket.on('myevent',myhandler);
function myhandler(){
console.log('current client id: '+this.id);
}
Apparently the handler function is executed in the Socket context itself, which similarly will give you access to other self-properties like client (connected Client object), request (current Request object being processed), etc. You can see the full API here https://socket.io/docs/server-api/#socket

Reconnection in socket.io problem in `socket.on('message',function(){})`

I have a socket.io connection to connect to server and client,
It work fine.
Now when I try to reconnect it on disconnect from server it get connected but then socket.on('message' doesnt get fired any more.
I checked it from server side it is pushing that message.
Please suggest me some thing I am out of ideas now.
I am sure that problem is on client side socket.on message
Client side code
var socket = new io.Socket('some host name',{port:80,rememberTransport:true});
socket.on('connect', function(){
clearInterval(socketInterval);
});
socket.on('message', function(obj)
{
alert("meg from server");
});
socket.on('disconnect', function()
{
socketInterval=setInterval("socket.connect()",5000);
});
socket.connect();
I don't know node.js, but it looks like syntax error, haven't you forgot the right paratheses?
socket.on('connect', function(){
clearInterval(socketInterval);
});
socket.on('message', function(obj)
{
alert("meg from server");
});
socket.on('disconnect', function()
{
socketInterval=setInterval("socket.connect()",5000);
});
it would appear that the "problem" most likely is on the server side. The server has two ways to send messages to the client (emit and broadcast). If you are doing a one to one message, most people use emit. I am assuming that you built a chat server which stores the sessionIds of the client. It works fine with the initial connection because the server has the correct sessionId, but let's say connection is lost and you reestablish connection, now the server tries to send a message to the client. If your server stored the initial sessionId, say in an array, and attempts to use the original sessionId to emit a message, it will fail because reconnection causes a new sessionId to be created.
The solution in this case is to remove the previous sessionId from the array and add the new sessionId upon reconnection.

Categories