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

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

Related

Client (JRE) read server (node) variables directly?

I am trying to set up a server where clients can connect and essentially "raise their hand", which lights up for every client, but only one at a time. I currently just use the express module to send a POST response on button-click. The server takes it as JSON and writes it to a file. All the clients are constantly requesting this file to check the status to see if the channel is clear.
I suspect there is a more streamlined approach for this, but I do not know what modules or methods might be best. Can the server push variables to the clients in some way, instead of the clients constantly requesting a file? Then the client script can receive the variable and change the page elements accordingly?
Usually, this kind of task is done by using WebSockets. Since you already have socket.io set up, it'd be great to reuse it.
From the server, start emitting different messages:
socket.emit("hand", { userId: <string> });
From the client, listen to the new event and invoke whatever the appropriate behavior is:
socket.on("hand", (payload) => {
// payload.userId contains user ID
});

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.

io.emit() not emmiting data to all clients

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.

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.

Private messaging through node.js

I'm making a multiplayer (2 player) browser game in JavaScript. Every move a player makes will be sent to a server and validated before being transmitted to the opponent. Since WebSockets isn't ready for prime time yet, I'm looking at long polling as a method of transmitting the data and node.js looks quite interesting! I've gone through some example code (chat examples, standard long polling examples and suchlike) but all the examples I've seen seem to broadcast everything to every client, something I'm hoping to avoid. For general server messages this is fine but I want two players to be able to square off in a lobby or so and go into "private messaging" mode.
So I'm wondering if there's a way to implement private messaging between two clients using nodejs as a validating bridge? Something like this:
ClientA->nodejs: REQUEST
nodejs: VALIDATE REQUEST
nodejs->ClientA: VALID
nodejs->ClientB: VALID REQUEST FROM ClientA
You need some way to keep track of which clients are in a lobby together. You can do this with a simple global array like so process.lobby[1] = Array(ClientASocket, ClientBSocket) or something similar (possibly with some additional data, like nicknames and such), where the ClientXSocket is the socket object of each client that connects.
Now you can hook the lobby id (1 in this case) onto each client's socket object. A sort of session variable (without the hassle of session ids) if you will.
// i just made a hashtable to put all the data in,
// so that we don't clutter up the socket object too much.
socket.sessionData['lobby'] = 1;
What this allows you to do also, is add an event hook in the socket object, so that when the client disconnects, the socket can remove itself from the lobby array immediately, and message the remaining clients that this client has disconnected.
// see link in paragraph above for removeByValue
socket.on('close', function(err) {
process.lobby[socket.sessionData['lobby']].removeByValue(socket);
// then notify lobby that this client has disconnected.
});
I've used socket in place of the net.Stream or request.connection or whatever the thing is.
Remember in HTTP if you don't have keep-alive connections, this will make the TCP connection close, and so of course make the client unable to remain within a lobby. If you're using a plain TCP connection without HTTP on top (say within a Flash application or WebSockets), then you should be able to keep it open without having to worry about keep-alive. There are other ways to solve this problem than what I've shown here, but I hope I got you started at least. The key is keeping a persistent object for each client.
Disclaimer: I'm not a Node.js expert (I haven't even gotten around to installing it yet) but I have been reading up on it and I'm very familiar with browser js, so I'm hoping this is helpful somehow.

Categories

Resources