Socket.io 'disconnect event' firing too late after reconnect - node.js

I've a simple code which set offline for the user in my sql database.
Server
io.on('connection', function (socket) {
ApiHelper.setUserOnline(socket.token);
socket.on('disconnect', function () {
ApiHelper.setUserOffline(socket.token);
});
}
So lets say,
An user connect to socket than he lost the network connection then reconnect.
I get this logs.
User connected (he is online)
Network losted.
Network losted but disconnect event has not received by server yet, so user is still online
User reconnect to network and then socket. User is still online.
Previous disconnect event recevied by server and user is set offline in database. But wait user has just reconnect so actually user must be online.
Because of disconnect event fired late we saw the user is offline in database.
How can I achieve this problem?

I think the best way is to store the socket.id in the sql database, so when a user login / connects you first check if that user.account is already online or not.
If it is online, then use the old socket.id to notify that the account has been opened from another window/device/whatever and replace it in the db for the new socket.id
and by replacing the old socket.id for the new one in the db, when a disconnect occurs you will really know if it was the currently active client (socket.id) using that account or not.
So at the disconnection : check if the disconnecting socket.id is in the database or not
(in other words : currently connected or not)
io.on('connection', function (socket) {
/* check if the account associated to this socket was
previously associated to another socket.id ...
(that might be currently connected or about to disconnect (lost connection)
*/
if(thisAccountWasOnlineBefore){
socket.to(old.socket.id).emit('exit', 'account opened from another session');
}
//pass also the socket.id so you can store it in the db
ApiHelper.setUserOnline(socket.id, socket.token);
socket.on('disconnect', function () {
//check if socket.id is associated to any account in the db
// if true : remove the socket.id and set as account status : offline
});
}

Related

SocketIO Presence System Redis

I'm building a presence system for live streaming using Socket.IO with Redis, with load balanced instances.
Everytime the user enters a room, I send to the server a socket.emit('join', roomId) and the server joins the user to the corresponding room.
Every minute, I refresh my dashboard and get all the users connected to the instance by using the redis adapter like this:
io.of('/').adapter.clients([req.params.name], (err, clients) => {
console.log(clients.length); // an array containing all connected socket ids
if (req.query.detail) {
res.send(clients);
} else {
res.send({onlineUsers: clients.length});
}
});
If one of my servers goes down, Redis automatically removes all the connected sockets and refreshes almost instantaneously the number of sockets in that room.
Now the problem:
Whenever the user connects, I call
io.of('/').on('connection', function (socket) {
//.. set user Online in database
});
for setting the user online.
And when the user disconnects I do
io.of('/').on('disconnect', function (socket) {
//... Set user offline
});
The problem is...
If my server goes down for a while, the user will be marked as Online forever.
Seen that I have more than 20k people online, it's almost impossible to send every minute a request to the server update the database with a LastSeen, because this would cause more than 300 writes/sec (I don't think mongodb will handle, will it ?)
Is there any how to solve this problem? Use redis to store some data in case the server goes offline for a while, I recover the last state ? How could that happen?
Is there any solution for this ? Even if socketio retries to reconnect, what if the server goes offline for 1 minute and the user leaves, he'll be online forever.
THANK YOU !

send message to specific client using soocket.io while socket id changes rapidly

I'm running a chat server using node.js and socket and want to send message to specific client.I use socket.id to send the message to the defined user,like this:
io.sockets.in(user socket.id).emit('message',message)
but there is a problem:
user remains connect but socket id changes rapidly(About once per second) so i can not use socket.id.I tried socket.join(user email) to use user email instead of socket id but after socket id changes it does not work any more.
what's the best way to solve this?session-id?If yes,how?chat application for clients runs on android device.
This is my code:
io.on("connection", function(socket) {
socket.on("login", function(useremail) {
socket.join(useremail);
});
//Here i want to send message to specific user
socket.on('messagedetection', (senderNickname,messageContent,targetuser) => {
//create a message object
let message = {"message":messageContent, "senderNickname":senderNickname}
//targetuser is the email of target user,joined to the socket in login
io.sockets.in(targetuser).emit('message',message)
});
socket.on('disconnect', function() {
console.log( ' user has left ')
socket.broadcast.emit("userdisconnect"," user has left ") });
Making my comment into an answer since it was indeed the issue:
The problem to fix is the rapidly disconnecting/reconnecting clients. That is a clear sign that something in the configuration is not correct.
It could be that network infrastructure is not properly configured to allow long lasting socket.io/webSocket connections. Or, if your system is clustered, it could be caused by non-sticky load balancing.
So, each time the connection is shut-down by the network infrastructure, the client-side socket.io library tries to reconnect creating a new ID for the new connection.

Socket.io client specific variable

How do I store session specific info in Socket.io?
var client={}; //is this static across all sockets (or connected clients) that are connected?
io.on('connection', function(socket){
client.connectiontime=Date.now();
});
//on another io.on('connection') for the same connected client
io.on('connection', function(socket){
store(client.connectiontime);
}
How do I use the client variable only for the operations related to the currently connected client if it is considered static?
First, each socket is given a name that can be used to refer to it, but that changes each time the same client connects so this would not be useful if it is supposed to remain after the client leaves. If your goal is to store the connection time somewhere (a database?) then you would have to get a unique identifier from the client that could be used to find them again similar to a login. You would then pass the date object into the function that handles storing that time.
You should note though, that 'connection' is only called the first time the socket connects. A connection is not the event you normally would be using for when a client does something unless they disconnects between each access of the server program.
If you are sure you want to just use the Client object, you would likely have to create a client array and use the socket id as a key to access the object later. You would then have something like
array[socket.id].connectiontime = Date.now()
var client={}; //is this static across all sockets (or connected clients) that are connected?
var clients = [];
io.on('connection', function(socket){
clients[] = {
id : socket.id
connectiontime : Date.now()
}
});
//on another io.on('connection') for the same connected client
io.on('connection', function(socket){
// Here you would search for the object by socket.id and then store
store(client.connectiontime);
}

How can I identify user when user reload the page

I'm creating a card game like crazy8. And I already publish prototype.
Look here http://himapoyo.com
My problem is, when I reload the page during the game, socket is disconnected and my socket.id is changed.
So server side program can't identify me. Now, server remove player from table when socket is disconnected.(because if server don't remove the player who server can't identify, game is stopped, so I program). But if I can identify user, I don't want to remove player who just reload the page from table.
QUESTION: how can I identify user when user reload the page?
I think using cookie as ID is best for this problem. Are there Other solutions?
Other options would include:
Using local storage on the client
Passing query string values in the url
Posting the user id as part as the refresh
Storing the user in a server side session
Storing user information in redis cache.
I'm sure there are many more but this should be enough
After reading your question I understand (correct me if I'm wrong) that once a user refreshes the page (reconnects his socket) the socket ID changes (obviously) and you can't identify the user.
First of all, you shouldn't keep track of user using his socket ID as the socket ID will change every time the user reconnects. You should keep track of the socket ID for communication with the user AFTER you have identified the user.
So, what I would do is make an "authenticate" event on the server side and disconnect those sockets who don't emit the "authenticate" event. This event can expect any form of credentials (JWT, username:password etc).
After the user has been authenticated, you can take that socket ID and map that to the user ID and use the socket ID for further communication.
So the server side flow will be like this:
io.on("connect", function (socket) {
socket.on("authenticate", function(data) {
// do auth and then make other events
// if auth
socket.auth = true;
socket.emit("message", "User authenticated");
});
// If socket fails to authenticate within 3 seconds after connect, disconnect it
setTimeout(function() {
if (!socket.auth) {
socket.disconnect();
}
}, 3000);
});
After going to your app, I found that there is no authentication required. This should not be the case as I can highjack any user's slot at the table by just sending his/her name.
If you are okay with that then perhaps you can just keep track of the user name and map it to socket ID on connect.

How to detect reconnect event in Socket IO

Whenever a client connects, I assign an id to that client using socket.id, and maintain it in the server for future reference. The problem I'm having right now is with disconnect/reconnect. I'm not even sure how to simulate this scenario, because if I reload the client page, it's technically creating a new client and connect to the server with different id. If I disconnect and connect the client manually then the client will again have a different id (socket.id).
I set up 'reconnect' event on both client and server using socket.on('reconnect',function(){...}), but it never seems to get called, given what I tried above.
So how would you go about simulating this scenario? And then what's the best way to detect if this new client is actually the same client that has disconnected?
There is a reconnect event on the client side inside which you can emit to the server and find the the reconnected client
socket.on('reconnect', function () {
console.log('you have been reconnected');
// where username is a global variable for the client
socket.emit('user-reconnected', username);
});
on the server you can get that as
socket.on('user-reconnected', function (username) {
console.log(username + ' just reconnected');
});

Resources