I seem to lack understanding of how to structure node modules.
I have the following in app.js.
var io = require('socket.io')(http);
io.on('connection', function(socket){
socket.on('disconnect', function(){
console.log('user disconnected');
});
console.log("New user " + socket.id);
users.push(socket.id);
io.sockets.emit("user_count", users.length);
});
And this is fine. I can react to all kinds of messages from the client, but I also have several modules that need to react to different messages. For example my cardgame.js module should react to:
socket.on("joinTable"...
socket.on("playCard"
While my chessgame.js should react to
socket.on("MakeAMove"...
and my user.js file handles:
socket.on('register' ...
socket.on('login' ...
How would I link up/structure my files to handle these different messages, so that my file that reacts to socket requests does not become too huge.
Basically it would be great if I could pass the socket object to these modules. But the issue is that until a connection is established the socket will be undefined.
Also if I pass the whole io variable to my modules, then each of those modules would have the io.on('connection',..) call. Not sure if that is even possible or desired.
You don't need to pass the whole io object around (but you can, I do just in case I need it). Just pass the socket to the modules on connection, and then set your specific on callbacks for the module
main
io.on("connection",function(socket){
//...
require("../someModule")(socket);
require("../smoreModule")(socket);
});
socket
//Convenience methods to setup event callback(s) and
//prepend socket to the argument list of callback
function apply(fn,socket,context){
return function(){
Array.prototype.unshift.call(arguments,socket);
fn.apply(context,arguments);
};
}
//Pass context if you wish the callback to have the context
//of some object, i.e. use 'this' within the callback
module.exports.setEvents = function(socket,events,context){
for(var name in events) {
socket.on(name,apply(events[name],socket,context));
}
};
someModule
var events = {
someAction:function(socket,someData){
},
smoreAction:function(socket,smoreData){
}
}
module.exports = function(socket){
//other initialization code
//...
//setup the socket callbacks for the connected user
require("../socket").setEvents(socket,events);
};
Related
I'm using the Node.js net package to connect to and communicate with a Telnet server. I'm using net.createConnection() to create and initialize a socket; then doing something like the following to bind the initial data event listener:
client.on('data', (data) => { this.handleFoo(data) });
I thought it might be a good idea to encapsulate different functionality in different callbacks; one to handle negotiating the connection, logging in, etc. One for general purpose communication.
I tried simply calling .on() again to see if I could replace the event listener callback to something that didn't have all of the connection/logon code:
client.on('data', (data) => { this.handleBar(data) });
Unfortunately that had no effect, and the original callback was getting called again. Is this possible to do?
I couldn't find another socket method that would un-register that first listener callback.
Thanks.
You need a named function. Use client.removeListener() or client.off() available since Node.js v10.0.0. These are available to net.Socket because it extends EventEmitter.
const fooHandler = (data) => { this.handleFoo(data) };
client.on('data', fooHandler);
...
client.off('data', fooHandler);
From my understanding, a socket.io server manages N connections between itself and N clients via N separate sockets, each with its own ID.
It therefore makes sense that you get two different ID's when opening two tabs.
Consider, however, this minimal example:
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"></html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<title>Stuffy Stuff</title>
</head>
<body>
<button id="btnA">A</button>
<script src="/socket.io/socket.io.js"></script>
<script src="app.js"></script>
</body>
index.js
var express = require('express');
var path = require('path');
var app = express();
var servercode = require('./server');
app.use(express.static(path.join(__dirname,'public')));
var server = require('http').createServer(app).listen(process.env.PORT || 8080);
var io = require('socket.io').listen(server);
io.on('connection', function (socket)
{
console.log('client ' + socket.id + ' connected');
servercode.init(io, socket);
});
app.js
;
jQuery(function($) // client-side code
{
socket = io.connect();
$(document).on('click', '#btnA', stuff);
function stuff()
{
socket.emit('dostuff', socket.id);
socket.on('answerstuff', function()
{
console.log("Answered");
});
}
}($));
server.js
// Server-side
var io;
var socket;
exports.init = function(sio, sock)
{
io = sio;
socket = sock;
socket.on('dostuff', doStuff);
};
function doStuff(idd)
{
console.log('who is asking: ' + idd);
console.log('who am I answering to: ' + socket.id);
console.log(socket.id);
socket.emit('answerstuff');
}
Now say I fire up node index.js and then open two instances of localhost:8080; let's call them A and B. If I press the button on A's instance, the 'dostuff' message is emitted, it's caught by the server, but the socket it answers to is the one relative to B's instance, not A's. In general, it answers to the most recent instance. Why is it so?
EDIT: explanation
As I've finally grasped the nature of my mistake I thought I could help whoever runs into a similar issue.
Let's take our sample workflow: A connects, B connects, A presses button.
server.init(io, socket A) gets called
init(socket A) attaches the dostuff event, with callback function doStuff, to socket A
B connects... init attaches dostuff to socket B
Client A presses button, thus A emits dostuff passing socket A's id
doStuff(idd) is executed, but its definition contains a reference to a variable (socket) not defined within the function's scope, therefore it has to navigate its parent scopes
Such reference is found: it's socket = sock (if we omit var, the declaration 'bubbles up' in scope until it finds a variable with such a name. Had we written var socket = sock INSIDE init, the function couldn't have found any such variable)
But socket points to socket B thanks to B connecting and init being executed again. Therefore, the function only knows socket B.
In order to solve this issue we need to employ closures: functions that encapsulate the state of the world at the moment of their definition. In other words, a closure is an object containing the function definition AND the value of the variables not in the function scope but referenced by it.
As illustrated in the solution given by JagsSparrow, there are two ways of dealing with this issue and both involve creating a closure:
1. Use the function.bind(thisArg, args) function
The bind() function returns the function 'A' it's called upon where this is bound to thisArg (where it would normally point to the object calling A) and instantiates whatever arguments are specified in args.
In our case we don't need this as it's never mentioned in the doStuff() function, but we do need it to remember whose socket is the one mentioned in the arguments. We can do this by writing
doStuff.bind(null, socket)
This expression returns an object that is the doStuff() function where, in its context, this equals null (we could've written doStuff.bind(this, socket): in that case, this equals init) and its first argument, socket, is bound to socket. Therefore,
socket.on('dostuff', doStuff.bind(this,socket));
tells socket A to fire up doStuff (which contains a reference to socket A) when dostuff happens. Same with socket B and any other socket.
2. Use a nested function
We simply move the definition of function doStuff(idd) inside socket.on():
socket.on('dostuff', function doStuff(idd)
{
console.log('who is asking: ' + idd);
console.log('who am I answering to: ' + socket.id);
console.log(socket.id);
socket.emit('answerstuff');
});
This way, again, the socket is bound to a function that contains (by virtue of the closure) the definition of whichever socket is performing this operation.
In server.js you declared var socket; which is local of the server.js module (module scope),
So this variable gets assigned to different client each time its get connected
When client A get connected socket is of client A.
When client B get connected socket is of client B.
So, Don't make socket variable local to server.js module
Solution 1 :
server.js
var io;
//var socket;
exports.init = function(sio, sock)
{
io = sio;
socket = sock;
socket.on('dostuff', doStuff.bind(this,socket));
};
function doStuff(socket,idd)
{
console.log('who is asking: ' + idd);
console.log('who am I answering to: ' + socket.id);
console.log(socket.id);
socket.emit('answerstuff');
}
Solution 2 :
server.js
var io;
//var socket;
exports.init = function(sio, sock)
{
io = sio;
var socket = sock;
socket.on('dostuff', function doStuff(idd)
{
console.log('who is asking: ' + idd);
console.log('who am I answering to: ' + socket.id);
console.log(socket.id);
socket.emit('answerstuff');
});
};
I've got a code setup and I would like to know if it gets garbage-collected so my system won't start swapping and freeze/crash.
The setup should be able to run an undetermined amount of time assuming you stay within the normal parameters of your system (e.g. so don't connect 1.000.000 users to one single thread process all at the same time)
I've made a simplified version.
// connection.js
var io, Handler;
io = require('socket.io');
Handler = require('./handler');
io.on('connection', function (socket) {
new Handler({socket: socket})
});
// handler.js
var Handler,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
function Handler(arg) {
this.socket = arg.socket;
this.test = bind(this.test, this);
console.log('Handler', 'New connection', this.socket.id);
this.listeners();
}
Handler.prototype.listeners = function() {
return this.socket.on('test', this.test);
};
Handler.prototype.test = function() {
return console.log('SocketIO', 'test', this.socket.id);
};
module.exports = Handler;
So is 'new Handler' garbage-collected after a 'socket disconnect event' has occurred?
If not how should I change the code?
At first I thought this might solve the problem
var io, connections, Handler;
io = require('socket.io');
Handler = require('./handler');
connections = {}
io.on('connection', function (socket) {
connections[socket.id] = new Handler({socket: socket});
socket.on('disconnect', function (){
connections[socket.id] = null
})
});
But this only creates a reference to the 'new Handler' and removes that same reference on disconnect. That doesn't necessarily mean the 'new Handler' will be removed as well. It could still be listening for and/or doing stuff.
Your Handler object should get garbage collected as long as no other code of yours is keeping a reference to it.
When the actual socket itself is closed, the system knows that the event handlers are no longer live and thus they don't prevent garbage collection. The browser does the same sort of thing when you remove a DOM object that has event handlers on it so that these event handlers don't prevent garbage collection.
So, when the socket is closed and socket.io releases any references it has to the Javascript socket object, there should be no code that has any references to the Handler object any more and it will be eligible for garbage collection.
If you make your own reference to the Handler object such as:
var connections = {};
io.on('connection', function (socket) {
connections[socket.id] = new Handler({socket: socket});
});
Then, you will have to clear that reference upon disconnect as in the code below. Otherwise, this reference will stay alive and will keep the Handler object from being garbage collected.
var connections = {};
io.on('connection', function (socket) {
connections[socket.id] = new Handler({socket: socket});
socket.on('disconnect', function() {
delete connections[socket.id];
})
});
Note: I'd recommend you actually use delete to remove the property rather than just setting it to null. That way you won't get unused properties building up on the connections object.
I've just started learning node.js and express and there's something that confuses me a bit in the "hello world" example on the express.js website. In the example, they refer to the server variable inside the callback function.
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('App listening at http://%s:%s', host, port);
});
Does app.listen() return a value to server variable before it executes the callback function? How can it do that and how does it work? Is this the same for all callback functions in node (and javascript)?
I would just like a simple explanation of the execution process.
To be clear, I understand that the callback function has access to the server variable. But if the app.listen method executes the callback function before it returns a value to the server variable, wouldn't that mean that the server variable is still underfined when you try to access server.adress()? That is what I don't understand.
Does app.listen() return a value to server variable before it executes the callback function?
Yes, exactly. app.listen() resembles to the plain Node.js server.listen method.
The callback is an shortcut for assigning the server an listener to the listening event.
You could do the same with the following code:
var server = app.listen( 3000 );
server.on( "listening", function () {
console.log( "server is listening in port 3000" );
});
How can it do that and how does it work? Is this the same for all callback functions in node (and javascript)?
This happens because IO events in Node.js are all run asynchronously (with exceptions from the fs module) - this is, they will only take place when other synchronous code have finished to run.
This works the same in browser JS - if you run some JS process synchronously, any events triggered (like click, blur, etc) will only execute after that one finishes.
A function has access to all the variables that existed in the scope where it was created (unless it masks them).
var in_the_global_scope = 1;
function outer() {
function inner() {
alert(in_the_global_scope);
}
inner();
}
inner has access to any variable declared in inner, outer and the global scope.
A function being a callback isn't really relevant.
The listen method itself doesn't have access to server because listen was created in a different scope.
But if it returns a value, how can it then execute the callback function?
Because it doesn't just execute the callback. It waits for an event and the callback gets fired in reaction to that.
var timeOutId = setTimeout(function() {
alert(timeOutId);
}, 1000);
var server is being assigned to the function app.listen()
If you look at the express documentation it states that
The app.listen() method is a convenience method for the following (for HTTP only):
app.listen = function() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
By setting var server = app.listen() and the fact that app.listen() returns something, you're essentially setting var server to whatever app.listen() return.
You can thinking like that, the app.listen() return an object server which include information of how to run the server, example port, address, like a paper of instructions. Then it go to run server.
When server was run, the application also add some remark on that instructions example porcessid
then server is also call callback function app.listen(port[, callback]). Withing that function, we can access server information back from instruction and remarks.
I just wanted to confirm a suspicion of mine.
I stumbled across an article which recommended using Socket.io in the following fashion:
var app = require('express').createServer()
var io = require('socket.io').listen(app);
app.listen(8080);
// Some unrelated stuff
io.sockets.on('connection', function (socket) {
socket.on('action1', function (data) {
// logic for action1
});
socket.on('action2', function (data) {
// logic for action2
});
socket.on('disconnect', function(){
// logic for disconnect
});
});
I feel like the following would be a better use of resources:
var app = require('express').createServer()
var io = require('socket.io').listen(app);
app.listen(8080);
// Some unrelated stuff
io.sockets.on('connection', function (socket) {
socket.on('action1', action1);
socket.on('action2', action2);
socket.on('disconnect', disconnect);
});
function action1(data) {
// logic for action1
}
function action2(data) {
// logic for action2
}
function disconnect() {
// logic for disconnect
}
My feeling is that although the anonymous function that handles the connection event is only created in memory once, the anonymous functions that handle action1, action2, and disconnect are created in memory for every socket connection. The issue with the second approach is that socket is no longer in scope.
So firstly, is my suspicion about the creation of functions true? And secondly, if so is there a way to get socket in scope for the named functions?
Using a closure helps to keep the scope clean:
io.sockets.on('connection', function () {
function action1(data) {
// logic for action1
}
function action2(data) {
// logic for action2
}
function disconnect() {
// logic for disconnect
}
return function (socket) {
socket.on('action1', action1);
socket.on('action2', action2);
socket.on('disconnect', disconnect);
}
}()); // <- note the immediate function call
To your questions:
So firstly, is my suspicion about the creation of functions true?
Yes. The closure approach above prevents this, the callback functions are created only once. Plus: all see the correct parent scopes.
And secondly, if so is there a way to get socket in scope for the named functions?
The socket will be available as this in the callbacks.
You're correct, that the anonymous methods are created for each connection - and if you don't need scope, then the second method does avoid that. If you need the socket scope there's no real way to avoid it. If you want to keep the methods external (for some other reason) and still keep scope, you could always:
//...
socket.on('action1', function(){
action1.apply( socket, arguments );
} );
//... and so forth.
But that has you back to creating a method signature for each connection, so I'm not sure what you'd be gaining.