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');
});
};
Related
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.
io.on('connection', function(socket) {
socket.join('myRoom');
if(condition){
bindFunctions();
}
});
Let's say condition is true when the 4th socket connects. The code below successfully binds a .on handler to all the sockets in myRoom.
bindFunctions(){
for(var socketId in io.sockets.adapter.rooms["myRoom"]){
var socket = io.sockets.connected[socketId];
socket.on('my event',function(submit){
console.log(socket.myVariable);
//This is always the same property, no matter who calls this function.
});
}
}
However, when 'my event' fires, regardless of which client triggered it, the socket object is always the 4th socket that connected. It is always the socket that called bindFunctions. I would have thought that the handler socket should be the same as the socket inside the function.
So I have two questions:
Why is the socket inside the 'my event' function the socket that called bindFunctions instead of the socket that fired the event?
What can I do instead to be able to access properties of the socket that called 'my event' whilst inside the function?
That happens because although you're doing var socket = io.sockets.connected[socketId]; inside the loop, in Javascript, the var keyword is function scoped, and not block scoped.
Under the guts, what really happens is:
bindFunctions(){
var socket;
for(var socketId in io.sockets.adapter.rooms["myRoom"]){
socket = io.sockets.connected[socketId];
socket.on('my event',function(submit){
console.log(socket.myVariable);
//This is always the same property, no matter who calls this function.
});
}
}
So, since your event will happen later, your socket variable is always the last one set.
To prevent this behavior, if using Node.js version > 4.0.0, you can use const instead of var, because it's block scoped. But in Node.js, to be able to use const and let keywords block-scoped, you must put 'use strict'; in the begining of your function, e.g:
bindFunctions(){
'use strict';
for(var socketId in io.sockets.adapter.rooms["myRoom"]){
const socket = io.sockets.connected[socketId];
socket.on('my event',function(submit){
console.log(socket.myVariable);
//This is always the same property, no matter who calls this function.
});
}
}
Another way of solving your problem is by creating an IIFE, so you always pass the current variable to the event, e.g:
bindFunctions(){
for(var socketId in io.sockets.adapter.rooms["myRoom"]){
var socket = io.sockets.connected[socketId];
(function(socket) {
socket.on('my event',function(submit){
console.log(socket.myVariable);
});
})(socket);
}
}
Doing that, the socket variable inside the IIFE will always refer to the object passed as parameter, and you'll get your desired behavior.
You can also use Object.keys combined with Array.prototype.forEach instead of for...in loop:
bindFunctions(){
var room = io.sockets.adapter.rooms["myRoom"];
Object.keys(room).forEach(function(key) {
var socketId = room[key];
var socket = io.sockets.connected[socketId];
socket.on('my event',function(submit){
console.log(socket.myVariable);
});
});
}
Doing that, you're creating a new context each iteration, since it's a callback function being called, and you won't have problems with function scoping.
If I create a server using this code:
var net = require('net');
server = net.createServer(function(sock){
/** some code here **/
});
server.listen(9000);
How do I access the socket outside of the callback function? This would help me to be able to create an array/list of sockets that I can store and access whenever I need to send data to a specific client.
I did have a weird workaround that I am currently using but I don't think this is the correct way. Basically I added socket to the server object by saying:
this.socket = sock
inside the callback function of createServer.
Then I can access it saying server.socket but this is only really useful if there is only one client.
To help better understand what I am trying to accomplish I have created a class that has a constructor function which holds this code:
this.server = net.createServer(this.handleConnection)
This class also has the method handleConnection to use as the callback. I also have a method called start which can be called with a port as a parameter to initiate server.listen(port).
What I want to accomplish is a method like below:
sendCommand(message, socket){
socket.write(message);
}
This way the server can send a message to the client from a function that is not within the callback function of createServer. Keep in mind that I am doing this from within a class so using the this keyword within the callback function accesses the net server object and not the class I created.
Every example I see is an extremely simple tcp server that only has one message sent from the server within the callback function. If anyone could shed some light on my problem that would be great thanks!
I'm not sure if this is what you're after, but I would do something like this.
var net = require('net');
var conns = [];
var server = net.createServer(function(sock){
conns.push(sock);
sock.on('close', function (){
// TODO: Remove from conns
});
});
server.listen(9000);
var net = require('net');
var listeners = [];
server = net.createServer(function(sock){
/** some code here **/
listeners.push(sock);
sock.on('close', function (){
var index = listeners.indexOf(sock);
if (index > -1) {
listeners.splice(index, 1);
}
});
});
server.listen(9000);
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);
};
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.