10

I can only seem emit messages to users when their socket id has been directly stored within the io.sockets.on('connect') function. I don't know why it doesn't work when trying to store their socket id after they have logged in.

Working:

  var clients = {};
  /** A new socket connection has been accepted */
  io.sockets.on('connection', function (socket)
  {
    //Used to access session id
    var hs = socket.handshake;            
        clients[socket.id] = socket; // add the client data to the hash   
        users['brownj2'] = socket.id; // connected user with its socket.id

    socket.on('user-login', function(user){
      if(!users[username]){
        //users[username] = socket.id; // connected user with its socket.id
      }
    })

    socket.on('page-load', function(pid)
    {
      if(users['brownj2'])
      {        
        clients[users['brownj2']].emit('hello');
      }
      else
      {
        console.log("NOT FOUND BROWNJ2");
      }
    }
  }

Not working:

  var clients = {};
  /** A new socket connection has been accepted */
  io.sockets.on('connection', function (socket)
  {
    //Used to access session id
    var hs = socket.handshake;            
        clients[socket.id] = socket; // add the client data to the hash            

    socket.on('user-login', function(user){          
      if(!users['brownj2']){
        users['brownj2'] = socket.id; // connected user with its socket.id
      }
    })

    socket.on('page-load', function(pid)
    {
      if(users['brownj2'])
      {        
        clients[users['brownj2']].emit('hello');
      }
      else
      {
        console.log("NOT FOUND BROWNJ2");
      }
    }
  }

JavaScript Client-side code snippet

var socket = io.connect('');
socket.on("hello", function(){
  alert("Hi Jack");
  console.log("WEBSOCKET RECIEVED");
})

Solution: Thanks @alessioalex I have had to remove the reference to socket.io from the login page, and add the following into io.sockets.on('connection')

var hs = socket.handshake;

sessionStore.get(hs.sessionID, function(err, session){
  console.log("SESSION: " + util.inspect(session));

  clients[socket.id] = socket; // add the client data to the hash
  validUsers[session.username] = socket.id; // connected user with its socket.id
})
Jack
  • 15,614
  • 19
  • 67
  • 92

2 Answers2

26

There are some problems with your code, the first being you shouldn't authenticate users through Socket.IO, you should make sure they can connect only after they authenticated. If you are using Express, then the following article can help you a lot: http://www.danielbaulig.de/socket-ioexpress/

Also you should be avoiding sending messages like so, since that is part of Socket.IO internally and may change:

io.sockets.socket(id).emit('hello');

Instead (if you want to send a message to a specific client) it's better to keep an object for example with the connected clients (and remove the client once disconnected):

// the clients hash stores the sockets
// the users hash stores the username of the connected user and its socket.id
io.sockets.on('connection', function (socket) {
  // get the handshake and the session object
  var hs = socket.handshake;
  users[hs.session.username] = socket.id; // connected user with its socket.id
  clients[socket.id] = socket; // add the client data to the hash
  ...
  socket.on('disconnect', function () {
    delete clients[socket.id]; // remove the client from the array
    delete users[hs.session.username]; // remove connected user & socket.id
  });
}

// we want at some point to send a message to user 'alex'
if (users['alex']) {
  // we get the socket.id for the user alex
  // and with that we can sent him a message using his socket (stored in clients)
  clients[users['alex']].emit("Hello Alex, how've you been");
}

Sure, io.sockets.socket(id) may work (didn't actually test), but also it can be always changed as it's part of the Socket.IO internals, so my solution above is more 'safe'.

Another thing you may want to change in your code on the client side is var socket = io.connect(''); with var socket = io.connect('http://localhost');, as we can see in the official example of Socket.IO here: http://socket.io/#how-to-use

alessioalex
  • 62,577
  • 16
  • 155
  • 122
  • Thanks, it works however when I try to use socket.on('login', .....){ users[u] = socket.id } it doesn't work. I can only seem to add users directly inside io.sockets.on('connection') – Jack Dec 12 '11 at 10:14
  • 1
    Are you sure? Since socket is a variable defined 'above' it should be visible in that function also, just like I show in my example with disconnect. – alessioalex Dec 12 '11 at 10:20
  • 1
    I've modded the question to show you a working and non-working example. I can't figure out why it won't allow me to send messages in the 'non-working' example above. – Jack Dec 12 '11 at 10:32
  • 1
    Can you console.log(socket.id) and console.log(socket) in there..? – alessioalex Dec 12 '11 at 10:38
  • http://pastebin.com/fAwgK6P5 socket.id, and socket are taken from within if(!users[username){...} and the clients[users['brownj2']] is taken from just before the message should be emitted. – Jack Dec 12 '11 at 10:47
  • (ignore the id differences) they were originally the same, but I re-ran the server to gather the 'clients[users['brownj2']] details. – Jack Dec 12 '11 at 10:49
  • 1
    So .. where's the problem? You said users[u] = socket.id doesn't work but if you console.log(socket.id) is shows you his id? Do you have another problem elsewhere.. ? – alessioalex Dec 12 '11 at 11:02
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/5770/discussion-between-jack-and-alessioalex) – Jack Dec 12 '11 at 11:10
  • this worked for me with node js 0.9.6 but it doesn't work after updating node to 1.0.6 and gives erros as : TypeError: Object # has no method 'socket' – Jigar Tank Jul 25 '14 at 13:20
2

I can see that this post is generating quite a lot of interest... so i'm going to post the code I used to implement this:

Notes

I have used Redis as the session storage. The session is initially setup during the login page via a HTTP request containing the users details, if these details are valid (Checked against a MongoDB document)... then the session is created.

Server Side Code Creation and Removal of Session / Socket

//Auth the user
io.set('authorization', function (data, accept) {
  // check if there's a cookie header
  if (data.headers.cookie) {
    data.cookie = parseCookie(data.headers.cookie);
    data.sessionID = data.cookie['express.sid'];

    //Save the session store to the data object
    data.sessionStore = sessionStore;

    sessionStore.get(data.sessionID, function(err, session){
      if(err) throw err;

      if(!session)
      {
        console.error("Error whilst authorizing websocket handshake");
        accept('Error', false);
      }
      else
      {
        console.log("AUTH USERNAME: " + session.username);
        if(session.username){
          data.session = new Session(data, session);
          accept(null, true);
        }else {
          accept('Invalid User', false);
        }
      }
    })
  } else {
    console.error("No cookie was found whilst authorizing websocket handshake");
    return accept('No cookie transmitted.', false);
  }
});

/** A new socket connection has been accepted */
io.sockets.on('connection', function (socket)
{    
  var hs = socket.handshake;
  if(hs.session)
  {
    if(hs.session.username)
    {
        clients[socket.id] = socket; // add the client data to the hash
        validUsers[hs.session.username] = socket.id; // connected user with its socket.id
    }
  }


  socket.on('disconnect', function()
  {
    delete browsing[hs.session.username]; //Remove the client from the browsing hash
    delete clients[socket.id];         // remove the client from the array
    delete validUsers[hs.session.username]; // remove connected user & socket.id
  })
....
}

Server Side Selective Message Sending

This code allows me to send a message to team members of a project. A team of users won't receive messages from a separate team of users..

for(var i = 0; i < project.team.length; i++)
{
  if(validUsers[project.team[i].username])
  {
    if(clients[validUsers[project.team[i].username]])
    {
      projects.update({"_id" : projectId},
        {"$pull" :
          {"stories" : {"_id" :  storyId}}
        }
      );                
      clients[validUsers[project.team[i].username]].emit('delete-story', storyId); 
    }
    else
    {
      console.error("Project team member not found");
    }
  }
}
Jack
  • 15,614
  • 19
  • 67
  • 92