Question

I'm afraid this is difficult to explain and to be honest, I can't really expect anyone to go through all of this but I don't know how else to explain it.

I seem to be having a problem linking usernames to sockets in a node.js socket.io service. I took a fairly standard chat sample I found and modified it for my needs and have

been trying to test its performance from a javascript client. The client app has a user interface to allow it to specify how many connections are to be made and then tap a

button to open all the connections. When the connect event fires in the client I then send off a username ("User"+connection number) and attempt to handle this at the server.

In the client, I am using the "force new connection" option when I connect so that a new connection is used rather than re-using previous connections. I do this to allow

performance to be tested.

The problem I have is that the events used at the server seem to have the wrong socket object (the last client to have connected). I can kind of see why this might happen for

async reasons but I'm at a loss to solve it. Here is my small server app:

var httpd = require('http').createServer();
var io = require('socket.io').listen(httpd);
io.sockets.on('connection', function (socket) {

    noOfConnections = noOfConnections + 1;
    console.log('SocketID ' + socket.id + ' connected. Connections = ' + noOfConnections);

    socket.on('login', function(UserName) {
        socket.set('UserName', UserName, function(err) {
            if (err) { throw err; }
            io.sockets.socket(socket.id).emit('serverMessage', 'SocketID (' + socket.id + ') Currently logged in as ' + UserName);
            console.log('SocketID (' + socket.id + ') ' + UserName + ' logged in. Connections = ' + noOfConnections);
        })
    });

});

httpd.listen(3000);
console.log("Server listening on port 3000");

var noOfConnections = 0;

The relevant part of the client app is as follows:

    for (var index = 0; index < noOfConnections; index++) {
        var socket = io.connect('http://192.168.0.12:3000', { 'force new connection': true });

        socket.on('connect', function () {
            userNo = userNo + 1;

            addMessage("Socket connected Server SocketId = " + socket.socket.sessionid);

            var username = userNamePrefix + userNo
            socket.emit('login', username);
        });

    }
},

I write lots of data to the server console and return some data to the client so it appears in the client browser. If the client makes 5 connections, I see this at the server

console:

Server listening on port 3000
SocketID i0M_578QQZcT0q9yraV_ connected. Connections = 1
SocketID 0c1l31sGy8sl1DkPraWA connected. Connections = 2
SocketID BEmhxokbfd95K-lGraWB connected. Connections = 3
SocketID i0UD5Prnkcu_o29araWC connected. Connections = 4
SocketID mbOSMts7JLCYlT6eraWD connected. Connections = 5
SocketID (mbOSMts7JLCYlT6eraWD) User1 logged in. Connections = 5
SocketID (mbOSMts7JLCYlT6eraWD) User2 logged in. Connections = 5
SocketID (mbOSMts7JLCYlT6eraWD) User3 logged in. Connections = 5
SocketID (mbOSMts7JLCYlT6eraWD) User4 logged in. Connections = 5
SocketID (mbOSMts7JLCYlT6eraWD) User5 logged in. Connections = 5

The socket.id shown in the first 5 connection messages, look great! They are all different as I'd expect.

The socketID for the next messages when the client logs in show the same (last connection) socketID for each user logging in. This means I can't set the username against the

socket be cause the user names are then all associated with the same socket.

The client browser screen is as follows:

Socket connected Server SocketId = mbOSMts7JLCYlT6eraWD
Socket connected Server SocketId = mbOSMts7JLCYlT6eraWD
Socket connected Server SocketId = mbOSMts7JLCYlT6eraWD
Socket connected Server SocketId = mbOSMts7JLCYlT6eraWD
Socket connected Server SocketId = mbOSMts7JLCYlT6eraWD
SocketID (mbOSMts7JLCYlT6eraWD) Currently logged in as User1
SocketID (mbOSMts7JLCYlT6eraWD) Currently logged in as User2
SocketID (mbOSMts7JLCYlT6eraWD) Currently logged in as User3
SocketID (mbOSMts7JLCYlT6eraWD) Currently logged in as User4
SocketID (mbOSMts7JLCYlT6eraWD) Currently logged in as User5

The first 5 messages are from the client socket connected event and show the server socketID to all be the same for the 5 connections. This implies the login socket is not at

fault but that the connection at the client fires 5 times for the last socket instead of once for each client socket. The login then takes place on that one socket at the

client and hence is wrong at the server when it fires there.

So is it possible the server code is not at fault and the problem is my for loop at the client? Is this type of for loop a no, no in async World?

Any advice or insights are very welcome because I've spent a long time on this now.

Thanks

Was it helpful?

Solution

This is an issue in your client. The two important points are that only functions create new scopes in javascript (specifically, for loops do not), and the way that closures in javascript work - they capture the variables, not the contents of the variables.

So, what's happening is that each iteration of your loop is using the same socket variable but setting it to a different socket, so when the loops end the one socket variable contains the last socket created. Then, once the sockets' connected events fire, they all emit on the same socket (the last one).

You'll want to create an IIFE to capture that socket variable separately for each run of the loop by creating new scopes:

for (var index = 0; index < noOfConnections; index++) {
    (function(socket) {
        socket.on('connect', function () {
            userNo = userNo + 1;

            if (showDataMessages) {
                addMessage("Socket connected Server SocketId = " + socket.socket.sessionid);
            }

            var username = userNamePrefix + userNo
            socket.emit('login', username);
        });
    })(io.connect('http://192.168.0.12:3000', { 'force new connection': true }));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top