Socket.io stops working after multiple refreshes - javascript

I have a socket.io connected to a https server listening for javascript to emit information to it. I am finding that when I refresh, the socket is recording the connection - so if i console.log something in the on('connection', function(socket) { } part of the code, I will see a response in the console.
What I am finding though, is that on the server side, the socket.on() calls are not picking up anything.
So if I have a line in Javascript which is socket.emit('ping', msg), 'ping' will work on the server side for the first 9-10 refreshes, and then on the 11th refresh it will stop working - and it will stop working on all devices and all browsers. So if I open another browser and try to load it up, it won't work either.
I have checked to see if there are multiple connections building up or something like that, but I am not seeing anything. Any idea why this weird behaviour is happening?
Server Side
io.use(function(socket, next) {
// Authentication of the user is happening here - checking for cookies, sessions, etc.
}).on('connection', function(socket) {
// Setting up some variables and checking other stuff
var socketID = socket["id"];
var username = socketUsers[socketID];
locked[socketID] = {};
// Checking for the username
if(username == '' || typeof username == "undefined") {
// Emit some stuff back to Javascript
io.to(`${socketID}`).emit('criticalError', true);
return false;
}
else {
// We have a valid login, so connect to the server
++connected;
// This line will console log every time I refresh
console.log("Some text");
socket.on('ping', function(msg) {
// This line will console log for the first 9-10 refreshes, and then it suddenly stops working. Any ideas why?
console.log('Server was pinged by ' + username);
});
// This line will console log every time I refresh
console.log("Some other text");
}
});
Client side
var socket = io('https://example.com:1337');
$(document).ready(function() {
socket.emit('ping', true);
});

This is an interesting problem that you are facing. There are multiple points to check on - However the first problem I would suggest is with the below code:
var socket = io('https://example.com:1337');
$(document).ready(function() {
socket.emit('ping', true);
});
If this is the exact code on the client side, chances are that the socket.emit is getting called before the connection handshake goes through. This can be random as sometimes the document ready event might be delayed. Try setting a timeout for the emit event -
var socket = io('https://example.com:1337');
$(document).ready(function() {
setTimeout(function(){socket.emit('ping', true);},2000);
});
and give it a try. If this works, you found the problem. Next step is to ensure authentication is working correctly - Without the complete code, advising on this is difficult. Feel free to edit the question and share the code if your issue isn't resolved.

Related

Send SocketIO message on unload or upon confirmation onbeforeunload

I have a server application that performs firmware update on remote devices via radio.
Sometimes the update may continue like forever (if there is disturbance in the radio network). In that case the user may wish to interrupt the update by refreshing or leaving the page. In this case I have to:
Alert the user that he is about to interrupt the update (which is not recommended), and if the user is sure about his decision (and confirm):
Send a socketIO event to the server to inform that the update should be canceled.
I searched through the internet and came across to different solutions. The best of them are assigning handlers to the global object events onbeforeunload and onunload.
Using onbeforeunload I don't have choice. Can't send the SocketIO message only if the user confirm, and do nothing if the user decide to wait the update to finish. I can send the SocketIO message to the server, but what if the user decide to wait? The harm is already done.
Using onunload - it seems that doesn't work for me. I see that the socket event is being send by the browser, but before handled by the server the connection is closed.
Is there any way keep the SocketIO connection or delay the browser refresh so the server can handle the event?
I think that this is problem in the server because it runs on CPU with very limited resources, even CPU speed. It is ARM A7 architecture.
There is a way:
Server: Create a user id with:
var custom_id = 0;
io.engine.generateId = (req) => {
let id = custom_id++;
return id++; // very basic but unique id
}
Server: Create a listener to an attempt to close event and store the user id attempt:
var userAttempt = 0;
socket.on('attempt to close', function(data){
userAttempt = socket.id;
console.log(data)
})
Server: Modify disconnect event to check which id user is disconnected:
socket.on('disconnect', function(){
if(userAttempt === socket.id){
console.log("user attempt and close");
//the update should be canceled !!!!
}
console.log('user disconnected');
});
Client: Create the event emitter attempt to close inside onbeforeunload, this event is always going to be fired if user attempt to close or reload the page.
window.onbeforeunload = function (e) {
e.returnValue = " ";
var socket = io();
socket.emit('attempt to close', "user attempt to close");
};
If user try to close the tab, we fire the event attempt to close, we check if user close or not the tab checking the disconnect event. If user disconnected is the same as user attempted to close, the update should be cancelled.
What I did is place the socket event in
window.addEventListener('unload', function (e) {});
window.addEventListener('beforeunload', function (e) {
e.preventDefault();
e.returnValue = ' ';
});
window.addEventListener('unload', function (e) {
socket.emit('some event', {data to be sent});
});
it worked fine to accomplish the task
Although this doesn't fully answer the question, here are some observations I've made using Chrome Version 95.0.4638.54 (Official Build) (64-bit) and socket.io.
The window.beforeunload or window.unload events cause the socket to disconnect before you can send a message via socket.io that the window/tab is closing.
It's probably better to manage this on the server where you can detect the disconnection:(https://socket.io/docs/v4/server-api/)
socket.on("disconnect", (reason) => {
// reason = disconnect, server shutting down, ping timeout,
//transport close, transport error
console.log('disconnect:'+reason+ ' '+socket.id);
//then do your stuff
});
If you're managing the connections between user ids and socket ids on the server it's posssible to track who is doing what etc. I suspect that the answers given may have worked 3 yrs ago but Chrome has changed a lot and I always got a socket disconnect before I could use socket.emit.
Hopefully this will help.
Update 6-11-21
The latest version (v4.1.0 =>) has by default 'closeOnBeforeunload' as true
This will cause the socket to close before you can send a message.
You can of course set this to false.
(quote from docs):
"Whether to (silently) close the connection when the beforeunload event is emitted in the browser.
With closeOnBeforeunload set to false, a disconnect event will be emitted by the Socket instance when the user reloads the page on Firefox (but not on Chrome or Safari).
With closeOnBeforeunload set to true, all browsers will have the same behavior (no disconnect event when reloading the page). But this might cause issues if you use the beforeunload event in your application.
"
see: https://socket.io/docs/v4/client-options/#closeonbeforeunload

Sending data in a timer by JS WebRTC crashes when reloading before the end

I have this simple piece of code in a server.js javascript file served by node:
function multiStep(myConnection, data) {
var i=0;
var myTimer=setInterval(function() {
if (i<data.length){
var element=JSON.stringify(data[i]);
console.log("mando elemento: "+element);
myConnection.send(element);
i++;
}
}, 3000);
}
//require our websocket library
clearInterval(myTimer);
var WebSocketServer = require('ws').Server;
//creating a websocket server at port 9090
var wss = new WebSocketServer({port: 9090});
//when a user connects to our sever
wss.on('connection', function(connection) {
loadJSON(function(response) {
//when server gets a message from a connected user
connection.on('message', function(message){
console.log("Got message from a user:", message);
});
var json = JSON.parse(response);
multiStep(connection, json, 0);
})
});
loadJSON simply loads a json data file from another web site.
When I run the client application the first time or when the timeout has ended everything goes fine. Yet if I reload the page while the timeout is not finished I get a crash when I try to use the connection of the old page on the server with report:
/var/www/html/MATERIALI/phonegap/node_modules/ws/lib/WebSocket.js:219
else throw new Error('not opened');
^ Error: not opened
at WebSocket.send (/var/www/html/MATERIALI/phonegap/node_modules/ws/lib/WebSocket.js:219:16)
at null. (/var/www/html/MATERIALI/phonegap/WebRTC/server.js:36:9)
at wrapper [as _onTimeout] (timers.js:261:14)
at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)
As a matter of fact I could simply ignore the old session given the page is reloaded. How do I avoid the server to crash in these circumstances?
Ok, I think I found the solution; function multiStep becomes:
function multiStep(myConnection, data) {
var i=0;
clearInterval(myTimer);
myTimer=setInterval(function() {
if (i<data.length){
var element=JSON.stringify(data[i]);
console.log("mando elemento: "+element);
try {
myConnection.send(element);
console.log("mandato elemento");
} catch(err) {
console.log('Websocket error: %s', err);
}
i++;
} else {
}
}, 3000);
}
And it does not crash any longer.
You need to do some checking along the way. Your code assumes that everything is 100%.
var json = JSON.parse(response);
multiStep(connection, json, 0);
You assume there is data in response (it might be empty, or contain non-json data)
You should also check that json is a valid array before passing it to multiStep
The function multiStep also assumes that data.length will return something numeric
This may not be the complete answer, but it should give you a start on making your code more robust.
It's probably failing at myConnection.send(element); but that is probably only a symptom of your lack of checking along the way (you can also check if myConnection is still valid before you send something to it)
Referring to https://developer.mozilla.org/en-US/docs/Web/API/WebSocket, you should be able to check the myConnection.readyState value:
Ready state constants
These constants are used by the readyState attribute to describe the state of the WebSocket connection.
Constant Value Description
CONNECTING 0 The connection is not yet open.
OPEN 1 The connection is open and ready to communicate.
CLOSING 2 The connection is in the process of closing.
CLOSED 3 The connection is closed or couldn't be opened.
Your code will look like this now:
console.log("mando elemento: "+element);
if (myConnection.readyState === 1)
myConnection.send(element);
else
console.log("web socket not open");

PeerJS Auto Reconnect

I have recently developed a web app using PeerJS, and am trying to add reconnect functionality.
Basically, my app works by someone creating a server that clients then connect to. The server person can control what the hosts are doing but its basic two way communication.
If a client disconnects they simply reconnect and it works normally. However if the server user refreshes the page, or their computer crashes then they need to be able to re-establish control over the clients.
The start of this is by regaining the original connection ID and peer api ID, which is fine and easy as they are stored in a database and assigned a unique ID the server user can use to query them. Then to enable the client to reconnect I do this upon close:
// connection is closed by the host involuntarily...
conn.on('close', function() {
// if the clients connection closes set up a reconnect request loop - when the host takes back control
// the client will auto reconnect...
connected = false;
conn = null;
var reconnect_timer = setInterval(function () {
console.log('reconnecting...'); // make a fancy animation here...
conn = peer.connect(connectionid, {metadata: JSON.stringify({'type':'hello','username':username})});
// upon connection
conn.on('open', function() { // if this fails need to provide an error message... DO THIS SOON
// run the connect function...
connected = true;
connect(conn);
});
// didnt connect yet
conn.on('error', function(err) {
connected = false;
});
if(connected === true) {
clearInterval(reconnect_timer);
}
}, 1000);
});
This appears to work, as on the server end the client looks like they have reconnected - the connect function has fired etc. However messages cant be sent between, and the client console says:
Error: Connection is not open. You should listen for the `open` event before sending messages.(…)
Where the 'open' event is shown as having been listened to above...
I hope this is clear - any help is appreciated :)
So in the end to create an auto reconnect script, I simply dealt with the client end of things, ensuring the server was set to the same api_key (for cloudservers) and key:
peer = new Peer(return_array.host_id, {key: return_array.api_key});
and then having the client, upon connection closing:
// connection is closed by the host involuntarily...
conn.on('close', function() {
// if the clients connection closes set up a reconnect request loop - when the host takes back control
// the client will auto reconnect...
peer.destroy(); // destroy the link
connected = false; // set the connected flag to false
conn = null; // destroy the conn
peer = null; // destroy the peer
// set a variable which means function calls to launchPeer will not overlap
var run_next = true;
// periodically attempt to reconnect
reconnect_timer = setInterval(function() {
if(connected===false && run_next===true) {
run_next = false; // stop this bit rerunning before launchPeer has finished...
if(launchPeer(false)===true) {
clearInterval(reconnect_timer);
} else run_next == true;
}
}, 1000);
});
Where launch peer will attempt to launch a new peer. To ensure continuity the new id from the client replaces the old id from the client and everything is a smooth takeover. The hardest part in the end was having the "setInterval" only fire once which is achieved (badly...) through use of boolean flags.
Thanks to anybody who read and thought how they could help :)

data send by firefox tcp socket.send() can't retrive until close the socket

I'm trying to send some data from Firefox to java desktop application .so my java class work as a server and Firefox script work as a client .when i test it using another java class which is client.java data successfully sent to server.java how ever when i use this firefox script to send data ,it actually connect to the server .but send("text"); doesn't work realtime .i mean sever shows received data when i close the socket socket.close(); . but i know there is no problem with server.java code.
this doesn't work
setTimeout(function(){socket.send("i'm firefox");},5000); // because socket isn't closed yet
this work
setTimeout(function(){socket.send("i'm firefox");},5000);
setTimeout(function(){socket.close();},6000);
but i really don't want to close the socket because i want to send lot of data one by one.
here is the complete code tested on scratchpad [-browser]
var tcpSocket = Cc["#mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
var socket = tcpSocket.open("127.0.0.1", 5000);
setTimeout(function(){socket.send("i'm firefox");},5000);
//setTimeout(function(){socket.close();},8000);// i'm firefox text retrieve by server when run this line / when close the socket.
i think java code isn't important.but here it is.
I'm asking why do i need to close the socket to send data ? and how can i send data without close the socket ?
update
i made a Gif to show my problem here you can see data not sending real time but when socket is closed all the data flushed .
It's actually working. Your data is being received, but you're currently waiting for a new line to print your received messages:
while ((inputLine = in.readLine()) != null) {
System.out.println("Server: " + inputLine);
out.println(inputLine);
if (inputLine.equals("Bye.")) {
break;
}
}
Which currently only happens when you close the socket. Just add a new line at the end of your messages and it will work as expected:
var tcpSocket = Cc["#mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
var socket = tcpSocket.open("127.0.0.1", 5000);
function sendMessage(msg){
socket.send(msg + "\n");
}
setTimeout(function(){
sendMessage("hi mark");
},3000);
setTimeout(function(){
sendMessage("hi johnny");
},5000);

Storing RabbitMQ connection in NodeJs

I currently forced to create a new RabbitMQ connection every time a user loads a page on my website.
This is creating a new TCP connection every time. However, i'm trying to reduce the number of TCP connections i make to Rabbit with the NodeJS AMQP plug in. Here is what i have:
var ex_conn = get_connection(uri); //http:rabbitm.com
if(ex_conn == false) {
var tempConn = amqp.createConnection({
url: uri
});
connections.push({
host: uri,
obj: tempConn
});
}
else {
var tempConn = ex_conn.obj;
}
The issue i'm running into is that if i try to do:
tempConn.on('ready', function() {
});
Then the ready function does not get triggered. I'm assuming, that is because the ready call back was already defined and it is not going to be re triggered. What i'm looking to do is bind a new queue by doing:
tempConn.queu('', {});
Any thoughts on how to get around this issue is much appreciated.
thanks.

Categories