socket.io broadcast from client - node.js

I just started with socket.io, and I want some function which will broadcast somethink from one client throuh server to all clients.
For example I will have game, when I will need handle a lot of things.
There is two option:
1.
Every call handle on server and client side, for every call create socket.on('call');
But I think there is better solution
2.
On client call emit when I get payload and name of socket which will be called from server to other clients.
Like this:
SERVER
// Broadcast specific emit to all clisnts expect sender
socket.on('broadcast', function(data){
socket.broadcast.emit(data.emitName, {id: socket.id, payload: data.payload});
});
and client:
// Call to server
socket.emit('broadcast', {emitName: 'playerMove', payload: {....}});
// Handle called from server
socket.on('playerMove', function(data){
....
});
Second solution can save a lot of time, but is it safe solution ?
And is there any better solution how call from client broadcast throuht server and call to other clients ?

Only the server can broadcast to all clients. The server retains control.
Your second solution is the best solution.
You may want to somehow ensure that you trust the client that is making the emit that will result in the broadcast. HTTP protocol is well-established in respect of client authentication.
Websockets does not have the same standard approach. Maybe you should investigate Websocket client authentication. There is no reason why you cannot use JWT authentication with websockets, just send the JWT token in the data packet the client is sending. This is probably how I would handle authentication but I am no expert on how to do this with websockets.

Related

why is my socket.io server assigning new socket ids to clients or losing track of existing socket ids?

I'm developing a simple website, where the client and server communicate over web sockets. I'm using nodejs and the socket.io library for the socket communication
Specifically, my server works as a middleware between an mqtt broker and my client. So on one hand, my server connects with the mqtt broker to consume messages and on the other hand delivers these messages to the connected clients over web sockets. I'm using the node mqtt library for the mqtt communication.
My codebase is fairly large, so to give you a feeling of how my code looks like, I will show this example, which should be straightforward to understand:
const io = require("socket.io")(port);
handleRequests(io) {
io.on("connection", (socket) => {
logger.info(`New client connected: ${socket.id}`);
this.clients[socket.id] = { // track clients and subscribed topics
topic: '',
};
this.numberOfUsers++;
io.sockets.emit("onUser", this.numberOfUsers);
this.handleChange(socket);
this.addToSubscribedClients(socket);
this.removeFromSubscribedClients(socket);
this.handleDisconnect(socket);
this.sendMqttMessageToClient(socket);
});
}
This is my "main" function, where as you can see, I'm initializing an io object and using it later by passing it to the handleRequests function. Each time a new client connects, I'm calling the callback function where I call the five other functions and passing the socket object as a parameter, which should be fine I guess. I'm passing the socket object as a parameter because I need it to later call socket.emit in order to send back message to a specific client, since the socket object is unique for each client.
This works great until more than ~ 30 clients are connected. I'm trying to debug this for 2 weeks now and can't figure out why this is happening. I'm testing this by opening multiple tabs in my browser. I start with one client and then increase the number of clients/tabs. At some points, I notice that some clients receive no values from the server but other clients still do, which is incorrect since all clients should receive the values in real time.
I noticed that the clients, which are not receiving values have other ids than the ones stored on the server. I tested this with a simple console.log() on both clients and server. How this is happening? I'm very positive that I'm sending the ids correctly since there are other clients, which still receive values from the server. My guess is that the server is somehow disconnecting some clients automatically, because if a client reconnects then a new id will be assigned to it automatically.
Does anyone have any idea why this is happening? and why it works fine with the first ~30 clients and starts to occur when many clients are connected? This issue is very hard to debug since the code works fine for a small number of clients and no errors are thrown when the bug occurs, so I'm hoping that someone had this before.
Edit
Now I just found that i can print a reason for socket disconnection. When I do that, ping timeout is printed, which I don't understand because when I have one single or few clients connected then this error does not happen.

How to send data to client with node express when client has not ask for?

My Question is above. I want that when an event triggered like a new Mail comes in or an other client has connected to the server, my application send to the existing connection a request(?) which say something like "Hey you not alone !". I know how to answer on request from client but how can i send the client information when he has not ask for it explicit.
i hav draw an simple image to visualize what i mean
You cannot directly do this. However, you can archive this by Server-sent Event which is making use of long-polling.
Reference:
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
As per my understanding you are looking for a way to send data / message from server to client side.
if so there are two ways to do it.
Create an API and pool is after certain interval of time from client side.
Create a socket server, connect your client with socket which will give you maintain the socketId on server

How to send data from client to only socket server

I know that if I emit data on a socket, the data would be emitted to the all connected clients.
io.emit("test", data)
For instance if I run the command above, actually the data reaches to the all clients. But only test channel listeners handle it.
To prevent that, I use rooms. I use code like the following:
io.in("test").emit(data)
So, what if a client send data to the server, then will data be reached to the all clients?
Note: I also use socket.io-redis to make cluster.
This code will send to all client in "roomName"
io.to(roomName).emit('new msg', {data:'hello every clients'});

How to send real time notification to single user using node.js and PHP

I am trying to integrate real time notifications with Node and socket.io in a Symfony Application. I have read a lot of information about this topic and have a working Node application.
nodeClient.js
var socket = io.connect( 'http://192.168.15.106:8080' );
$('a.sendSmile').click(function(){
socket.emit( 'message', { name: 'something' } );
});
socket.on('message', function(data){
console.log(data.name);
});
The problem now is with the above which is working perfectly I am able to send real time notification to all the users at once. But what's the best way to target a single user?
For example a user can send a smile to another user so only the second user should receive the notification and not all the users.
Should I make multiple listeners for node? or any other method to do this?
You need some way of identifying which socket that connected to your server is the one you want to send data to and then you can send to just that socket. You can keep track of user names when users connect or if you have some auth system, you can keep track of which socket belongs to which authenticated user.
Your server holds a list of connected sockets. Each connected one at a time and triggered a connection event on your server when they connected. Your application needs to create a way of knowing which of those connected sockets you want to send the data to. This is not something you've described anything about how you want that to work so we can't really help more specifically.
You can dispatch a notification to single user if you can discriminate that user. For example you can get a user_id on client connection to your nodejs server (the user_id is send from client, inside message) and save it in a key-value store (like Redis, memcache, ...). In this way you can correctly dispatch the notification, arrived from the server (like Symfony2 application), to right user.
I suggest you use Redis, both as a key-value store and for its implementation pattern of the publish/subscribe usable as a channel of communication between the server and the application of realtime.

How does socket.io send messages across multiple servers?

The Socket.io API has the ability to send messages to all clients.
With one server and all sockets in memory, I understand how that server one can send a message to all its clients, that's pretty obvious. But what about with multiple servers using Redis to store the sockets?
If I have client a connected to server y and client b connected to server z (and a Redis box for the store) and I do socket.broadcast.emit on one server, the client on the other server will receive this message. How?
How do the clients that are actually connected to the other server get that message?
Is one server telling the other server to send a message to its connected client?
Is the server establishing its own connection to the client to send that message?
Socket.io uses MemoryStore by default, so all the connected clients will be stored in memory making it impossible (well, not quiet but more on that later) to send and receive events from clients connected to a different socket.io server.
One way to make all the socket.io servers receive all the events is that all servers use redis's pub-sub. So, instead using socket.emit one can publish to redis.
redis_client = require('redis').createClient();
redis_client.publish('channelName', data);
And all the socket servers subscribe to that channel through redis and upon receiving a message emit it to clients connected to them.
redis_sub = require('redis').createClient();
redis_sub.subscribe('channelName', 'moreChannels');
redis_sub.on("message", function (channel, message) {
socket.emit(channel, message);
});
Complicated Stuff !! But wait, turns out you dont actually need this sort of code to achieve the goal. Socket.io has RedisStore which essentially does what the code above is supposed to do in a nicer way so that you can write Socket.io code as you would write for a single server and will still get propagated over to other socket.io server through redis.
To summarise socket.io sends messages across multiple servers by using redis as the channel instead of memory.
There are a few ways you can do this. More info in this question. A good explanation of how pub/sub in Redis works is here, in Redis' docs. An explanation of how the paradigm works in general is here, on Wikipedia.
Quoting the Redis docs:
SUBSCRIBE, UNSUBSCRIBE and PUBLISH implement the Publish/Subscribe
messaging paradigm where (citing Wikipedia) senders (publishers) are
not programmed to send their messages to specific receivers
(subscribers). Rather, published messages are characterized into
channels, without knowledge of what (if any) subscribers there may be.
Subscribers express interest in one or more channels, and only receive
messages that are of interest, without knowledge of what (if any)
publishers there are. This decoupling of publishers and subscribers
can allow for greater scalability and a more dynamic network topology.

Resources