io.emit() not emmiting data to all clients - javascript

I am creating a chat application.
In the server, we had an event.
io.emit('userAgentConnectMsg', data);
In the client, we are listening to this event.
socket.on('userAgentConnectMsg', (data) => { console.log(data)} );
Suppose think I have two clients connected to server.
If one client gets message another client will not get that.
This is happening randomly.
I changed code from io.emit to io.sockets.emit still the same issue.
And the worst thing is I am not getting any error either from client or server.
And I am not at all sure where the issue is happening from either it is from server or client.
I did some analysis for socket io before the io.emit
I logged io.sockets.adapter.sids which basically return all connected socket ids.
the issue is sometimes it shows only some sockets and sometimes it shows empty object.
Some can help me if you have faced the same issue.

Related

Socket io Client in ReactJS is getting multiple emits from the server

I am building a chat room with some extra features, and I have a socket io server, as well as a socket io client in ReactJS.
I have it so if someone pins a message or changes the settings of the chat room, it emits the changes to the server, the server saves those changes, and then emits them back out to everyone so everyone is synced.
The settings and pinned messages successfully transfer and are communicated, I have console.logs at almost every step of the transfer, the client logs out a single request, the server logs that it emits a single time, but the client logs that it recieved an emit multiple times, sometimes a couple of times like 2-6 requests, sometime it gives out 60 requests. I'm trying to really control the efficiency, but I have no idea what is causing this
I'm not sure it matters but another thing of note is that the client also connects to a native WebSocket server to get messages from another source
what the client generally looks like is:
effect(()=>{
socket.emit('changeSetting', setting)
},[setting])
socket.on('recieveSetting', (arg)=>{
if(arg != setting){
setSetting(arg);
}
})
the server then looks like this:
socket.on('changeSetting', (arg)=>{
storedSetting = arg
socket.emit('recieveSetting', storedSetting)
})
That's the general structure, so I don't think its an issue of the code, more like if reactJS or connecting to the other websocket causes it to get multiple emits

Closing single node server connection closes them all

I barely ask any questions on Stack Overflow, but this one is beyond me. I guess I'm missing something basic as I'm pretty new to Node server.
Our application is pretty basic. The server is supposed to receive a handful of text lines (data), merge and parse them, and once the connection is closed (data sending is over) it sends the data to the api.
var net = require('net');
var fs = require('fs');
const axios = require('axios')
const server = new net.Server();
server.listen(PORT, IP);
server.on("connection", client => {
client.write("Hello\n");
console.log('connected');
let received = "";
client.on("data", data => {
received += data
console.log("Partial data is: " + data);
});
client.on("close", () => {
received = received.toString('utf8');
fs.appendFile('log.txt', received, function (err) {});
received = received.replace(/(?:\r\n|\r|\n)/g, "||");
axios.post(APIADDRESS, {data: received});
console.log('Full data is: '+ {data: received});
});
});
To send the data I'm simply running a netcat or nc using the netcat ipaddress port, that's not a problem. It's connecting fine, status message is received.
The thing is - once I open two or more connections from two DIFFERENT SSh servers something weird happens. I can send the line after line just fine. The server reports back "partial data" debug without problem, for both of them.
However, once I close one of the connections (ctrl+c) they BOTH close.
In the end, only the data from the manually closed connection is received. The other one, from a separate nc on a separate ssh server never reaches the client.on("close") part, it seems. It's just terminated for no reason.
Any ideas? I don't even know where to start.
//EDIT
Just tested it from my pc and some ssh mobile app using separated SSH servers. As soon as ctrl+c is sent at any device it closes the connection for all clients.
//Forgot to mention I'm running pm2 to keep the server up. Once I turned on the script by hand, ignoring pm2 - it works fine. Weird. It is happening because of PM2.
I would guess that you have Putty configured to ‘Share SSH connections if possible’. Per some doc, when doing so:
When this mode is in use, the first PuTTY that connected to a given server becomes the ‘upstream’, which means that it is the one managing the real SSH connection. All subsequent PuTTYs which reuse the connection are referred to as ‘downstreams’: they do not connect to the real server at all, but instead connect to the upstream PuTTY via local inter-process communication methods.
So, if you Ctrl+C the PuTTY session that is managing the actual shared connection, they both lose their connection.
You could presumably disable this shared connection feature at either the client or server end of things since both must be enabled for sharing to occur.
To anyone coming here in the future.
If you are using pm2 with --watch enabled and the text log file is in the same folder as your main server script... That's the reason why it drops the connection after a single client disconnects. It just detects that the log has changed.
I'm not facepalming, that's not even funny.

How synchronise socketIO connection ID's on client and server?

I have a javascript GameClient that uses SocketIO to send messages to a nodeJs server. Multiple users can open the GameClient separately and send messages to the server.
GameClient
GameClient ---> NodeJS Server
GameClient
The server can send messages to specific clients using io.to(socketid).emit(). The code looks something like this:
CLIENT
this.socket = io({ timeout: 60000 })
this.socket.on('connect', () => Settings.getInstance().socketid = this.socket.id)
this.socket.on('reconnect', (attemptNumber:number) => console.log("reconnecting..."))
const json = JSON.Stringify({socketid:this.socket.id, name:"Old Billy Bob"})
this.socket.emit('user created', json)
SERVER (simplified for clarity, just keeping track of one user here)
user = {}
io.on('connection', (socket) => {
console.log('new connection')
socket.on('disconnect', () => {
console.log('user disconnected')
});
socket.on('user created', (json) => {
user = JSON.parse(json)
});
});
// demo code, send a message to our user
io.to(user.socketid).emit("message to one user")
PROBLEM
When the client browser tab becomes inactive for any reason at all, the client disconnects and reconnects and gets a new socket connection ID. This actually happens a lot in Chrome and Safari.
The server only knows the old connection id, so now it can't send direct messages any more. How do I keep the socket connection id synchronised on the client and server?
Since the server also gets a reconnected event, how does it know which user reconnected?
The answer to your question is quite simple: you need a way to identify who is who. And that is not socket.id because this only identifies sockets, not users, as you've already noticed.
So you need some authentication mechanism. Once a user authenticates you can reuse his true id (whether it is simply a name or an integer in a database is irrelevant). And then on the server side you keep a collection of pairs (true_id, socket_id). And whenever a message comes to that user, you broadcast it to all matched socket.io objects.
Edit: So here's the flow:
Client authenticates with the server, the server sends him his own true_id, which the client stores somewhere. The client may also store some session_id or maybe some other mechanism that will allow him fast reauthentication in case of disconnection (note: do not store credentials, its a security issue).
The server keeps track of (true_id, socket_id) pairs in the form of a double way, mutlivalue map (it's an implementation detail what kind of data structure should be used here, maybe two {} objects is enough). If a connection dies then (true_id, socket_id) entry is removed. Note that for a given true_id there still may be some other socket_id alive. And so it doesn't mean that the user disconnected. It only means that this particular channel is dead.
Users don't care about socket_id, they only care about true_id. What you emit is {target_id: true_id, ...} instead of {target_id: socket_id, ...} on the client side, when you want to send a direct message.
When the server receives such message with true_id inside, it retrieves all (true_id, socket_id) pairs and passes the message to all of these sockets (note: maybe you don't even need socket_id, you can simply store socket objects here). Although this is a business logic: do you allow multiple connections per user? I would. There are many edge cases here (like for example a client thinks that he disconnected, but the server thinks he is still connected, etc) and making this 100% correct is unfortunately impossible (due to the nature of networking). But with a bit of effort it is possible to make it work 99% of the time.
If a connection dies then it is your client's responsibility to automatically reconnect and reauthenticate. New socket_id for old true_id is generated on the server side.
Let me emphasize this again: clients don't care about socket_id at all. Because that doesn't identify them. This only identifies a channel. And only the server cares about this information.

Node.js error handling with socket.emit

I have some node.js client side code like this:
socket.emit('clickAccept', { myrecid: recid });
Server side node.js code gets it fine and all is well.
If I take the server down to simulate a server side outage, then click the button that fires this socket.emit on the client side, this happens:
Nothing really, I guess it might eventually time out
When I bring the server back up, the clicks end up being sent to the server and the server acts on them (TCP-like I Guess).
What I want to happen is for those socket.emit calls to die after a short timeout and not send when the server comes back up, it causes all sorts of confusion because if they click 3 times, nothing happens, then when/if the connection or server comes back up they get 3 reactions all at once.
Also, if they click and it times out because the server is down, I would like to show an error to the client user to let them know that basically the click didn't work and to try again.
I know how to act on and show an error if the socket goes down but I don't want to do this if they aren't trying to click something at that time. No sense is firing errors at the user because the socket went down briefly if they have no need to do anything at that moment.
So, to be clear, I only want to show an error if they click on the button and the socket between the client and server is down. AND... If they get an error, I want to kill that emit, not save it all up and fire it and all the other clicks when the server comes back up a few seconds later.
Thanks in advance and I hope that was at least reasonably clear.
The root of your issue is that socket.io attempts to buffer any data that it can't currently send to the server (because the connection to the server is disconnected) and when the server comes back up and the connection is restored, it then sends that data.
You can see the technical details for how this works here: socket.io stop re-emitting event after x seconds/first failed attempt to get a response
You have several implementation options:
If socket.io already knows the client is not connected to the server, then don't buffer the data (perhaps even give you back an error to show to your user).
When socket.io reconnects and there was data buffered while the connection was down, clear that data and throw it away so old data isn't sent on a reconnect.
Implement a timeout to do one of the above after some sort of timeout.
So, to be clear, I only want to show an error if they click on the button and the socket between the client and server is down. AND... If they get an error, I want to kill that emit, not save it all up and fire it and all the other clicks when the server comes back up a few seconds later.
Probably, the simplest way to do that is to implement a version of what is shown in the above referenced answer:
Socket.prototype.emitWhenConnected = function(msg, data) {
if (this.connected) {
this.emit(msg, data);
return null;
} else {
return new Error("not connected");
}
}
Then, switch your code from using .emit() to use .emitWhenConnected() and check the return value when using it. If the return value is null, then no error was detected. If the return value is not null, then there was an error.
Thanks for the other answers and help. I ended up solving this in a super simple way. See below:
if (socket.connected){
// Do your thing here
} else {
// Throw error here that tells the user they're internet is likely down
}
Hope this helps someone out there, it was a huge improvement in our code to make sure that user's are getting proper feedback when if they have brief network/internet outages.

Socket.io not listening

im using this simple code
Client.html
http://pastebin.com/c1C2F9MQ
Server.js
http://pastebin.com/Cqjhjfwm
Everything working fine till the last step of my client code
socket.on('add', function(data) {
socket.broadcast.emit('AAA');
});
It seems that the socket add never comes but on my server I have
socket.on('Text', function(data) {
socket.emit('add', data);
});
And I tested if socket text was coming and it does, I cant find the problem
Thanks
socket.broadcast.emit sends message to all sockets connected to server except the concerned socket. So most likely add does come to server but it broadcasts AAA which your client cannot get. Use io.sockets.emit to send to all connected sockets. Change this
socket.broadcast.emit('AAA');
to
io.sockets.emit('AAA');
Update
I too overlooked that you are calling socket.broadcast.emit from client not from server. It would have shown error on browser console, since broadcast is absent on client.
Currently your on('add') code on the client side is within the on('connect') event which is not correct...
You need to take it ouside there, so it becomes:
socket.on('connect', function () {
$('#button').click(function() {
var addtext = $('#text').val();
socket.emit('Text', addtext);
});
});
socket.on('add', function(data) {
socket.emit('AAA');
});
EDIT: I also just noticed you had socket.broadcast.emit() in your client side code. As far as I know there's no concept of broadcasting from the client. If you want to broadcast something then the client should send it to the server then the server broadcasts to the other clients.

Categories

Resources