NodeJS, websocket, and module.export - javascript

I have a server.js file.
Inside, i defined my routes.
// routes
var mainRoutes = require('./routes/main.js')(app, express);
var apiRoutes = require('./routes/api.js')(app, express);
var socketRoutes = require('./routes/socket.js');
app.use('/', mainRoutes);
app.use('/api', apiRoutes);
// socket.io communication
io.sockets.on('connection', socketRoutes);
My socket.js file looks like that :
module.exports = function (socket) {
console.log('user connected');
socket.on('myEvent', function(data) {
// whatever ...
});
}
Inside this function, i can catch events and send some.
BUT i need to send a message to everyone at some point. Let say when i receive an 'myEvent' event.
So basically, i would like to do :
io.sockets.emit('messageForEveryone', "This is a test");
But here, i can only work on the 'socket' argument, which is for 1 person only i guess.
I would like to pass io from server.js, to socket.js.
I tried that (in server.js) :
var socketRoutes = require('./routes/socket.js', io);
And that (in socket.js) :
module.exports = function (io, socket)
Obviously, it's not working. I don't even understand where the socket argument is coming from.
Question : How can i work on io object, when i'm inside the module.export of the sockets.js file ?
I would really appreciate any help, i'm new to all of this.
Thanks !

Since you just want to emit to all clients, instead of passing io to socketRoutes, you can simply do this.
module.exports = function (socket) {
var sockets = this;
console.log('user connected');
socket.on('myEvent', function(data) {
sockets.emit('hello_all_clients',data);
});
}

You could return a function from exports as such.
module.exports = function (io) { // pass io to initialize
return function (socket) {
// io and socket are both scoped
}
}
then in server.js
// socket.io communication
io.sockets.on('connection', socketRoutes(io));

Related

Using redis in an Express route

I want to simply be able to store a value in a key in one route
/api/foo?redisKey="1" (set value for id=1)
then I want to get the value in another route.
/api/bar?redisKey="1" (get value for id=1)
However, redis is async so you have to wait for it to connect
client.on('connect', function() {
//perform redis operations
});
I'm not sure how to synchronize this in my router.
I am going to assume you're using redis for your client library.
In your express route file, you do not want to create the connection during each request. Instead you will instantiate your redis client outside of the express middleware function, and use it during requests.
Here's a sample app:
var redis = require("redis");
var client = redis.createClient();
var express = require('express')
var app = express()
// GET request
// example: curl "localhost:3000/api/foo?redisKey=1"
app.get('/api/foo', function (req, res) {
var redisKey = req.query.redisKey
client.get(redisKey, function (err, reply) {
if(err){ res.status(400).send(err.getMessage()); return; }
res.status(200).send(reply.toString())
});
})
// for setting, use POST request
// example: curl -X POST "localhost:3000/api/foo?redisKey=1&redisValue=helloWorld"
app.post('/api/foo', function(req, res){
var redisKey = req.query.redisKey,
redisValue = req.query.redisValue
// assuming value is also a string in URL query string
client.set(redisKey, redisValue, function (err, reply){
res.status(200).send(reply.toString())
});
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})

Socket.io only successfully connects if window opens before server starts

I've been trying to resolve a really strange Socket.io bug.
If I open the page on the client while the server is running, it will fail to connect with the message:
universalModuleDefinition:3 WebSocket connection to
'ws://localhost:4000/socket.io/?EIO=3&transport=websocket&sid=f6LwPIDZubiPKE-TAAAA'
failed: Connection closed before receiving a handshake response
If I then restart the server, while leaving the page open, it connects without issue.
app.js
const app = express();
const server = require('http').Server(app);
require('./socket')(server);
// More code here
server.listen(app.get('port'))
socket.js
const io = require('socket.io');
const jackrabbit = require(`jackrabbit`);
const rabbit = jackrabbit(process.env.RABBIT_URI);
const exchange = rabbit.default();
function Socket (app) {
this.io = io(app);
this.io.on('connection', socket => {
socket.emit('sync');
socket.on('room', room => {
socket.join(room);
});
})
this.queue = exchange.queue({ name: 'worker.socket' });
this.queue.consume(this.onMessage.bind(this), { noAck: true });
}
Socket.prototype.onMessage = function (message) {
this.io.to(message.report).emit('photo', message.photo);
}
module.exports = function (app) {
return new Socket(app);
}
client
var socket = io.connect();
socket.on('connect', function () {
// This gets triggered every time (after the error above)
console.log('Connected');
// This is never logged by the server
socket.emit('room', value); // value set by template engine
});
socket.on('sync', function(){
// will not execute first time I connect, but if I restart
// the server, it runs no problem
alert('Synced with server');
})
socket.on('photo', function(data) {
// also will not be run the first time, but works if the
// server is restarted when the page is open
})
Edit:
I've tried rewriting it to
Initialise socket.io within app.js, then pass it to the socket controller
Run server.listen before requiring socket.js
Initialising the client after a timeout
Setting the transport method on the client strictly to websocket
None of these methods have worked
Found the solution to my problem (actually not an issue with any of the code I posted). I was using the compression middleware for Express, which appears to break socket.io. Solution was to add the following:
app.use((req, res, next) => {
// Disable compression for socket.io
if (req.originalUrl.indexOf('socket.io') > -1) {
return next();
}
compression()(req, res, next);
});

How to use middleware that requires socket connection?

I am using express.io and am trying to craft a middleware that requires a connection to a remote server via two sockets. However, I am having a problem.
var net = require('net');
module.exports = function (host, port) {
return function (req, res, next) {
req._messages = net.connect(port, host);
req._commands = net.connect(port, host);
req._messages.on('data', function (data) {
req.io.broadcast('data', data.toString('ascii'));
});
req._messages.write('CF I\r'); // initialization command
next();
}
}
then in my main app:
var port = process.env.CYLON_PORT;
var host = process.env.CYLON_HOST;
var app = require('express.io').http().io();
app.use(require('./cylon/controller')(host, port));
module.exports = app;
However, I am coming across a problem. On each request, it attempts to reconnect. This causes an Error: connect ECONNREFUSED. Ideally, I would like this to connect once when the application starts and maintain that socket, but it needs to intercept each connect.
How can I use sockets in middleware?
You can try that way:
var net = require('net');
module.exports = function (host, port) {
var messagesConnection = net.connect(port, host);
var commandsConnection = net.connect(port, host);
return function (req, res, next) {
req._messages = messagesConnection;
req._commands = commandsConnection;
req._messages.on('data', function (data) {
req.io.broadcast('data', data.toString('ascii'));
});
req._messages.write('CF I\r'); // initialization command
next();
}
}
The call to require('./cylon/controller')(host, port) will start the connections and they will then be reused each time the middleware is called.
Edit: I'm wondering whether you really need to set all those new fields on you req object. You could have some files that export io, messagesConnection and commandsConnection for instance, and you could require them when needed.
Here you end up adding an even listener on messagesConnection each time the middleware is called, which is everything but good.
Edit2: What you could do instead, because you seem to be using express-io:
var net = require('net');
module.exports = function (io, host, port) {
var messagesConnection = net.connect(port, host);
var commandsConnection = net.connect(port, host);
messagesConnection.on('data', function (data) {
io.broadcast('data', data.toString('ascii'));
});
messagesConnection.write('CF I\r'); // initialization command
return function (req, res, next) {
req._messages = messagesConnection;
req._commands = commandsConnection;
next();
}
}
And in your main file:
var port = process.env.CYLON_PORT;
var host = process.env.CYLON_HOST;
var app = require('express.io').http().io();
app.use(require('./cylon/controller')(app.io, host, port));
module.exports = app;

Nodejs accessing nested variable in global scope

How would I access socket in the global scope based on my following NodeJS code
io.on('connection', function (socket) {
console.log('connection '+socket)
socket.on("data",function(d){console.log('data from flash: ',d);});
socket.emit("message","wtfwtwftwftwf hello from server");
socket.on('disconnect', function (socket) {
console.log("disconnect");
});
});
I need to access socket from within the following app.post method
var express = require('express'),
multer = require('multer');
var app = express();
//auto save file to uploads folder
app.use(multer({ dest: './uploads/'}))
app.post('/', function (req, res) {
console.log(req.body); //contains the variables
console.log(req.files); //contains the file references
res.send('Thank you for uploading!');
});
app.listen(8080);
Haven't tested yet but going to try a simple getter function first
io.on('connection', function (socket) {
console.log('connection '+socket)
socket.on("data",function(d){console.log('data from flash: ',d);});
socket.emit("message","wtfwtwftwftwf hello from server");
return{
getSocket: function(){
return socket;
}
};
socket.on('disconnect', function (socket) {
console.log("disconnect");
});
});
io.getSocket() ??
Express's app and Socket.io have nothing to do with one another.
So fundamentally, you can't use socket inside app.post.
You need to identify the client. You can use Passport which has a Socket.io plugin that essentially bridges the app.post/get's req.user to socket.request.user.
Note: It doesn't have to be an authenticated client with user that's fetched from database, just a client with a temporary user stored in memory would do. Something like this:
app.use(function(req, res, next) {
if (!req.user) { // if a user doesn't exist, create one
var id = crypto.randomBytes(10).toString('hex');
var user = { id: id };
req.logIn(user);
res.redirect(req.lastpage || '/');
return;
}
next();
});
var Users = {};
passport.serialize(function(user) {
Users[user.id] = user;
done(null, user);
});
passport.deserialize(function(id) {
var user = Users[id];
done(null, user);
});
Then you can attach the client's socket ID to its user session.
io.on('connection', function (socket) {
socket.request.user.socketid = socket.id
});
And then instead of socket.emit use io.emit in app.post using the socketid
app.post('/', function (req, res) {
io.to(req.user.socketid).emit('whatever');
});
Note: io.to() is used to emit to a room, but every socket is by default joined to the same room named as its socket.id, so it'll work.
Javascript and socketIO experts > please tell me why this simple solution shouldn't work. It seems to...
1 Define a global pointer
var _this=this;
2 In my socketIO handler make a reference to the socket object
_this.socket=socket;
3 And finally within app.post, access the socket like thus
_this.socket.emit(....

Socket.io in Different Files

To use the io object in different files I am using the approach below.. I seems like I am not getting compile errors. So io is "defined" in the users.js file.. But I dont seem to be emmiting any event. Does anyone see the problem?
app.js
var app = require('express')()
, server = require('http').createServer(app)
global = require('./global.js');
global.io = require('socket.io').listen(server);
global.io.configure(function () {
global.io.set("transports", ["xhr-polling"]);
global.io.set("polling duration", 10);
});
// express settings
require('./config/express')(app, config, passport)
require('./config/passport')(passport, config, env)
// Bootstrap routes
require('./config/routes')(app,passport)
var port = process.env.PORT || 3000
server.listen(port)
global.js
var Global = {
io : { }
};
module.exports = Global;
routes.js
var users = require('../app/controllers/users')
module.exports = function (app, passport) {
app.post('/incomingFake', users.getFake);
}
users.js
var global = require('../../global.js')
exports.getFake = function(req, res){
global.io.sockets.on('connection', function(socket) {
socket.broadcast.emit('email', req.body);
});
User.findAndStoreEmail(senderEmail, email ,function(){
res.writeHead(200, {'content-type': 'text/plain'})
res.end('Message Received. Thanks!\r\n')
})
}
and I try to receive this in a backbone collection in the frontend
define([
'jquery',
'underscore',
'backbone',
'Email',
'marionette',
'socketio'
], function ($, _, Backbone, emailModel, Marionette, io) {
'use strict';
var Emails = Backbone.Collection.extend({
url : '/emails',
model : emailModel,
initialize : function() {
var socket = io.connect('http://localhost:3000');
socket.on('email', function (data) {
console.log("i got the socket connection");
console.log(data);
});
}
});
return Emails;
});
edit:
I realized something! I think this might help a lot to resolve the problem, I actually receive the connection event and the socket emits something but this all happens with the page load.. I want this event to be emitted only the 'getFake' function is called
since you are trying to manage connections, based on the route that the client is in.
You can try Socket.io namespaces
Represents a pool of sockets connected under a given scope identified by a pathname (eg: /chat).
By default the client always connects to /.
so on server-side code, a short example would be
io.of("/users").on("connection",function(socket){
/*
when connecting from /users, the socket object will be available here
*/
});
// all clients of pathname /users will get this message
io.of("/users").emit("greeding","Hello fellow users");

Categories