0

This must have been asked already a thousand times, but I do not find any of the answers satisfying, so I'll try having another go, being as clear as possible.

I am starting out with a clean Express; the one that is usually done via the following terminal commands:

user$ express
user$ npm install

then I proceed installing socket.io, this way:

user$ npm install socket.io --save

on my main.js file I then have the following:

//app.js
var express  = require('express'),
  http     = require('http'),
  path     = require('path'),
  io       = require('socket.io'),
  routes   = require('./routes');

var app = express();

I start my socket.io server by attaching it to my express one:

//app.js
var server = http.createServer(app).listen(app.get('port'), function(){
  console.log('express server started!');
});

var sIo = io.listen(server);

What I do now is to set the usual routes for Express to work with:

//app.js
app.get('/', routes.index);
app.get('/send/:recipient/:text', routes.sendMessage);

Now, Since I like to keep things organized, I want to put my socket.io code in another file, so instead of using the usual code:

//app.js
sIo.sockets.on('connection', function(socket){
  console.log('got a connection');
});

I use the following to be able to access both the socket and the sIo object (as that object contains all the connections infos (important)):

//app.js
sIo.sockets.on('connection', function(socket){
  routes.connection(sIo, socket);
});

// index.js (./routes)
exports.connection = function(sIo, socket){
  console.log('got a connection.');
};

This way I can do all my socket.io jobs in here. I know that I can access all my clients information now from the sIo object, but of course, they do not contain any information about their session data.

My questions now are the following:

  1. Suppose a user makes an HTTP request to send a message and the handler in my routes is like this:

    exports.sendMessage = function(req, res){ //do stuff here };

How can I get this to "fire" something in my socket.io to send a message? I do not want to know all the underlying work that needs to be done, like keeping track of messages, users, etc. I only want to understand how to "fire" socket.io to do something.

  1. How can I make sure that socket.io sends the message only to a person in particular and be 100% sure that nobody else gets it? From what I can see, there is no way to get the session infos from the sIo object.

Thanks in advance.

john smith
  • 541
  • 6
  • 24

2 Answers2

3

question one: The cleanest way to separate the two would probably be to use an EventEmitter. You create an EventEmitter that emits when an http message comes in. You can pass session information along with the event to tie it back to the user who sent the message if necessary.

// index.js (./routes)
var EventEmitter = require('events').EventEmitter;

module.exports.messageEmitter = messageEmitter = new EventEmitter();

module.exports.sendMessage = function(req, res) {
  messageEmitter.emit('private_message', req.params.recipient, req.params.text);
};

question 2: You can access the socket when the initial connection is made. An example mostly borrowed from this answer:

var connect = require('connect'),
    userMap = {};

routes.messageEmitter.on('private_message', function(recipient, text) {
    userMap[recipient].emit('private_message', text);  
});

io.on('connection', function(socket_client) {
  var cookie_string = socket_client.request.headers.cookie;
  var parsed_cookies = connect.utils.parseCookie(cookie_string);
  var connect_sid = parsed_cookies['connect.sid'];
  if (connect_sid) {
    session_store.get(connect_sid, function (error, session) {
      userMap[session.username] = socket_client;
    });
  }

  socket_client.on('private_message', function(username, message) {
      userMap[username].emit(private_message, message)
  });
});

So we're just creating a map between a session's username and a socket connection. Now whenever you need to send a message you can easily lookup what socket is associated with that user and send a message to them using their socket. Just make sure to handle disconnects, and reconnects and connecting in multiple tabs, etc.

Community
  • 1
  • 1
Timothy Strimple
  • 22,920
  • 6
  • 69
  • 76
  • also, I guess with your point number 2, I'd need to have a handler for disconnections as well, to avoid my userMap object getting super-sized, right? – john smith Aug 14 '13 at 19:32
  • Yep. Called that out in the last sentence. Adding example now. – Timothy Strimple Aug 14 '13 at 19:40
  • I cannot understand how eventEmitter and messageEmitter relate with socket.io – john smith Aug 14 '13 at 19:50
  • They don't relate. It's just an easy way to separate where the http request comes in and socket.io firing off the message. The http handler emits an event via messageEmitter and the socket code can subscribe to that event and then emit it's own event off to the connected user. – Timothy Strimple Aug 14 '13 at 19:55
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/35471/discussion-between-timothy-strimple-and-john-smith) – Timothy Strimple Aug 14 '13 at 19:56
0

I have built something like what you are saying. If a user can make a socket request, it pushes the message via the socket, and then the server does a broadcast or emit of it. But, if a user can't connect to the socket, it then does the http post, like what you are saying by calling the sendMessage. What I have done, rather than having sendMessage shoot off a socket is that I also have my clients doing an ajax request every 5 seconds or so. That will bring back new messages, and if any of the messages were not received via socket.io, I then add them to my clientside array. This acts as sort of a safety net, so I don't have to always fully trust socket.io.

see below in pseudo code

client

if canSendSocketMessage()
  sendSocketMessage(message)
else
  sendAjaxMessage(message)

setInterval( ->
  // ajax call
  getNewMessages()
), 5000

server

socket stuff

socket.on 'message' ->
   saveMessage()
   socket.emit(message)

ajax endpoints

 app.post 'sendMessage'
    saveMessage()

 app.get 'getNewMessages'
    res.send getNewMessages()
WallMobile
  • 1,939
  • 1
  • 20
  • 35