2
votes

In a nodejs-express app, in the server.js file I set the socket-io connection. It works fine doing something like this

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

    // how can I save globally this 'socket'?

});

I would like to save this 'socket' globally in the server.js file and so, be able to use it everywhere in the project. Like this:

app.get('/on', function(req, res){
    socket.on('test', function (data) {
        console.log(data);
    });
});
app.get('/emit', function(req, res){
    socket.emit('test', "ciao");
});

I red there is a way to do it saving the 'socket' connection in session. My sessions settings:

app.configure(function(){
    // ....
    app.use(express.cookieParser(SITE_SECRET));
    app.use(express.session({
        secret : 
        ,store  : new MongoStore({
        mongoose_connection : mongoose.connection
        ,db: mongoose.connections[0].db
    })
    ,cookie: { maxAge: new Date(Date.now() + (1000*60*60*24*30*12)) }
    }));
     ...
});

What is a good way to do it?

And also, after have made this saving, how to use socket.on() and socket.emit() loading the first page if the connection is not opened yet?

1

1 Answers

3
votes

What you might have heard is not saving a socket into a session, but referencing sockets by their session cookie, which is passed to the server during the socket authorization process. During authorization, this is an example of the type of object that is passed to the server:

{
  headers: req.headers,       // <Object> the headers of the request
  time: (new Date) +'',       // <String> date time of the connection
  address: socket.address(),  // <Object> remoteAddress and remotePort object
  xdomain: !!headers.origin,  // <Boolean> was it a cross domain request?
  secure: socket.secure,      // <Boolean> https connection
  issued: +date,              // <Number> EPOCH of when the handshake was created
  url: request.url,           // <String> the entrance path of the request
  query: data.query           // <Object> the result of url.parse().query or a empty object
}

What we're interested in is the headers property, where we can find the session cookies of a connecting socket. We then parse the cookies during authorization:

// pass same objects from Express to Socket.IO so they match
var parseCookie = express.cookieParser(SITE_SECRET);
var store = new MongoStore({
  mongoose_connection: mongoose.connection,
  db: mongoose.connections[0].db
});

io.configure(function() {
  io.set('authorization', function(handshake, callback) {
    if (handshake.headers.cookie) {
      parseCookie(handshake, null, function(err) {
        // we used the signedCookies property since we have a secret
        // save the session ID to the socket object, we can access it later
        handshake.sessionID = handshake.signedCookies['connect.sid'];

        store.get(handshake.sessionID, function(err, session) {
          // we have the same Express session, reference it
          socket.session = session;
          callback(null, true);
        });
      });
    } else {
      // they client has no session yet, don't let them connect
      callback('No session.', false);
    }
  });
});

app.use(parseCookie);
app.use(express.session({
  secret: SITE_SECRET,
  store: store,
  cookie: {maxAge: new Date(Date.now() + (1000*60*60*24*30*12))}
}));

Then once we have saved the session ID, we can use the typical connection events:

var server = require('http').createServer(app)
var io = require('socket.io').listen(server);

var clients = {};
io.sockets.on('connection', function (socket) {
  // save to a global object
  var session = socket.handshake.sessionID;
  clients[session] = socket;

  socket.on('disconnect', function() {
    delete clients[session];
  });
});

Then we have a global reference by cookie signature. We can then access the socket like this:

app.get('/path', function(req, res) {
  var socket = clients[req.sessionID];
  socket.emit('Socket client accessed route.');
});

Keep in mind you might have to add some logic into your global logic for clients with multiple tabs, which would result in two sockets with the same authorization cookie.

As for your question about using socket.on() and socket.emit(), you can't use that before the connection has been established because the socket itself does not exist. If you want to send a message to all connected clients, then you should just use the global io.sockets object. It would then be more like io.sockets.emit().