I use NodeJS with socket.io for my chat application. When client refreshes/closes the window or navigates to different URL I need client to emit "disconnect" event to NodeJS server. All works nice with excpetion of IE8/9. When refresh happens "disconnect" event is not send to server so server is not aware that this particular client is no longer connected.
I managed to use:
window.onbeforeunload = function(e) {
socket.disconnect();
};
and this takes care of clicking back/forward button and then the server is informed about client disconnecting. Refreshing the page or closing the tab however doesn't send disconnect to server. It seems that refreshing the page is too quick so the socket.disconnect() has no chance of executing. It seems so as if I do alert like below, client pauses for alert window and server receives disconnect message.
window.onbeforeunload = function(e) {
socket.disconnect();
alert("hey watchout!");
};
Now, is there any way to make IE8/9 send disconnect event to NodeJS when page is refreshed?
You can send information back and forth and find the time of the last reply, compare it to now and if the difference is greater than a number, disconnect the client, then reconnect when the client starts sending again.
You can use something like:
On client:
socket.on('ping',function(){socket.emit('pong',(new Date()).getTime());});
On server:
socketReferences = new Array();
io.on('connection',function(socket){
socketReferences.push(socket);
socket.on('pong',function(data){
if(new Date()).getTime()>5000){
// disconnect code here
}
});
});
setInterval(function(){
for(socket in socketReferences)
socketReferences[socket].emit('ping','');
},700);
Related
I made a nodeJS script to automate a few actions on a website - which is not mine!
To have a bit more control over what is going on, I would like to listen to the events on the website's socket.io stream.
Works in NODE so far:
Logging into the website and receiving their cookies as a string for further requests
Sending requests with the cookies from the login (do the actual actions)
Open a websocket connection and listen to the public (!) events
Doesn't work in NODE yet:
Read "private" events that are only being sent to a specific user (me)
I inspected a XHR request that is happening in chrome when clicking a specific button on this website. After this request has been sent, the websocket connection on chrome emits events about the status of my action. Of course, these events are only being sent to the user who performed this action.
Doing the exact same request in node (with the cookies from the website login) gives the right response (success), but the socket stream i opened before, only shows some public events - nothing about my actions.
As seen here, it logs in, displays the website's cookies, opens a socket stream. Then it sends a XHR POST request with the displayed cookies in the headers. The response says "success", but the socket.io events popping up once a second are only the public ones (userCount).
http://i.imgur.com/ZUrA2el.jpg
After sending the request, there should be events like "step_calc" popping up, displaying the status of my action.
My script
After receiving the website's login cookies as a string, I am running this:
const io = require('socket.io-client');
const request = require('request');
main()
function main() {
var socket = io(socketURL, {});
socket.on('connect', function () {
setTimeout(function(){
performAction(); // Send XHR to server
console.log(" > Sending XHR request...")
}, 1500)
});
socket.on('step_calc', function (data) { // Personal event about my action
console.log(" >>> Event = step_calc: " + data)
});
socket.on('login_time', function (data) { // Personal event being displayed every few seconds IF LOGGED IN (chrome) console.log(" >>> Event = step_calc: " + data)
});
socket.on('userCount', function (data) { // Public event
console.log(" >>> Event = userCount: " + data)
});
socket.on('disconnect', function () {
console.log(" > [Disconnected]");
});
}
1500ms after being connected to the socket, it would send the XHR request that should make the server emit information to the socket - performAction().
When I check the chrome console:
step_calc follows to a successfull XHR request (account specific)
login_time is being displayed every 2 seconds, but only if i am logged in (account specific)
userCount is being displayed all the time - to everybody
I checked the socket.io-client's API guide and found out about socketIDs. But it only says, how to get this id after connecting to the server...
https://github.com/socketio/socket.io-client/blob/master/docs/API.md#socket
... and yes ... when opening the website, the first thing chrome does, is send a GET request to the website, with data like this:
EIO=3&transport=polling&t=1493058868222-0
The response contains some kind of "sid".
{"sid":"g_mqoOS__________bHb","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":60000}
Well...
Now that I have gathered all of this information, how can I use it?
How can I make the socket connection be "connected" to the cookies that I got from the login (which I am using to send requests to the website)?
I really hope that my question is kind of understandable. Any help is appreciated, I have already put a lot of time into trying to make it work by myself.
Thanks a lot!
Edit:
I tried to add the same cookies from the handshake in chrome. One weird thing is, that the first XHR it does when i open the website (which seems to be the handshake), already contains a cookie named "io", which is then replaced by a new one. If I check the chrome console>application>cookies, I can't see this cookie at all. Where does it come from?
Left side: The request under the XHR tab on chrome
Right side: This is being displayed under the Websocket tab
http://i.imgur.com/VkRouQf.jpg
Are those two different requests or is it the same one in some way?
Does this information help somehow help to solve my problem?
From what I can see, you're not passing the session cookies to the socket.io-client constructor, which would probably mean that the socket connection isn't being authenticated.
Try this:
var socket = io(socketURL, {
extraHeaders: {
Cookie : '...'
}
});
Documented here.
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');
});
I have a socket.io connection using xhr as its only transport. When I load up the app in the browser (tested in chrome and ff), the socket connects and everything works well until I navigate away from the page. If I reload the browser, I can see the 'disconnect' event get sent out by the client, but the server disconnect event doesn't fire for a very long time (presumably when the client heartbeat times out). This is a problem because I do some cleanup work in the server when the client disconnects. If the client reloads, I get multiple connection events before disconnect is fired. I've tried manually emitting a disconnect message from the client in the window's 'beforeunload' event as well, but to no avail. Any ideas?
I debugged the socket.io server, and I can confirm that Manager.prototype.onClientDisconnect is only getting hit for "close timeout" reasons.
After some more debugging, I noticed the following configuration in the socket.io Manager object:
blacklist : ['disconnect']
That causes this branch from namespace.js to not process the event:
case 'event':
// check if the emitted event is not blacklisted
if (-~manager.get('blacklist').indexOf(packet.name)) {
this.log.debug('ignoring blacklisted event `' + packet.name + '`');
} else {
var params = [packet.name].concat(packet.args);
if (dataAck) {
params.push(ack);
}
socket.$emit.apply(socket, params);
}
The change is detailed in this pull request https://github.com/LearnBoost/socket.io/pull/569. I understand why this is in place for XHR, since anyone could send an HTTP request with random session IDs trying to disconnect other users from the server.
What I plan to do instead is to check each new connection for an existing session id in the server, and make sure to run my disconnect logic before continuing with the connection logic.
I need to send data to nodejs server via socket.io when the user closes the browser tab .
I tried doing :
var data={};
window.onbeforeunload = function() {
// i have a object to be sent
data.data1='abcd';
data.data2=1234;
socket.emit("senddata",data);
}
This code works when the user navigates around clicking links on the site but doesnot work when the user closes the browser tab
I also tried configuring the socket io on server side as below .. thinking the error may be due to socket connection being closed before emitting data:
var io = require('socket.io').listen(app.listen(port));
io.configure(function () {
io.set('close timeout',12000);
});
It also didnt work most of the time.
I also tried this on client side:
var socket = require('socket.io').listen(80, {
"sync disconnect on unload":false
});
It also did not work
I had tried receiving data like this
var io = require('socket.io').listen(app.listen(port));
io.sockets.on('connection', function (socket) {
socket.on('senddata', function (data) {
// data processing
});
});
please ..help me with this problem..thanks in advance..
When user connects - register on server side the time it happened.
Socket.IO has heart beating and pinging functionality. So just subscribe to disconnect event on server side and once it happens - means client disconnected - and you have on server time when client had connection. So that way you have timespan of client session.
Do not trust client to tell you any time or important data, as client can simply 'lie' - which leads to hacks/cheats/bugs on your server-side.
There is no reliable way to send data just before disconnect from client-side at all. There is nothing in Socket.IO for that, nor in just one of transports (WebSockets). As well as there is too many different scenarios of disconnection (power off, force close, ethernet cable out, wifi lose, etc).
I want to run some code when a a socket is closed but only if the user didn't reload the page...
I have something like
socket.on("close", function() {
//do something here
});
The problem is that this function runs during a reload event as well...Can I somehow pause it to run at a later time with the value at that later time. I tried just using a set timeout within the callback but couldn't access the socket object anymore from within the callback.
Whats the best way to prevent the function from running if a socket connection is regained shortly after?
the main concept of my thought, is that you can never know that a user is reloading or disconnecting and never come back for 1day or so , there are ways to detect that the browser is navigating away from the website, but cant know (in server side) that it will go to the same address, or different..
Instead if any clients disconnect, of course the disconnect event will be fired to socket.io server for that socket, so taking that in account you can set a Session Variable to false you can say that the player is "disconnected" so when the client of that session "reconnects" aka reloads , the socket.io will fire an "connection" event, BUT reading the session variable you can say that the client has previously disconnected and then connected again. Timestamps could apply to this so a reconnection after 15min would have to load some extra data etc..
So you can try this with sessions (assuming that you use express or something)
sockets.on("connection",function(socket){
if(session.isAReload){
// this is a reconnection
}
socket.set("isAReload",session.isAReload /*defaults to false*/);
});
sockets.on('close',function(){
socket.get('isAReload',function(err,isAReload){
if(isAReload){
// closed after reconnecting
}else{
/*
just closed connection, so next time it will be a "reload" or reconnection
a timer of lets say 30s could run to ensure that the client is reloading
*/
session.isAReload=true;
}
});
})