Node.js - sharing sockets among modules with require - node.js

I'm using socket.io. In app.js I set up it and whenever a connection is established, I add the new socket to sockets array. I want to share sockets among modules such as routes by require(). However, app.js also requires the routes, so it forms a require loop. Here's the code.
// app.js
var route = require('routes/route')
, sockets = [];
exports.sockets = sockets;
// route.js
var sockets = require('../app').sockets; // undefined
How can I resolve the loop? Or are there other approaches?

You could do all your socket.IO work inside the route file
var route = require('routes/route').init(io)
with in routes.js
var io;
exports.init = function(io) {
io = io
}

Related

I'm trying to access socket.io in an express-generator app, but getting a null object

I've built a web app with express generator. I'd like to be able to send updates to the browser from a number of different modules.
However, I'm not quite sure why the solution I've put together, below, doesn't work.
I know that the exports.initialize() function is being called, through logging to the console, but the error I'm getting, cannot read property 'emit' of null suggests that socket.io is not being initialized, because it's returning a null object.
I've created a module containing the below wrapper:
const sio = require('socket.io')
let io = null
exports.io = function () {
return io
}
exports.initialize = function(server) {
console.log("init")
return io = sio(server)
}
I've initialised the module in my app.js file, like so:
// Create the http server
const server = require('http').createServer(app);
// Create the Socket IO server on
// the top of http server
const io = require('./app_modules/sockets').initialize(server)
and then, in modules where I need to send information to the browser, I'm calling the sockets module like this:
const io = require('./sockets').io()
Can anyone help me understand why this isn't working, and what I can do to fix it?
In order to start a Socket.IO server, you need to invoke the Server constructor. The io constructor is only for the client side.
Your sockets module should look like this:
const sio = require('socket.io')
let io = null
exports.io = io
exports.initialize = function(server) {
io = new sio.Server(server);
return io
}
And in modules where you need to send information to the clients, you should use something like this:
const io = require('./sockets').io

Node.js get same instance of module accross different classes

I've a main class (app.js) where I declared an instance of socket-io and I would like to know how is possible to get same instance of socket-io inside another classes.
app.js
var io = require('socket.io')();
//initialize socket and more related do web socket
routes.js
var io = ?? //How get same var io from app.js?
On Node.js, is possible to do that? I need to manipulate some informations from socket-io but I would like to avoid write a single file with all logic inside.
Let's say you have a file routes.js and this is a file inside which you want to access the instance of socket-io which you have created on app.js. Then you can pass this var io inside the routes.js file that will pass the same instance of socket-io like this,
Inside app.js
var io = require('socket.io')();
//pass the `io` inside the route file
require('routes')(io);
Inside routes.js
module.exports = function(io) {
//your route codes here that can access `io`
};

How to connect multiple sockets to sails in test

I have a messaging controller setup in sails.js and want to test it with multiple clients to see if the pub/sub code works. I set up a test file
var socketIOClient = require('socket.io-client');
var sailsIOClient = require('sails.io.js');
var socket1 = socketIOClient;
var client1 = sailsIOClient(socket1);
var socket2 = socketIOClient;
var client2 = sailsIOClient(socket2);
var socket3 = socketIOClient('http://localhost:1337', {'force new connection': true});
var client3 = sailsIOClient(socket2);
...
client1.socket.get... works and says it is subscribed.
client1.socket.post... works and posts a message to the DB.
So I want to test that a client can receive the notification when a new message is posted. However, when I post from either client1 or client2, it posts from both. Essentially, they are linked to the same socket object or something like that, but I don't know where. So I want to connect multiple sockets, and I've tried variations like socket3 and client3, but get the following problem:
client3.socket.get... and client3.socket.post... and other variations (forceNew, multiplexing, etc.) each hang up and don't resolve.
Example of hang up:
sails.log('posting...');
client3.socket.post('/v1.0/messaging', data, function(body, JWR){
sails.log('posted');
done();
});
Only posting... is logged in this way, but posted is logged if using client1 or client2.
My question:
How can I connect multiple clients to my sails api to test if my pub/sub controller works?
I can't test it right now, but you could try this
var socketIOClient = require('socket.io-client');
var sailsIOClient = require('sails.io.js');
// Instantiate the socket client (`io`)
var io = sailsIOClient(socketIOClient);
// prevents socket to connect with it's default origin
io.sails.autoConnect = false
// Ask the client to create two socket connections
var socket1 = io.sails.connect('http://localhost:1337');
var socket2 = io.sails.connect('http://localhost:1337');
// Test it
socket1.get(url, data, cb)
socket1.post(url, data, cb)
socket2.get(url, data, cb)
socket2.post(url, data, cb)
// If you want to configure and use the "eager" instance
io.sails.url = 'http://localhost:1337';
io.socket.get(url, data, cb)
This way, you would create several SailsSocket instance instead of using the "eager" instance.
When you use sails.io.js in a browser, io.socket contains the socket instance (called "eager instance" in the comments) which will automatically try to connect using the host that the js file was served from. io.sails.connect() allows you to create other instances.
The correct syntax for actual version of socket.io should be
//first socket
var socket1 = socketIOClient('http://localhost:1337', {'forceNew: true});
//second socket
var socket2 = socketIOClient('http://localhost:1337', {'forceNew: true});
See socket.io docs http://socket.io/blog/socket-io-1-2-0/#

Two way communication between routers within express app

I have an express app that has a router for different sections of my application, each contained within individual files. At the end of each file I export the router object like so.
var express = require("express");
var router = express.Router();
//routing handlers
module.exports = router;
However my problem is that I am trying to implement a feature were a user is allowed to edit a post that could be displayed on the front page, therefore in order to have the most current version of the user's post I need to be able to know when the user edits the post to make the necessary changes.
I have two modules one that handles dispatching the user's posts call this module B and another that handles editing call this module A. I need to be able to have module A include handler function and an array from module B, but I also need module B to be able to be notified when to make changes to the its array that module A requires.
I have tried
module A
var express = require('express');
var EventEmitter = require('events').EventEmitter;
var evt = new EventEmitter();
var router = express.Router();
var modB = require('moduleB');
router.evt = evt;
module.exports = router;
Module B
var express = require('express');
var router = express.Router();
var modA = require('moduleA').evt;
modA.on('myEvent',handler);
var myArray = [....];
router.myArray = myArray;
module.exports = router;
This gives me an undefined for modA and throws an error. I suspect it might be the order the modules are included but anyhow I would like to obtain some feedback since I sense that this might not even be good practice.
I think you are running into a common scenario for someone just starting out with express. A lot of people stick everything into routes/controllers when really the route should be very simple and just extract the data needed to figure out what the request is doing and then pass it to a service for most of the processing.
The solution is to create a Service and put the bulk of your logic and common code there, then you can wire up ModA and ModB to use the Service as needed.
EDIT with an example(not working but should give you a good starting point):
Shared Service
var EventEmitter = require('events').EventEmitter;
var evt = new EventEmitter();
module.exports = {
saveData: function(data) {
// do some saving stuff then trigger the event
evt.emit('myEvent', data);
},
onDataChange: function(handler) {
evt.on('myEvent', handler);
}
};
Module A
var service = require('SharedService.js');
// listen for events
service.onDataChange(function(e, data) {
// do something with the data
});
Module B
var service = require('SharedService.js');
// save some data which will cause Module A's listener to fire
service.saveData(data);
This example above hides the implementation of EventEmitter which may or may not be desirable. Another way you could do it would be to have SharedService extend EventEmitter, then your Modules could listen/emit directly on the service.

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

Resources