Is it possible to open two sockets on server side - node.js

I am building an app using nodeJS along with socket io .
I have a server.js file in which i did the following :
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.on('connection', function(socket){
console.log("a user is connected");
});
Also i have another file "api.js" in which i have the following function :
filter.watch(function(error, result){
if (!error){
var newblock = new Block(web3.eth.getBlock(result));
newblock.save(function (err) {
if (err) return handleError(err);
// saved!
})
}
});
Im trying to send the object "newblock" (which is changing constantly) from the file api.js to server.js with socket io . I tried doing " socket.emit('fromApi_toserver',newblock)" in api.js but it's not working... Does anyone have any ideas ?

There appear to be several issues here.
First, if you want to send to all connected clients, then you would use io.emit(), not socket.emit().
Second, since io is defined in your app.js file, if you want access to it in your api.js file, then you need to specifically make it available to that file. There are a number of ways to do that, but the most common is to pass it to that file with a module constructor when the api.js module is loaded. Here's a way to do that:
app.js
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.on('connection', function(socket){
console.log("a user is connected");
});
// pass io variable to app.js when loaded
require('api.js')(io);
api.js
// define module constructor
let io;
modules.exports = function(socketio) {
// save io instance for later use
io = socketio;
}
filter.watch(function(error, result){
if (!error){
var newblock = new Block(web3.eth.getBlock(result));
newblock.save(function (err) {
if (err) return handleError(err);
// send JSON stringified version of newblock to all connected clients
io.emit(newblock);
});
}
});
Third, you can't send live objects to clients. Data for socket.io is serialized into JSON strings before sending to clients and the client-side socket.io will then deserialize the JSON back into an object. But, it will just be a plain object on the client end, not any specific object type. If you need the data to be a specific object type (such as a client-side Block object), then you must take the data you receive and use it to create your own new client-side Block object.
Fourth, your questions asked about a second socket, but you don't need another socket. You can use the io instance to send to all currently connected clients with io.emit().

Related

socket.io only working in main server file, not in route files

Version 2 of post
Okay, first the file structure:
app
|___app.js
|___models/
|_user.js
|___routes/
|___admin.js
|___public/
|___js/
|___script.js
app.js:
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
http.listen(3000, function (err){
if (err) throw err;
console.log("Server is running");
});
script.js:
var socket = io();
//Next, this tells the browser that is has connected to the socket.io server
socket.on('connect', function() {
console.log('Connected to socket.io server!');
});
socket.on('message', function(message){
console.log('New message: ');
console.log(message.text);
});
var $newUsername = $('#Username');
$newUsername.on('blur', function(event){
socket.emit('message', {
text: $newUsername.val()
});
});
So on a registration page, if a user enters a 'username' already in the database, it will console.log 'This user already exists'. At least, that's the idea.
There is still sockets.io code I need to add on the server side. I am trying to put it here:
admin.js:
var router = require('express').Router();
var User = require('../models/user');
io.on('connection', function(socket) {
console.log('User connected via socket.io!');
socket.on('message', function(message) {
Username.findOne({username: message.text}, function (err, existingUsername) {
if (existingUsername) {
console.log('This user already exists: ' + message.text);
}
});
});
});
So, as this stands, the sockets.io code in admin.js won't work because it can't access the io function. I would like to know how I can fix this.
To add to this: The sockets.io code I have in the admin.js file will work fine if I placed it in my app.js file.
The key here is to pass the io instance to any module that needs it when that module is first loaded. This is called the "push" method of sharing as you share by pushing data from one module to another by passing it in the constructor function of the other module.
There is also a "pull" module where one module asks some other module for some shared data by calling a method in that module.
Here's how you could implement the "push" model:
In your admin.js file, you define a constructor function that you call and pass the io instance to when you load it:
var router = require('express').Router();
var User = require('../models/user');
var io;
// define constructor function that receives the io instance so the rest
// of the module can use it
module.exports = function(ioInstance) {
io = ioInstance;
io.on('connection', function(socket) {
console.log('User connected via socket.io!');
socket.on('message', function(message) {
Username.findOne({
username: message.text
}, function(err, existingUsername) {
if (existingUsername) {
console.log('This user already exists: ' + message.text);
}
});
});
});
}
Then, in your app.js file:
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
http.listen(3000, function (err){
if (err) throw err;
console.log("Server is running");
});
// when you load the admin.js file, you pass it the io instance
require('./routes/admin.js')(io);
Below are 2 answers. While both answers will technically work, it is advised that you do not use either of them. Use the answer provided above.
Answer 2
So my route files all have this:
var router = require('express').Router();
Now, in any of the route files where I need sockets.io I did this:
var http = require('http').Server(router);
var io = require('socket.io')(http);
And this got my sockets.io code working.
Answer 1
So to get sockets.io code working in my route files, I simply removed 'var' in front of my http and io variables.
I changed:
//Socket io config
var http = require('http').Server(app);
var io = require('socket.io')(http);
to this:
//Socket io config
http = require('http').Server(app);
io = require('socket.io')(http);
Now my sockets.io code in my route files work fine.

NodeJS, Express, and eventful Socket.io

I want to build a NodeJS API so that when I hit an endpoint, the app will trigger an event that will cause its unique socket connection to emit a message to its listeners. I have built a solution before using Python/Django, Redis, and NodeJS/Socket.io with Django as the API and Redis as the 'event trigger', but I would like to consolidate the different technologies into NodeJS and Socket.io.
I tried moving the socket.emit() code into different modules and then app.use()'d those modules, but the code broke because it didn't have an instance of the socket.
I also know that you can broadcast to all socket connections inside on an endpoint, for example:
app.use('socket.io/help', function(req, res) {
console.log(req.query);
io.sockets.emit('help_message', 'You should help me.');
res.send('help msg sent');
});
But I am looking for a way that allows a client (that doesn't have a socket connection) to hit an endpoint and pass a query param that tells NodeJS which of its connected sockets to send a message to.
Is this possible? Or am I trying to fight the framework? e.g., is there a different way of doing this with different JS WebSocket frameworks/technologies?
I have been stuck on same situation but resolved easily
you have created socket on app.js
server = require('http').createServer(app),
io = require('socket.io').listen(server);
server.listen(port);
global.socketIO = io;
Now you can call this io instance to your any controller like
app.use('socket.io/help', function(req, res) {
console.log(req.query);
var io = global.socketIO;
// UID IS THE VARIABLE
io.sockets.emit('help_message'+UID, 'You should help me.');
res.send('help msg sent');
});
CLIENT SIDE
<script src="/socket.io/socket.io.js"></script>
<script>
window.socket.on("help_message_<%-UID%>", function(msg){
//// ACTION
});
You can join a room with the specific sockets you want to recieve the messages on.
see Rooms & Namespaces in the socket.io documentation
join a chan on your helpdesk conenctions:
io.on('connection', function(socket){
socket.join('helpdesk');
});
and broadcast to them:
app.use('socket.io/help', function(req, res) {
console.log(req.query);
var io = global.socketIO;
io.sockets.emit('adduser', req.body.uid);
io.to('helpdesk').emit('some event'):
res.send('help msg sent');
});

where to add socket.io in my node.js app

So i have this basic node.js server running, now i want to add another layer to it with socket io. I can use socket io, the example here is pretty simple and works great. But my app is much bigger so I do this thing where i load a bunch of resources and then load this server module by calling start_app.
I know i cant put io.listen(app) on line 3 because my server hasn't started yet.
I need that functional dependency, so how do i add add socket.io to the mix?
var app= require('http') // all http requests go to onRequest
, url= require('url') // path info stuff
, io = require('socket.io') // socket io
function start_app(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname.replace("/","")
route(handle, pathname, request, response)
}
app.createServer(onRequest).listen(process.env.PORT || 8888)
io.listen(app)
console.log(". http://localhost:8888 .")
}
exports.start_app = start_app;
my error:
...socket.io/lib/manager.js:104
server.on('error', function(err) {
^
TypeError: Object #<Object> has no method 'on'...
Instead of your code:
app.createServer(onRequest).listen(process.env.PORT || 8888)
io.listen(app)
Could you do it like below?
var createdServer = app.createServer(onRequest).listen(process.env.PORT || 8888);
io.listen(server).on('connection', function (socket) {
socket.on('message', function (msg) {
console.log('Message Received: ', msg);
socket.broadcast.emit('message', msg);
});
});
We need the actual created server so socket.io can listen to that. Then handle the 'connection' event.
changeing the first few lines of the server fixed it.
I'm not 100% sure why this works but, I guess it makes sense that the server shouldn't start until this function is called, since its the entry into the main event loop of the app.
var app // all http requests go to onRequest
, url = require('url') // path info stuff
, sio = require('socket.io') // socket io
function start_app(route, handle) {
app = require('http').createServer(onRequest)
sio = require('socket.io').listen(app)
app.listen(process.env.PORT || 8888)

Scoping of socket.io server side and client side objects in express routes

In my app.js I have
var app = express();
var serv = http.createServer(app);
var io = require('socket.io').listen(serv);
io.sockets.on('connection', function(socket) {
//some code here
}
var SessionSockets = require('session.socket.io'),
sessionSockets = new SessionSockets(io, express_store, cookieParser);
sessionSockets.on('connection', function (err, socket, session) {
//set up some socket handlers here for the specific client that are
//only called when a client does a socket.emit.
//These handlers have access to io, sessionSockets, socket, session objects.
}
How can the express routes access a particular client's socket reference after processing a post/get which is not triggered by a client socket.emit but triggered by a client post/get. What is the best way to scope the socket.io server(io/sessionSockets)/client(socket) objects in routes so that I can get this client's socket reference easily?
These three steps helped me to the solve the problem. This identifies tabs also uniquely as that was one of my requirements.
On connection, join using socket.id and then send the socket.id back to the client using
io.sockets.on('connection', function(socket) {
socket.join(socket.id);
socket.emit('server_socket_id', {socket_id : socket.id});
}
Client receives the emit event using
socket.on('server_socket_id', function(data){
//assign some global here which can be sent back to the server whenever required.
server_socket_id = data.socket_id;
});
In app.js I fetch the corresponding socket like this and pass it on to the routes.
app.post('/update', function(req, res){
var socket_id = req.body.socket_id;
route.update(req, res, io.sockets.in(socket_id).sockets[socket_id]);
});
The best way to do this is to use the socket.io authorization setting, although the module session.socket.io was created specifically for that purpose. Each time a socket establishes a connection, there is handshake data that is stored (although I've heard that flashsockets won't pass the browser cookies). This is what it looks like (and is similarly written in the module you're using):
io.configure(function () {
io.set('authorization', function (handshakeData, callback) {
//error object, then boolean that allows/denies the auth
callback(null, true);
});
});
What you could do from here is parse the cookie, then store a reference to that socket by the cookie name. So you would add this to the authorization setting:
var data = handshakeData;
if (data.headers.cookie) {
//note that this is done differently if using signed cookies
data.cookie = parseCookie(data.headers.cookie);
data.sessionID = data.cookie['express.sid'];
}
Then, when you listen on connections, store the client by session identifier:
var clients = {};
io.sockets.on('connection', function(socket) {
//store the reference based on session ID
clients[socket.handshake.sessionID] = socket;
});
And when you receive an HTTP request in Express, you can fetch it like this:
app.get('/', function(req, res) {
//I've currently forgotten how to get session ID from request,
//will go find after returning from school
var socket = clients[sessionID];
});

encapsulate a socket.io instance in nodejs

I am trying encapsule a socket.io instance in a module. I did this but it looks a bit messy because i have to inject some dependencies for authentication of the socket transport from express / passport.
My problem is i want to access the socket instance outside of the module like socket.on("newDevice", function (data) {});
The socket instance which i get by the connection event is inside the function and it even may not exist on creation because no connection is established. This simply looks kind of wrong to me. I dont want to inject more and more depencies just because i need them inside of the function scope.
I thought about doing the sio.on('connection', function(socket) {}); outside of the module. And maybe i could do it twice, first inside of the module and then outside but i would create two listeners i guess.
Is there any good practice or pattern how to do this properly?
var io = require('socket.io');
var socket = function (server, sessionStore, cookieParser, authentication) {
var sio = io.listen(server);
// Configure socket.io
sio.configure(function () {
// Authorize the socket.io request
sio.set('authorization', function (data, accept) {
// Authorization is done here
});
});
sio.on('connection', function(socket) {
var lastActionTime = new Date();
// Get the userId from the session
var session = socket.handshake.session;
var userId = session.passport.user;
var sessionID = socket.handshake.sessionID;
var userdata = null;
// Deserialize user by the userId
authentication.deserializeUser(userId, function(err, user) {
// get the userdata
});
socket.on('disconnect', function() {
});
socket.on('brightnessChange', function(data) {
// TODO Do something here device control
// Broadcast to other devices
this.broadcast.emit('brightnessChange', data);
});
});
return sio;
};
module.exports = socket;
I would suggest, the below, flexibility and scales well. I've tried both ways and resulted in using multiple connection events.
I thought about doing the sio.on('connection', function(socket) {}); outside of the module. And maybe i could do it twice, first inside of the module and then outside but i would create
two listeners i guess.

Resources