Node.js - Socket.io - Express 3 - node.js

I'm a noob to Node.js and Express :( I'm having an issue with accessing the socket.io object in other modules. I have created a global variable to hold a socket object however as soon as that user closes the connection the connection isn't available anymore. Technically I guess the connection still lives on but I delete it for resource reasons.
Notice below everytime a connection is made we assign that socket to the global variable which is accessible to all other modules.
// App.js
var io = require('socket.io').listen(server);
var sessionsConnections = {};
io.sockets.on('connection', function (socket)
{
global.socket = socket;
sessionsConnections[socket.id] = socket;
.....
}
socket.on("disconnect", function()
{
delete sessionsConnections[socket.id];
});
// Match.js
global.socket.emit('lobby:createMatch', data);
If the connection last assigned to the global closes Match.js will be screwed. At this point Match.js is the only module who will ever need that socket.io reference. Match.js has a bunch of exports for handling events, emitting the changes and rendering the view.
Are there any suggestions to how this is handled? Is it possible to instantiate an initial socket connection to live in App.js for the purpose of being a global reference?

The socket variable in io.sockets.on('connection', function(socket) {...}) is different for each connection.
Since in your code global.socket is always a reference to the socket relative to the last connected client, it is normal that if this client disconnects, this socket will die.
In any case, I don't see any reason to use a global variable. If you need to send a message to a specific client, you can use the socket variable inside the connection callback:
io.sockets.on('connection', function (socket)
{
socket.emit('foo', 'bar');
}
If you need to access the sockets in another module, you can export the sessionsConnections object, and access the socket you need by its id:
//In app.js
exports.connections = sessionsConnections;
//In match.js
var app = require('./app.js');
app.connections[clientId].emit('foo', 'bar');
Of course, you need to keep track of the id's somewhere.

You might try express.io,
http://express-io.org/
npm install express.io
It works the same as express, except that it has socket.io integration built-in. It also has basic routing, so that it is a little easier to deal with your issue.
Check out this simple example:
app = require('express.io')()
app.http().io()
app.io.route('some-event', function(req) {
req.io.broadcast('announce', 'someone triggered some-event')
})
app.listen(7076)
You can also do routing with objects, which makes it more like a traditional controller:
app = require('express.io')()
app.http().io()
app.io.route('posts', {
create: function(req) {
// create a post
},
remove: function(req) {
// remove a post
}
})
app.listen(7076)
Hope that helps!

Related

Websockets & NodeJS - Changing Browser Tabs & Sessions

I've started writing a node.js websocket solution using socket.io.
The browsers connects to the node server successfully and I get see the socket.id and all config associated with console.log(socket). I also pass a userid back with the initial connection and can see this on the server side to.
Question: I'm not sure the best way to associate a user with a connection. I can see the socket.id changes every page change and when a tab is opened up. How can I track a user and send 'a message' to all required sockets. (Could be one page or could be 3 tabs etc).
I tried to have a look at 'express-socket.io-session' but I'm unsure how to code for it and this situation.
Question: I have 'io' and 'app' variables below. Is it possible to use the 2 together? app.use(io);
Essentially I want to be able to track users (I guess by session - but unsure of how to handle different socket id's for tabs etc) and know how to reply to user or one or more sockets.
thankyou
The best way to handle the situation is rely on SocketIO's rooms. Name the room after the user's unique ID. This will support multiple connections out of the box. Then, whenever you need to communicate with a particular user, simply call the message function and pass in their id, the event, and any relevant data. You don't need to worry about explicitly leaving a room, SocketIO does that for you whenever their session times out or they close their browser tab. (We do explicitly leave a room whenever they log out though obviously)
On the server:
var express = require('express');
var socketio = require('socket.io');
var app = express();
var server = http.createServer(app);
var io = socketio(server);
io.on('connect', function (socket) {
socket.on('userConnected', socket.join); // Client sends userId
socket.on('userDisconnected', socket.leave); // Cliend sends userId
});
// Export this function to be used throughout the server
function message (userId, event, data) {
io.sockets.to(userId).emit(event, data);
}
On the client:
var socket = io('http://localhost:9000'); // Server endpoint
socket.on('connect', connectUser);
socket.on('message', function (data) {
console.log(data);
});
// Call whenever a user logs in or is already authenticated
function connectUser () {
var userId = ... // Retrieve userId somehow
if (!userId) return;
socket.emit('userConnected', userId);
}
// Call whenever a user disconnects
function disconnectUser () {
var userId = ... // Retrieve userId somehow
if (!userId) return;
socket.emit('userDisconnected', userId);
}

Reconnect socket in disconnect event

I am trying to reconnecct the socket after the disconnect event is fired with same socket.id here is my socket config
var http = require('http').Server(app);
var io = require('socket.io')(http);
var connect_clients = [] //here would be the list of socket.id of connected users
http.listen(3000, function () {
console.log('listening on *:3000');
});
So on disconnect event i want to reconnect the disconnected user with same socket.id if possible
socket.on('disconnect',function(){
var disconnect_id = socket.id; //i want reconnect the users here
});
By default, Socket.IO does not have a server-side logic for reconnecting. Which means each time a client wants to connect, a new socket object is created, thus it has a new id. It's up to you to implement reconnection.
In order to do so, you will need a way to store something for this user. If you have any kind of authentication (passport for example) - using socket.request you will have the initial HTTP request fired before the upgrade happened. So from there, you can have all kind of cookies and data already stored.
If you don't want to store anything in cookies, the easiest thing to do is send back to client specific information about himself, on connect. Then, when user tries to reconnect, send this information again. Something like:
var client2socket = {};
io.on('connect', function(socket) {
var uid = Math.random(); // some really unique id :)
client2socket[uid] = socket;
socket.on('authenticate', function(userID) {
delete client2socket[uid]; // remove the "new" socket
client2socket[userID] = socket; // replace "old" socket
});
});
Keep in mind this is just a sample and you need to implement something a little bit better :) Maybe send the information as a request param, or store it another way - whatever works for you.

Socket.IO & private messages

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:
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.
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.
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.
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()

Get SESSIONID in nodeJS

Now, after some hours of playing around with nodejs and socket.io, I'm getting a couple more problems - one being, that I need to get the sessionID inside node.js, whitout using app.get('/' ... - since that event doesnt seem to fire when socket.io connects, it only fires .on('connection', function( ...
var express = require('express')()
express.set('port', process.env.PORT || 8080)
var server = require('http').createServer(express)
var socket = require('socket.io').listen(server)
server.listen(express.get('port'))
// this event is fired, get('/', ... isnt!
server.on('connection', function(stream) {
// ??
} )
The Session is initially created by the PHP application, as the user logs in. Session data is stored in the database, and the key I need to access that data is the SESSION ID. What's the easiest way to get to it? Like mentioned, I found a couple examples that used app.get('/' ... but I couldnt get that event to fire at all.
Thanks.
If the session data is being stored as a cookie (most likely), then you should be able to re-parse that data during the socket handshake. I posted code for that on this answer, but copied the code here as well:
io.configure(function () {
io.set('authorization', function (handshakeData, callback) {
var cookie = handshakeData.headers.cookie;
// parse the cookie to get user data...
// second argument to the callback decides whether to authorize the client
callback(null, true);
});
});
If the session data is being propagated in the URL, then you may be able to gather this information from handshakeData.url or handshakeData.query. You'll probably have to do your own experimentation.

Sending message to specific client with socket.io and empty message queue

I´m going crazy with socket.io!
Documentation is so bad it's simply not true.
I want to send a feedback to specific client over socket.io
My server side looks like this:
app.get('/upload', requiresLogin, function(request, response) {
response.render('upload/index.jade');
io.sockets.on('connection', function (socket) {
console.log('SOCKET ID ' + socket.id);
io.sockets.socket(socket.id).emit('new', 'hello');
});
});
and the client side looks like this:
$(document).ready(function() {
var socket = io.connect('http://localhost:80/socket.io/socket.io.js');
socket.on('new', function (data) {
console.log(socket.id);
console.log(data);
//$('#state').html(data.status);
});
});
but the client does simply nothing. I have tried nearly everything. Can someone tell me what I am doing wrong, please! :(
to send a message to a specific client save every one that connects to the server in an Object.
var socketio = require('socket.io');
var clients = {};
var io = socketio.listen(app);
io.sockets.on('connection', function (socket) {
clients[socket.id] = socket;
});
then you can later do something like this:
var socket = clients[sId];
socket.emit('show', {});
A couple of ways to send feedback to a specific client over socket.io include:
As stated by pkyeck, save all clients to an Object, so you can send to these specific clients later in your route handlers, e.g.:
var sessionsConnections = {};
sio.on('connection', function (socket) {
// do all the session stuff
sessionsConnections[socket.handshake.sessionID] = socket;
});
or, use socket.io's built in support for rooms - subscribe each client to a room on connection and send via this room within route handlers, e.g.:
sio.on('connection', function (socket) {
// do all the session stuff
socket.join(socket.handshake.sessionID);
// socket.io will leave the room upon disconnect
});
app.get('/', function (req, res) {
sio.sockets.in(req.sessionID).send('Man, good to see you back!');
});
Acknowledgement:
http://www.danielbaulig.de/socket-ioexpress/#comment-1158
Note that both these example solutions assume that socket.io and express have been configured to use the same session store and hence refer to the same session objects. See the links above and below for more details on achieving this:
https://github.com/LearnBoost/socket.io/wiki/Authorizing
2 things
1) You should really place your io.sockets.on(..) outside your app/update route to prevent adding multiple listeners for clients.
2) io.sockets.socket(id); should not be used, it should have been socket.emit('new', 'hello')
In socket.io 1.0, this is how it would work. It may work for lower versions, but I cannot guarantee it.
socket.to(socket_id_here).emit('new', 'hello');
This works because socket.io automatically adds a socket to a room with the socket's id on connection.
Also, if you plan to upgrade to version 1.0, there are a lot of changes to the api, so you'll sometimes have to change some code to make it work in 1.0.
The correct way to do this in Socket.io 1.0+ is:
io.to(users_socket_id).emit('new', 'hello');
You can also substitute a room name for 'users_socket_id' to emit to a specific socket.io room.
First of all, you cannot use socket.id on client side.
And then change the line
var socket = io.connect('http://localhost:80/socket.io/socket.io.js');
to
var socket = io.connect('http://localhost:80/');
I believe io.sockets.socket has been removed and has been a problem in Socket.IO (https://github.com/socketio/socket.io/issues/1618).
You can use io.sockets.connected[socket.id] and store the socket.id to reference with the user via username:
var usernames = {};
usernames[username] = socket.id;
// Sending the message
io.sockets.connected[usernames[username]].emit(...);
I don't see it anywhere on the documentation so I can see how this hasn't been answered. Also, if you don't have any reference via usernames, you can instead try:
users[socket.id] = socket.id;
to duplicate the key and value for easy access.
There is also possibly another way by using io.clients as described below, but I haven't used that method.
OTHER SOLUTION: Send message to specific client with socket.io and node.js
Have you tried using?
var socket = io.connect('http://localhost:80/');
i tired with the latest version of node and socket.io
below i am going to post complete code
<ul id="messages"></ul>
<form action="">
<input id="id1" /><button>Send</button>
</form>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
var io = io.connect();
$('form').submit(function(){
io.emit('drumevent', $('#id1').val());
$('#id1').val('');
return false;
});
io.on('drumevent', function(msg){
console.log(msg);
$('#messages').append($('<li></li>').text(msg.message+' quantity = '+msg.quantity));
});
</script>
server side code
var usernames = {};io.on('connection', function(socket){usernames["username"] = socket.id;
socket.on('drumevent', function(msg){
var socketId = socket.id;io.to(socketId).emit('drumevent', data);

Resources