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'});
Related
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.
I'm trying to have 2 servers communicate with each other, I'm pretty new to websockets so its kind of confusing. Also, just to put it out there, i'm not trying to do this: websocket communication between servers;
My goal here is to basically use a socket to read data from another server (if this is possible?) I'll try to easily explain more below;
We'll assume there is a website called https://www.test.com (going to this website returns an object)
With a normal HTTP request, you would just do:
$.get('https://www.test.com').success(function (r) {
console.log(r)
})
And this would return r, which is an object thats something like this {test:'1'};
Now from what I understand with websockets, is that you cannot return data from them because you don't actually 'request' data, you just send data through said socket.
Since I know what test.com returns, and I know all of the headers that i'm going to need, is it possible to just open a socket with test.com and wait for that data to be changed without requesting it?
I understand how client-server communication works with socketio/websockets im just not sure if its possible to do server-server communication.
If anyone has any links to documentation or anything trying to help explain, it would be much appreciated, I just want to learn how this works. (or if its even possible)
Yes, I you can do what (assuming I understood your needs correctly). You can establish a websocket connection between two servers and then either side can just send data to the other. That will trigger an event at the other server and it will receive the sent data as part of that event. You can do this operation either direction from serverA to serverB or vice versa or both.
In node.js, everything is event driven. So, you would establish the webSocket connection and then just set up an event handler to be triggered when data arrives. The other server can then just send new data whenever it has updated data to send. This is referred to as the "push" model. So, rather than serverA asking serverB is it has any new data, you establish the webSocket connection and serverB just sends new data to serverA whenever that new data is available. Done correctly, this is both more efficient and more timely (as there is no polling interval and no cycles wasted asking for data when there is nothing new).
The identical model can be used between servers or client to server. The only difference with the client/server model is that the webSocket must be initially established client to server. With the server to server model, either server can initiate the connection.
You can think of a webSocket connection like establishing a phone call. Once the phone call is established, either side can just say something and the other end hears what they're saying. The webSocket connection is similar. Once its established, either side can just send some data to the other end and the other end will receive it. It's an open pipeline ready to have data sent either way. In node.js, when data arrives on that pipeline, it triggers and event so the listener will get that event and see the data that was sent.
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
I have a node server accepting websocket connections from the clients. Each client can broadcast a message to all of the other clients.
UPDATE: I am using https://github.com/websockets/ws as my library of choice.
At the moment, the server has an array with all of the connections. Each connection has a tabId. When one of the client emits a message, I go through all of the connections and check: if the connection's tabId doesn't match, I send the message to the client.
For loading issues, I am facing the problem of having to have more than one server. So, there will be say two servers, each one with a number of clients.
How do I make sure that a message gets broadcast to all of the websocket clients, and not only the ones connected to the same server?
One possible solution I thought is to have the connections stored on a database, where each record has the tabId and the serverId. However, even a simple broadcast gets tricky as messages to "local" sockets are easy to broadcast (the socket is local and available) whereas messages to "remote" sockets are tricky, and would imply intra-server communication.
Is there a good pattern to solve this? Surely, this is something that people face every day.
You could use a messagequeue like RabbitMQ.
When a client logs in to your server, create a consumer which listens to a queue which will receive messages directed to that particular client. And when the clients are sending messages, just use a publisher to publish them to the recipients queue.
This way it doesn't matter and you don't need to know on which nodes the clients are on, or if they jump from a node to another.
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.