I am switching from ajax polling to socket.io live data pushing but have a question about best practice of managing events.
Let's say I have a basic server like this:
var Clients = [];
// Event fired every time a new client connects:
io.on('connection', function(socket) {
Clients.push(socket);
// What else should go in here?
// Individual functions for each event?
socket.on('chat message', function(msg){
console.log(':> '+msg);
io.emit('chat message', msg);
});
// When socket disconnects, remove it from the list:
socket.on('disconnect', function() {
var index = Clients.indexOf(socket);
if (index != -1) { Clients.splice(index, 1); }
});
});
In the example there is a chat message event, but ideally there are many other events I'd like to push to clients in real-time, such as:
User sends message, user starts/stops typing, user likes a post, user invites you to become friends/group member, and so on.
There are many events that I'd like to capture in real-time but it seems like there has to be a better way than cramming the io.on() statement full of them all.
I've looked into Node OOP but not sure if it would necessarily help in this application.
So where we have the socket.on() statements, what would the best practice be for including event catching like I described?
There are many events that I'd like to capture in real-time but it
seems like there has to be a better way than cramming the io.on()
statement full of them all.
Nope. Inside the io.on('connection', ...) is where you have a closure with the socket variable that pertains to a new connection. So, any event handler you want to respond to from that socket goes in there or in some function you call from there and pass the socket to. That's just how it works.
FYI, in the interest of modularity, you don't have to put all the event handlers inside of one io.on('connection', ...). If you export the io object to other modules or pass it to them in a module constructor, you can have other modules create their own io.on('connection', ...) listeners and install their own event handlers inside their own io.on('connection', ...). io is an EventEmitter so it can support as many listeners for the connection event as you want and all will be notified.
Related
I am new to socket.io and have realized that every server-side eventlistenrs are wrapped in a io.on connection block:
io.on('connection', socket => {
socket.on('event name', callback)
})
I am a little confused about two things:
do I always have to wrap my socket.on in this kind of blocks? Is it because the socket represents the individual client that raised the connection event?
Why should I use socket.on inside the block instead of io.on since they seem to do the same thing(like io.on('connection', () => io.on('event name', callback)))?
io.on() means adding an event listener to the server
io.on('connection', socket => {
socket.on('event name', callback)
})
socket.on() means adding an event listener to the client as he connects
You cannot add an event listener to the client if he isn't connected, yet.
So what we do, is we add an event listener to the server, that triggers, when a client connects. Once the client is connected, we add event listeners to him, that trigger, when he is sending data (for example)
I am new to javascript world. Recently I was working on a chat application in nodejs. So I have a method called gracefulshutdown as follows.
var gracefulShutdown = function() {
logger.info("Received kill signal, shutting down gracefully.");
server.close();
logger.info('Disconnecting all the socket.io clients');
if (Object.keys(io.sockets.sockets).length == 0) process.exit();
var _map = io.sockets.sockets,
_socket;
for (var _k in _map) {
if (_map.hasOwnProperty(_k)) {
_socket = _map[_k];
_socket.disconnect(true);
}
}
...code here...
setTimeout(function() {
logger.error("Could not close connections in time, shutting down");
process.exit();
}, 10 * 1000);
}
Here is what is happening in the disconnect listener.The removeDisconnectedClient method simply updates an entry in the db to indicate the removed client.
socket.on('disconnect', function() {
removeDisconnectedClient(socket);
});
So in this case the disconnect event wasn't fired for all sockets. It was fired for only a few sockets randomly from the array. Although I was able to fix it using setTimeout(fn, 0) with the help of a teammate.
I read about it online and understood only this much that setTimeout defers the execution of of code by adding it to end of event queue. I read about javascript context, call stack, event loop. But I couldn't put together all of it in this context. I really don't understand why and how this issue occurred. Could someone explain it in detail. And what is the best way to solve or avoid them.
It is hard to say for sure without a little more context about the rest of the code in gracefulShutdown but I'm surprised it is disconnecting any of the sockets at all:
_socket = _map[ _k ];
socket.disconnect(true);
It appears that you are assigning an item from _map to the variable _socket but then calling disconnect on socket, which is a different variable. I'm guessing it is a typo and you meant to call disconnect on _socket?
Some of the sockets might be disconnecting for other reasons and the appearance that your loop is disconnecting some but not all the sockets is probably just coincidence.
As far as I can tell from the code you posted, socket should be undefined and you should be getting errors about trying to call the disconnect method on undefined.
From the method name where you use it I can suppose that application exits after attempts to disconnect all sockets. The nature of socket communication is asynchronous, so given you have a decent amount of items in _map it can occur that not all messages with disconnect will be sent before the process exits.
You can increase chances by calling exit after some timeout after disconnecting all sockets. However, why would you manually disconnect? On connection interruption remote sockets will automatically get disconnected...
UPDATE
Socket.io for Node.js doesn't have a callback to know for sure that packet with disconnect command was sent. At least in v0.9. I've debugged that and came to conclusion that without modification of sources it is not possible to catch that moment.
In file "socket.io\lib\transports\websocket\hybi-16.js" a method write is called to send the disconnect packet
WebSocket.prototype.write = function (data) {
...
this.socket.write(buf, 'binary');
...
}
Whereas socket.write is defined in Node.js core transport "nodejs-{your-node-version}-src\core-modules-sources\lib\net.js" as
Socket.prototype.write = function(chunk, encoding, cb)
//cb is a callback to be called on writeRequest complete
However as you see this callback is not provided, so socket.io will not know about the packet having been sent.
At the same time when disconnect() is called for websocket, member disconnected is set to true, and "disconnect" event is broadcasted, indeed. But synchronously. So .on('disconnect' handler on server socket doesn't give and valuable information about whether the packet was sent or not.
Solution
I can make a general conclusion from this. If it is so critical to make sure that all clients are immediately informed (and not wait for a heartbeat timeout or if heartbeat is disabled) then this logic should be implemented manually.
You can send an ordinary message which will mean for the client that server is shutting down and call socket disconnect as soon as the message is received. At the same time server will be able to accept all acknowledgements
Server-side:
var sockets = [];
for (var _k in _map) {
if (_map.hasOwnProperty(_k)) {
sockets.push(_map[_k]);
}
}
sockets.map(function (socket) {
socket.emit('shutdown', function () {
socket.isShutdown = true;
var all = sockets.every(function (skt) {
return skt.isShutdown;
});
if (all) {
//wrap in timeout to let current tick finish before quitting
setTimeout(function () {
process.exit();
});
}
})
})
Clients should behave simply
socket.on('shutdown', function () {
socket.disconnect();
});
Thus we make sure each client has explicitly disconnected. We don't care about server. It will be shutdown shortly.
In the example code it looks like io.sockets.sockets is an Object, however, at least in the library version I am using, it is a mutable array which the socket.io library is free to modify each time you are removing a socket with disconnect(true).
Thus, when you call disconnect(true); if the currently iterated item from index i is removed, this effect like this happens:
var a = [1,2,3,4];
for( var i in a) {
a.splice(i,1); // remove item from array
alert(i);
}
// alerts 0,1
Thus, the disconnect(true) call will ask the socket.io to remove the item from the array - and because you are both holding reference to the same array, the contents of the array are modified during the loop.
The solution is to create a copy of the _map with slice() before the loop:
var _map = io.sockets.sockets.slice(); // copy of the original
It would create a copy of the original array and thus should go through all the items in the array.
The reason why calling setTimeout() would also work is that it would defer the removal of the items from the array, allowing the whole loop iterate without modifying the sockets -Array.
The problem here is that sockjs and socket.io use asynchronous "disconnect" methods. IE. When you call disconnect, it is not immediately terminated. It is just a promise that it WILL be terminated. This has the following effect (assuming 3 sockets)
Your for loop grabs the first socket
The disconnect method is called on the first socket
Your for loop grabs the second socket
The disconnect method is called on the second socket
The disconnect method on the first socket finishes
Your for loop grabs the third socket
The disconnect method is called on the third socket
Program kills itself
Notice, that sockets 2 and 3 haven't necessarily finished yet. This could be for a number of reasons.
Finally, setTimeout(fn, 0) is, as you said, blocking the final call, but it may not be consistent (I haven't dug into this too much). By that I mean, you've set the final termination to be AFTER all your sockets have disconnected. The setTimeout and setInterval methods essentially act more like a queue. Your position in the queue is dictated by the timer you set. Two intervals set for 10s each, where they both run synchronously will cause one to run AFTER the other.
After Socket.io 1.0, the library does not expose you an array of the connected sockets. You can check that io.socket.sockets.length, is not equal to the open socket objects. Your best bet is that you broadcast a 'disconnect' message to all the clients that you want to off, and on.'disconnect' on the client side close the actual WebSocket.
I'm using Socket.IO like in this sample:
io.sockets.on("connection", function (socket) {
myService.on("myevent", function() {
socket.emit("myevent", { /* ... */ });
// some stuff happens here of course
});
});
myService is a singleton and a subclass of EventEmitter which triggers the myevent over the time. Anything works fine, however I guess that I create some kind of leak in this case. How does my service know that it doesn't need to call the handler once the connection is destroyed? Is there some kind of destroy event I can catch and then remove the handler from myService?
Listen to the socket disconnect event and when you get a disconnect event, remove the relevant event handler from the myService object.
You should be able to do that like this:
io.sockets.on("connection", function (socket) {
function handler() {
socket.emit("myevent", { /* ... */ });
// some stuff happens here of course
}
myService.on("myevent", handler);
socket.on("disconnect", function() {
myService.removeListener("myevent", handler);
});
});
If what you're trying to do is to broadcast to all connected sockets, you could just install one "myevent" listener (not one per connection) and use io.emit() to broadcast to all sockets too and not have to handle the connect or disconnect events for this purpose.
If you are planning to send data to all sockets when some other event fires, you don't need to add/remove another listeners every time a client connects/disconnects.
It is a lot more efficient and easier to simply fire the socket.io event to all sockets that are connected right now using io.sockets (which is a reference to the default namespace with all clients on it by default) and io.sockets.emit:
myService.on('myevent', () => {
io.sockets.emit('myevent', {/*...*/});
});
If you only need to fire this event to some subset of your users, try using specific namespaces or rooms:
myService.on('myevent', () => {
//with namespaces
io.of('namespace').emit('myevent', {/*...*/});
//with rooms
io.to('room').emit('myevent', {/*...*/});
});
I am trying to factor out the anonymous callback functions from my socket.on() events for two reasons:
It makes the code slightly easier to read and document.
I believe it can be more memory efficient.
Since the first reason is more of a personal preference, I will not address it--although I am definitely interested in seeing style guides and/or general recommendations.
The second reason is because the only way I have seen socket.io used is like this:
var io = require('socketio');
io.on('connection', function(socket) {
socket.on('event', function(data) {
// logic goes here
socket.emit('different event', differentData);
});
});
This can work quite well, but I believe the anonymous functions are instantiated for each and every incoming connection.
I would factor this out through the following:
io.on('connection', function(socket) {
socket.on('event', eventHandler);
function eventHandler(data) {
// logic goes here
socket.emit('different event', differentData);
});
});
Still, that appears to still create an instance of 'eventHandler' for every new connection.
My current attempt at factoring this out looks like this:
var sockets = {}
io.on('connection', function(socket) {
socket.on('login', login.bind(socket));
socket.on('get information', getInformation);
});
function login(data) {
// logic goes here
// Save the socket to a persistent object
sockets[data.userId] = this;
this.emit('logged in', loginData);
});
function getInformation(data) {
// authentication, db access logic goes here
// Avoid binding by using previously stored socket
sockets[data.userId].emit('got information', infoData);
});
This works, but I am believe that bind() just creates another function, anyway, subverting any real benefit of not including the login callback in the connection callback.
As for the other handlers, it seems that a persistent object is necessary, as evidenced by EtherPad's method (SocketIORouter.js:62). Since I plan to be listening to a sizable number of events, it seems like pulling the callbacks out of the connection callback is the best way to go about it. Maybe bind will create a new function each time it is called, but in this case, it will only be called twice per connection rather than 10-20 times. I imagine that for thousands of clients, that would make a difference, but I am not sure.
So, to sum up, is this a proper approach, or is there perhaps a better one that I should be using? Or, is this just another case of premature optimization?
When using the socket.io library, I am a little confused about how to place the different methods.
In a very simple chat application I have server.js:
io.sockets.on('connection', function(socket) {
//some methods to handle when clients join.
socket.on('text', function(msg) {
socket.broadcast.emit('text', msg);
});
});
and client.js:
var socket = io.connect();
socket.on('connect', function() {
//some methods to fire when client joins.
socket.on('text', function(msg) {
console.log(msg)
});
});
Right now, the methods that handle when a client joins AND the methods that handle the sending and receiving of messages afterwards, are placed within the connect / connection event methods, both on the server and the client side, but this structure seems to work as well on the client side:
var socket = io.connect();
socket.on('connect', function() {
//some methods to fire when client joins.
});
socket.on('text', function(msg) {
console.log(msg)
});
+potentially many more methods...
My question is, what is the fundamental difference between placing a method inside the connect method and outside, and what is considered the best option?
When you call this,
socket.on('xyz', function listener() {});
you listen for event xyz and add function listener as event handler. It is executed whenever xyz occurs. So when you do :
socket.on('connect', function() {
socket.on('text', function(msg) {
console.log(msg)
});
});
Event handler/listener for text is added only when connect event happens (connect event handler is executed). There is only one listener before connect happens and two (one more) when connect happens. But when you do :
socket.on('connect', function() {
//some methods to fire when client joins.
});
socket.on('text', function(msg) {
console.log(msg)
});
There are two listeners at any time, before/after connect happens.
The previous method is more efficient and logical. Logical because text cannot happen before connect happens, so why listen to it. Efficient as in the event-loop does not have unnecessary events to look for. Adding too many events may not hurt very much, but for performance-critical applications it can be a drag. The latter one just looks good, all event handlers placed one by one.