Is there a way to detect when a client disconnects from a meteor server, either by refreshing or navigating away from the page, so that the server can attempt some cleanup?
5 Answers
One technique is to implement a "keepalive" method that each client regularly calls. This assumes you've got a user_id
held in each client's Session
.
// server code: heartbeat method
Meteor.methods({
keepalive: function (user_id) {
if (!Connections.findOne(user_id))
Connections.insert({user_id: user_id});
Connections.update(user_id, {$set: {last_seen: (new Date()).getTime()}});
}
});
// server code: clean up dead clients after 60 seconds
Meteor.setInterval(function () {
var now = (new Date()).getTime();
Connections.find({last_seen: {$lt: (now - 60 * 1000)}}).forEach(function (user) {
// do something here for each idle user
});
});
// client code: ping heartbeat every 5 seconds
Meteor.setInterval(function () {
Meteor.call('keepalive', Session.get('user_id'));
}, 5000);

- 10,924
- 9
- 52
- 71

- 11,870
- 2
- 49
- 43
-
After much searching i think this is the best solution for now. Thanks!! – greggreg Apr 24 '12 at 05:38
-
6This is almost pseudocode, as in it's not functional as is: the first setInterval has no interval specified. Also the Connections.update command doesn't specify to update {'user_id':user_id}. There may be other errors. It's a fine start though. – Luke Stanley Sep 08 '12 at 16:53
-
3@debergalis Is this still the recommended way to see if clients are dead? – user2602152 Jan 19 '14 at 12:46
-
2@debergalis I'd like to know if you'd still recommend this method – Ayrton Senna Jun 25 '15 at 00:43
I think better way is to catch socket close event in publish function.
Meteor.publish("your_collection", function() {
this.session.socket.on("close", function() { /*do your thing*/});
}
UPDATE:
Newer version of meteor uses _session like this:
this._session.socket.on("close", function() { /*do your thing*/});

- 7,202
- 13
- 46
- 74

- 2,272
- 1
- 23
- 36
-
That's great. But then I apparently run into this problem: http://stackoverflow.com/questions/10192938/meteor-code-must-always-run-within-a-fiber-when-calling-collection-insert-on-s – huyz Sep 28 '12 at 08:53
-
Thanks. This is the answer to my question: [Meteor observe running forever](http://stackoverflow.com/q/12902392/599991) – zVictor Oct 16 '12 at 17:29
-
3I use meteor 0.6.1 and for this line `this.session.socket.on("close", function() { /*do your thing*/});` my server returns _TypeError: Cannot read property 'socket' of undefined_ But when I correct it to `this._session.socket.on("close", function() { /*do your thing*/});` it works great, thanks – fantom Apr 13 '13 at 18:04
-
-
5How come the Meteor docs doesn't say anything about this or sockets? I couldn't find any mention of _session variable or sockets? – Nearpoint Oct 05 '13 at 22:20
I've implemented a Meteor smart package that tracks all connected sessions from different sessions and detects both session logout and disconnect events, without an expensive keepalive.
To detect disconnect/logout events, you can just do the following:
UserStatus.on "connectionLogout", (info) ->
console.log(info.userId + " with session " + info.connectionId + " logged out")
You can also use it reactively. Check it out!
EDIT: v0.3.0
of user-status now tracks users being idle as well!

- 35,740
- 23
- 143
- 224
-
Can this be done on a per page basis and not through the entire app? – Scalahansolo Jun 26 '14 at 15:56
-
if you're using Auth you have access to the user's ID in Method and Publish functions, you could implement your tracking there.. e.g. you could set a "last seen" when the user switches room:
Meteor.publish("messages", function(roomId) {
// assuming ActiveConnections is where you're tracking user connection activity
ActiveConnections.update({ userId: this.userId() }, {
$set:{ lastSeen: new Date().getTime() }
});
return Messages.find({ roomId: roomId});
});

- 8,204
- 2
- 38
- 53
I'm using Iron Router and call my cleanup code on the unload
event of my main controller. Sure this will not catch the event of a tab closing, but still feels good enough for many use cases
ApplicationController = RouteController.extend({
layoutTemplate: 'root',
data: {},
fastRender: true,
onBeforeAction: function () {
this.next();
},
unload: function () {
if(Meteor.userId())
Meteor.call('CleanUpTheUsersTrash');
},
action: function () {
console.log('this should be overridden by our actual controllers!');
}
});

- 4,728
- 6
- 41
- 68