Socket.IO Emit is emitting to all clients - node.js

So I'm writing an app in NodeJS, and to preface my question please understand how my setup currently works:
I have Clients who connect via Socket.io to "Server X", and then my "Server X" connects via Socket.io to "Server Y", and they all send data back to each other via Socket.io.
Client <---> Server X <---> Server Y
Clients refer to users and their browsers, and then I have a node app running on both my server x and my server y.
So in the code below on my Server X, if you look at line 4 it works exactly as it should. It emits the message ONLY to the client who requested it.
io.on('connection', function(socket){
// This works just fine.
socket.emit('return_login', 'Test Message');
socket.on('login', function(data){
// This line correctly sends the data to Server Y
server_y.emit('login', data);
});
server_y.on('return_login', function(data){
// This emits to all connected clients???
socket.emit('return_login', data);
});
});
Now my problem is when "Server Y" emits return_login to server x, what I want to happen is for server x to take the value emitted by server y and just send it back to the original client, or web browser. But for some reason that line emits to ALL connected clients.
I have a laptop, this computer and my phone all testing this and every time that emit happens it sends to EVERYONE.
If someone could please help me with this I would greatly appreciate it. If I need to post more code please let me know.

I am not sure about your code. But I usually use room to emit to an user and callback function of socket. This is my solution using callback instead of return_login event
io.on('connection', function(socket){
socket.on('login', function(data, callback){
// Using callback instead of return_login event
server_y.emit('login', data, function(responseData){
// put this socket to room as name = user_id
socket.join(responseData.user_id);
// return result by callback
callback(responseData)
});
});
});
// emit to exactly user_id with return_login event if you want
io.to(user_id).emit('return_login', {key: 'ok'})

It is sending to all clients because you have installed a separate listener for each socket for the return_login message. So, when one client logs in and the return_login message is sent back to your server, you have a separate listener for every single socket that sees that message and forwards it to that socket. In this way, it gets sent to every connected socket.
One way to fix that is to make sure that the return_login message is only sent to the socket that it belongs to. If you can send a socket.id with that message and have that server echo that id back as part of the response, then you can check that when you receive the message to make sure you only send it to the socket that it belongs to.
This is one of the issues with a pure message-based system. You are trying to do a request/response where only the requester sees the response, but socket.io isn't a request/response system. A response is sent to all listeners of that particular message and since you have a listener for that message for every single socket that is connected, every single socket is seeing it and then forwarding it on to it's client.
So, with a corresponding modification to the other server to echo back the id value, you could do this:
io.on('connection', function(socket){
// This works just fine.
socket.emit('return_login', 'Test Message');
socket.on('login', function(data){
// add our id so we can identify our response back
data.id = socket.id;
server_y.emit('login', data);
});
let fn = function(data) {
// check to see if this message is destined for this socket
if (data.id === socket.id) {
// make data copy with id value removed so
// we don't send that to the client
let tempData = Object.assign({}, data);
tempData.delete(id);
socket.emit('return_login', tempData);
// we're done with this listener so remove it
server_y.removeListener('return_login', fn);
}
});
server_y.on('return_login', fn);
});
It might be tempting to just remove your listener for the return_login message after you receive it, but that causes a race condition if two clients happen to both be in the process of logging in at the same time and thus both have listeners at the same time, then the first message would be received by both listeners.

Related

Can't receive data from io.emit()

I am trying to send data to all clients just to test how socket.io works.
I can't seem to get any data back from the server, no matter how I try.
The event is fired, so the data gets to the server side, but it doesn't arrive to the clients.
On the server side, I do this:
io.on('connection', (socket) => {
socket.on('chatMessage', data => {
console.log(data);
io.emit(data);
});
});
The console.log() here works, so its fine until that line.
On the client side I tried to simplify things to see the result like this:
const socket = io.connect('http://localhost:5000');
socket.on('connect', () => {
socket.emit('chatMessage', {a:"fsdfsdfsd"});
});
socket.on('chatMessage', data =>{
console.log(data);
});
The socket.emit() fires, but it doesn't arrive anywhere.
I have never used socket.io before, but I read the docs and it said io.emit() sends the data to all connected clients.
Just in case, I tried with multiple clients connected, but it still doesn't work.
First thing I would recommend is to check if the client is really connecting to the server socket by adding a console.log() inside your on connection block in the server side.
Also, your io.emit(data); in the server side won't work since the first parameter of the emit function should be the event name.

Socket.io emit looping

I would like to be consistent with my socket.io "hooks". I use this format below pretty consistently, and it does not appear to fall into an infinite loop.
// inside my nodejs app
socket.on('foo', function (data) {
socket.emit('foo');
});
It only runs once when the hook is triggered from the client side, so it appears to be safe. But why would it not go in an infinite loop in the server side ? is this by accident or design ?
This behavior is by design, not a coincidence or accident
client: emit a message to server with a method
server: receive message from client with an event handler
server: send a message to client with a method
client: receive a message from server with an event handler
server socket receive a message from client
client.socket.emit('foo',aMessage) on foo event
socket.on('foo', function (data) {
//then server emit a message to connected clients
//not to server this is why not go through an infinite loop
socket.emit('foo');
});
then 'client' receive the message from server
chat.socket.on('foo', function(data){
console.log('got new message from server...',data)
})

How can a new client get socket data if it hasn't been emitted since connected

I have googled for hours but cannot seem to find an answer. I have clients emitting and listening to sockets, some change very frequent while others might only change every few minutes. My question is when a new client connects (or connected client refreshes) how does it get the data from those that only change every few minutes before the server emits it?
Client code
var socket = io.connect();
socket.on('frequent', function(data){
$frequent.text(data);
});
socket.on('occasional', function(data){
$occasional.text(data);
});
Server code (Theoretically. The emits work fine are are not the problem)
io.emit('frequent', data);
io.emit('occasional', data);
When a client connects to the server, the server can immediately send the current state of the data:
io.on('connection', function(socket) {
socket.emit("status", currentData);
});
This way, the client gets the current status immediately and doesn't have to wait for the next change.

Verify that the client has received the message from the server

In a chat service that I'm building, I need to send messages directly from the server.
I have found no solution, there is an example in the documentation:
// SERVER
io.sockets.on('connection', function (socket) {
socket.on('ferret', function (name, fn) {
fn('woot');
});
});
// CLIENT
socket.on('connect', function () { // TIP: you can avoid listening on `connect` and listen on events directly too!
socket.emit('ferret', 'tobi', function (data) {
console.log(data); // data will be 'woot'
});
});
but does the opposite of what I need!
I need to do the emit from the server and receive a confirmation of receipt from the client!
Is there a way to do this?
there is no guarantee as the connection can be killed before the servers message reaches the client. thus there is also no event on the server like "clientGotMessage". if a message MUST reach the user there is no other way than to tell the server that you received the message on the client.
You can do this 'easy' by sending a number down. client and server keep track of that number. each time the server sends, it counts up, each time the client receives, it counts up. When the client sends something, it sends the number, so the server will see if the client has everything. If the client missed a message, the next message will have a number that the client wont accept and request the lost message from the server.

node.js + socket.io broadcast from server, rather than from a specific client?

I'm building a simple system like a realtime news feed, using node.js + socket.io.
Since this is a "read-only" system, clients connect and receive data, but clients never actually send any data of their own. The server generates the messages that needs to be sent to all clients, no client generates any messages; yet I do need to broadcast.
The documentation for socket.io's broadcast (end of page) says
To broadcast, simply add a broadcast flag to emit and send method calls. Broadcasting means sending a message to everyone else except for the socket that starts it.
So I currently capture the most recent client to connect, into a variable, then emit() to that socket and broadcast.emit() to that socket, such that this new client gets the new data and all the other clients. But it feels like the client's role here is nothing more than a workaround for what I thought socket.io already supported.
Is there a way to send data to all clients based on an event initiated by the server?
My current approach is roughly:
var socket;
io.sockets.on("connection", function (s) {
socket = s;
});
/* bunch of real logic, yadda yadda ... */
myServerSideNewsFeed.onNewEntry(function (msg) {
socket.emit("msg", { "msg" : msg });
socket.broadcast.emit("msg", { "msg" : msg });
});
Basically the events that cause data to require sending to the client are all server-side, not client-side.
Why not just do like below?
io.sockets.emit('hello',{msg:'abc'});
Since you are emitting events only server side, you should create a custom EventEmitter for your server.
var io = require('socket.io').listen(80);
events = require('events'),
serverEmitter = new events.EventEmitter();
io.sockets.on('connection', function (socket) {
// here you handle what happens on the 'newFeed' event
// which will be triggered by the server later on
serverEmitter.on('newFeed', function (data) {
// this message will be sent to all connected users
socket.emit(data);
});
});
// sometime in the future the server will emit one or more newFeed events
serverEmitter.emit('newFeed', data);
Note: newFeed is just an event example, you can have as many events as you like.
Important
The solution above is better also because in the future you might need to emit certain messages only to some clients, not all (thus need conditions). For something simpler (just emit a message to all clients no matter what), io.sockets.broadcast.emit() is a better fit indeed.

Resources