Make two socket connections talk to each other - node.js

Here is the scenario...
I am working on an app I had an idea for, I'm building it in ember with an express backend. I am using the express-ws so I can run the ws websocket package inside express better. I was not able to get just ws to work with express.
My app will have two people connecting to two different url's that are socket connections, so that they can send and receive information to the server without the other getting it. At least that's the way I've come up in my mind to do it.
What I want is when one user does an interaction over the socket, for that socket to send a message to the other socket to perform an action and send it's information to the user connected on it.
I hope that makes sense. With express-ws here is what I have done so far which works at a basic level.
var express = require('express');
var app = express();
var expressWs = require('express-ws')(app);
app.use(function (req, res, next) {
console.log('middleware');
req.testing = 'testing';
return next();
});
app.get('/', function(req, res, next){
console.log('browser connected');
res.send('welcome to the api browser');
});
app.ws('/', function(ws, req) {
console.log('socket connected');
var object = {
message: 'welcome to the socket api',
time: Date.now().toString()
}
ws.send(JSON.stringify(object));
});
app.listen(1337);
I haven't made the other connection yet but for the time being it will be the same, but when the user on one connection sends a certain message to their socket, I want that socket to perform something and then pass some data to the other socket so it can send some information to it's user.

This might give you an idea of how to store the references for later use:
var express = require('express');
var app = express();
var expressWs = require('express-ws')(app);
// array to hold the connections
var openChannels = [];
app.use(function(req, res, next) {
console.log('middleware');
req.testing = 'testing';
return next();
});
app.get('/', function(req, res, next) {
console.log('browser connected');
res.send('welcome to the api browser');
});
app.ws('/', function(ws, req) {
console.log('socket connected');
// store connection for later reference
openChannels.push(ws);
// #todo: remove from array on disconnect
// set broadcast callback
ws.onmessage = function(msg) {
openChannels.forEach(function(index, item) {
if (item !== ws) { // make sure we're not sending to ourselves
item.send(msg);
}
});
};
var object = {
message: 'welcome to the socket api',
time: Date.now().toString()
}
ws.send(JSON.stringify(object));
});
app.listen(1337);

Related

Prevent closing connection on bad request

I have an express server.
I set socket.setKeepAlive(true, 60000); in order to maintain persistent connection for at least 1min.
Here is the code:
var express = require("express");
var app = express();
var server = app.listen(8080);
app.get("/", (req, res) => {
res.write("Hello Riko");
});
// server.listen(3000);
server.on("connection", function(socket) {
console.log("A new connection was made by a client.");
socket.setKeepAlive(true, 60000);
socket.on("data", data => {
console.log(data);
});
// 30 second timeout. Change this as you see fit.
});
When the client send invalid request, it receives 400 Bad Request
How to prevent connection close on invalid request?
Yes the suggestion i made in the comments works.
server.on('clientError',cb) prevents the default behavior of the stack.
I encountered one problem though. It registers event listener for error event every time clientError is fired. Therefore I changed the code litle bit and ended up with a solution that works for me:
var express = require("express");
var app = express();
var server = app.listen(8080);
app.get("/", (req, res) => {
res.send("Hello Riko");
});
onSocketError = err => {
console.log("Socket Error: " + err);
};
server.on("connection", function(socket) {
socket.on("data", data => {
console.log(data.toString());
});
console.log("A new connection was made by a client.");
});
server.on("clientError", (err, socket) => {
socket.removeAllListeners("error");
});
Hope this would help someone with similar problem.

'Sharing' object between NodeJS modules

I'm new to NodeJS development and I'm doing some tests with the socket.io library. Basically, what I want to do is to stablish a socket.io connection between the clients (Angular 6 web app) and the server and broadcast a message when a new user connects.
Right now, the code is quite simple, and this is what I have:
app.js
var express = require('express');
var http = require('http');
var socketIO = require('socket.io');
// Routes
var twitterRoutes = require('./routes/user');
var app = express();
var server = http.Server(app);
var io = socketIO(server); // <== THIS OBJECT IS WHAT I WANT TO USE FROM THE ROUTES
[ ... ]
io.on('connect', (socket) => {
console.log('New user connected');
socket.on('disconnect', (reason) => {
console.log('User disconnected:', reason);
});
socket.on('error', (err) => {
console.log('Error in connection: ', err);
});
});
I want to use the io object inside the user route, but I don't know how to do it:
routes/user.js
var express = require('express');
var config = require('../config/config');
var router = express.Router();
router.post('/login', (req, res, next) => {
// DO ROUTE LOGIC
// I WANT TO BROADCAST THE NEW LOGGED USER USING io.broadcast.emit, BUT DON'T KNOW HOW
// <=====
});
How could I do it? Thanks in advance,
Not sure if it is the best way but you could share things between request handlers using middleware
// define and use a middleware
app.use(function shareIO(req, res, next) {
req.io = io;
next();
})
Then you could use req.io inside request handlers.
router.post('/login', (req, res, next) => {
// DO ROUTE LOGIC
req.io.emit('event')
});
You could do what you want by injecting your IO var in a function
// app.js
var app = express();
var server = http.Server(app);
var io = socketIO(server);
server.use(require('./router')(io))
...
// router.js
module.exports = function makeRouter(io) {
var router = express.Router();
router.post('/login', (req, res, next) => {
// do something with io
}
return router
}
I don't know if it's the best practice, but I've assigned the io object to a property of the global object, and I can access it from everywhere all across the application. So this is what I did:
app.js
var io = socketIO(server);
global.ioObj = io;
routes/user.js
router.post('/login', (req, res, next) => {
// DO ROUTE LOGIC
if (global.ioObj) {
global.ioObj.sockets.clients().emit('new-message', { type: 'message', text: 'New user has logged in' });
}
});

How to fetch data in mongoDB to show in localhost (Node.js)

I am working on to do list app using mongoDB and node.js. Basically you type what you want to do then click add. I successfully connected the database but it doesn't show the text that's in the database. It shows only the bullets in the localhost.
Here's the code:
app.get('/', function(req, res) {
db.collection('list').find().toArray(function (err, result) {
console.log(result);
if (err) {return};
console.log(err);
res.render('index.ejs', {list: result})
});
});
app.post('/', function(req, res){
console.log(req.body);
db.collection('list').save(req.body, function(err, result) {
if (err) {return};
console.log(err);
console.log('saved')
res.redirect('/');
})
})
I have validated the code you posted and have revised it slightly with comments.
I hope this helps but it seems that the fault might be in the res.render method that is being used. Please refer to the following code:
// Requires
var express = require('express');
var bodyParser = require('body-parser');
var MongoClient = require('mongodb').MongoClient;
// Instantiation
var app = express();
var mongopath = "mongodb://localhost:27017/BitX";
// Port number the REST api works on
var portnum = 7500;
// MongoDB object
var db = null;
MongoClient.connect(mongopath, function(err,ldb){
db = ldb;
});
// Implement Body Parser
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(bodyParser.json());
// Start the REST service
var server = app.listen(portnum, function() {
var host = server.address().address;
var port = server.address().port;
console.log("Content Provider Service listening at http://%s:%s", host, port);
});
// Default route
app.get('/', function(req, res) {
// Find all items in orders and send back results to a front end
db.collection('orders').find().toArray(function (err, result) {
res.send(result);
// Consider that the rendering engine may not be functioning correctly
// SEE MORE: https://stackoverflow.com/questions/21843840/what-does-res-render-do-and-what-does-the-html-file-look-like
//res.render('index.ejs', {list: result})
});
});
// Accept a post on the root
app.post('/', function(req, res){
//Save into orders
db.collection('orders').save(req.body, function(err, result) {
res.send(true);
//res.redirect('/');
});
});
For additional information on the res.render method please have a look at:
What does "res.render" do, and what does the html file look like?
- if you have not already.
Hope it helps!

Express websockets on message get cookie info

I have an express web socket application.
In the onmessage function, I would like to access the cookies of the client that sent the message.
The reason for this is that I'm making a game and I have the user login. I need to check what to name cookie is so that I control the correct player.
This is what I've got so far:
var express = require('express');
var expressWs = require('express-ws');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var app = express();
app.use(cookieParser('secretkey123'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}))
expressWs = expressWs(app);
app.get('/', function(req, res) {
// stuff for logging in
})
app.post('/', function(req, res) {
// stuff for logging in
})
app.get('/logout', function(req, res) {
res.clearCookie('name');
res.redirect('/');
// more stuff for logging in
})
app.ws('/ws', function(ws, req) {
ws.on('open', function() {
// how do I check when a connection is opened?
})
ws.on('message', function(msg) {
// who sent the message? how do I get the cookie info to check the user who send it?
})
ws.on('close', function() {
// the've disconnected
})
})
var server = app.listen(8000, function () {
var host = server.address().address
var port = server.address().port
})
Is this possible?
Also, where do I check when a websocket connection is opened?
I tried the 'open' event but it doesn't seem to be working.
Thanks for the help in advance!
I figured out how to do it!
I forgot that the req argument can be accessed inside the other functions.
This means in the on message function you can just do this:
ws.on('message', function(msg) {
req.cookies.username //do stuff
});
The connection open code can be done before you setup any of the events:
app.ws('/ws', function(ws, req) {
// connection open code here
ws.on('message', function(msg) {
// connection message code here
})
})

NodeJS/Express Room Joining when a View is accessed - Sockets

I'm creating a simple NodeJS/Express webapp that has an api and sockets. Basically, what I want to do is send data to my API from an external source, and then send this data to the socket in pageView/id. Right now it works, but it is sending data to all of the views, not the specific pageView/id.
I know I have to create Rooms and I tried doing this by having my sockets join the room when the webapp is navigated to page/id: (note I do not have logged in users)
router.get('/:id', function (req, res, next) {
res.io.on('connection', function(socket) {
console.log("user connected");
//join room here
});
});
But then this creates multiple connections, every time I refresh the pageView I get a new connection + previous connections on my server side.
How can I join the room when a pageView/id is accessed? Here is my setup...
app.js
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
app.use(function(req, res, next){
res.io = io;
next();
});
module.exports = {app: app, server: server};
/bin/www
var app = require('../app').app;
var http = require('http');
var server = require('../app').server;
server.listen(port);
pageView.hbs
var socket = io.connect();
socket.on('mySocket', function (data) {
console.log(data);
});
pageView.js
router.post('/updatePage', function (req, res, next){
//send to the view
res.io.emit("mySocket", {
device: device
});
});
Ok, I understand now, your mistake is to create the listeners inside your route. You need only one set of event listeners on io. So, this would work :
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
io.on('connection', function(socket) {
console.log("user connected");
socket.join(room);
});
module.exports = {app: app, server: server};
remember that you can pass parameters on your connection, for example, you can do :
var myroom = window.location.pathname.split('pageView/')[1]; //example to get room name, be creative !
var socket = io.connect("http://127.0.0.1:3000/", { query: 'room='+myroom+' });
and on the server :
io.on('connection', function(socket) {
console.log("user connected");
var room = socket.handshake.query.room; // === 'myRoom'
socket.join(room);
socket.emit("mySocket", {
device: device
});
});
pageView.js
router.post('/updatePage', function (req, res, next){
});

Resources