Express specific socket.io connections per page - node.js

So currently my setup is I have a standard app.get('/', etc for my index and inside here, I have my io.on('connection', function etc). Now the goal is so that every time someone connects to only the homepage i can get that socket with io.on(connection and send things to it that way, and my syntax and all is fine however i believe having my io.on('connection' inside a route is my issue.
The problem: Whenever someone connects to my website after i start the server, it works great, for debug examples i have a console.log inside of it and its called once and we are good. However if that same person reloads the page my io.on('connection' is called again, and this time iw ill get 2 console.log's... when I again reload I then get 3 and so on and so on, no matter if i close the page and reopen it or come from a different ip. It seems as if the connection isnt closed when I reload and all the still hanging connections are recalled when I reload.
I know this is a little unorthodox with me not posting my code. Its not the syntax, here is an example of essentially the set up described. Oh and also i need access to the req input from the app.get which is why its in there in the first place, I have passport variables saved in it.
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
io.on('connection', function(socket){
console.log("1 connection");
});
});
I hope this explains my issue well enough. When i looked for answers first i found a bunch of stuff about routing, but was confused. Any help is appreciated!

For what I got from the question and the comments.
You are doing it wrong way. You should never put the ONs and EMITs of socket connection where they are called multiple times as they are appended every time.
for example : first time this is called
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
io.on('connection', function(socket){
console.log("1 connection");
});
});
the io.ons['connection'] has the value
function(socket){
console.log("1 connection");
}
second time you call it the callback is appended again. and the value of io.ons['connection'] is now
function(socket){
console.log("1 connection");
}
function(socket){
console.log("1 connection");
}
So it prints console.log two times
SECOND :
if you want to do the further work after the user is logged in.then you need to verify the user here, you can use a socket.emit('init',..... from client side
and server-side socket.on('init',...... will verify the user and can access else return and close the connection.

Never, ever put event handlers like io.on() inside app.get() handlers. That is just wrong and will not do even close to what you want. It will not have an event handler in place until someone hits your page and then every time someone hits that page, it will add a duplicate event handler. Both of these are wrong.
The structure should look like this:
// watch for people hitting the / route in the browser
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
// listen for incoming webSocket connections
io.on('connection', function(socket){
console.log("new connection");
});
This will prevent the duplicate event handlers you were getting.
In case what you were trying to do is to listen for incoming socket.io connections only from a specific page, that is not a support capability. Socket.io connections are from a browser, not from a specific route. You can get access to the cookies associated with the hosting page and the code in the web page making the browser can connect to something like a specific namespace, but there is no built in correlation between an incoming socket.io connection and a specific route the way there is with http requests.
If you're trying to get access to a session object from an incoming socket.io connection, that can usually be done via the cookies associated with the start of the socket.io connection.

Related

Wait for socket connection to establish (when changing page path), till sending router data to the user

I need the socket connection to establish before I can send data from the route to the user (otherwise it is not possible).
In app.js file I have socket connection logic:
app.use(function(req, res, next)
{
req.sio = sio;
next();
});
sio.on('connection',
function(soc)
{
console.log('socket connected');
soc.on('disconnect', function(reason)
{
console.log('socket disconnected');
});
// and more about socket connection here...
});
In index.js file I have route.post logic:
router.post('/route1', function(req, res, next) // user is moved from index.js to route1.js if he fills the form
{
var fromInput = req.body.form_name;
console.log('DATA passed from INDEX.JS: ' + formInput);
if ((formInput !== '') && (formInput !== null) && (formInput !== undefined))
{
function render()
{
//// first we render the page, so the javascript (with socket.io notes) can be read it and then the browser know that socket connection should be established
return new Promise(function(resolve, reject)
{
resolve(res.render('route1'));
});
}
// I need to pass some data AFTER the socked connection is established - cause I move to a different page path - using a **router.post** and cause of that socket connection is disconnected - so I need to wait till its usable again. For simplicity let suppose socket connection is established after 2 seconds (it is a simple check for req.soc.connected):
var soc = false;
setTimeout(function()
{
soc = true; // after 2 sec soc is true (connection is established)
}, 2000);
// Now I want to create an interval that will monitor IF socket connection is established every 100ms (so checking won't happen to often - it is not "resource hungry"). If socket connection is not ready the function should call it self (recursion) if the socket connection is established it (function) should fire a promise.
var arr = [];
arr.push(exe(100, data));
function exe(delay, d)
{
d = data;
return new Promise(function(resolve)
{
if (d === false)
{
setTimeout(function()
{
console.log('wait another ' + delay + ' [ms] - ' + d);
return resolve(exe(delay, d));
}, delay);
}
else
{
console.log('socket connected!');
return resolve(d);
}
});
}
render().then(function()
{
return Promise.all(arr).then(function(arr)
{
console.log(arr);
});
}).then(function()
{
console.log('ALL DONE!');
});
}
});
Comment are in code. If something isn't clear let me know.
#jfriend00
1 - true,
2 - true,
3 - I call render() immediately - so page is loaded and client make a socket connection, then the rest of the code should execute and send the data.
yes I did use POST with a form. There could be socket connection between the server and index page - not a problem I can create one, but I dunno what for.
"or there could be a socket.io connection created in the response to the POST when the browser renders and processes that." I'm trying that one :) I have data in this router.post I want to sent with help of sockets - but first I need to make a connection.
as I understand it... user did use form, so path is changed (socket connection is broken), then I'm in router.post I render the page FIRST - so the browser can read it's JS and make a socket connection, BUT you want to say that my response is not finished? So the browser say - ok you want me to render a page, but what now - cause we are NOT finish yet?!
So I will never establish a socket connection, cause I did not properly response? And cause of this I will not be able to send the data (later code in router.post) cause socket connection is never established cause I did not response properly? Cause my tests show me otherwise - it is working just fine.
you are right - code should works now.
till socket connection is established.
yea, good catch. I will make some kind of database - redis with express session I guess.
So again step by step.
User did fill the form so he is redirect from index.js to route1.js (so it does not make a difference if there is a socket connection BEFORE filling the form or not cause the connection is lost). We are in process of redirecting him (router.post) so I thought I will render the route1 page immediately, so the JS from it can be read by browser, and socket connection can be established (which take time - and IF its possible). So I wait with the data I want to sent to the user (in router.post for example... the form input or whatever) TILL the connection is established, and the send it to the user, with help of socket.io.
The thing is that socket io connection is lost when you change page (path). So I thought (and it could be wrong cause I'm newb) then I wait till it is established, and then send the data. I hope it does make sense.
This structure can never work. Here's what it looks like you're trying to do:
Express server receives a POST request.
Then, you try to wait for a socket.io connection to appear before you process the POST and send a response.
Finally, when you think you've found a socket.io connection, you then call your render() function to "presumably" send a response.
Without really understanding what you're' trying to accompilsh, there are a number of things wrong with the current code:
A POST request comes from either an Ajax call or a form POST. There is no socket.io connection associated directly with either one of those. There could have been a socket.io connection when the page loaded BEFORE the POST request was sent or there could be a socket.io connection created in the response to the POST when the browser renders and processes that.
Even if there was a socket.io connection created when the browser processes the POST response, you're trying to wait for the socket.io connection BEFORE you send the response so you're waiting for something that won't happen until you're done waiting (essentially a deadlock - A won't finish until B finishes, but B can't start until A finishes).
This structure render().then(waitUntil(100, d)) isn't correct. You MUST pass .then() a function reference. You are passing it a promise (the return value form calling waitUntil(...)). This is the least of your problems though because the overall structure of what you're trying to do is wrong.
The whole implementation of waitUntil() is confused and I can't even tell what it's trying to actually wait for.
This is a server that can field lots of connections from lots of clients. You can't just wait for the "next" socket.io connection and assume that connection is from the client you just got a request for. The only way to associate a socket.io connection with an http request is to use some identifying characteristic in both (usually a cookie) and then in the http request, you get the cookie and look up the cookie to see if you currently have a socket.io connection that matches that cookie. This is something that express-socket.io-session helps with.
Unfortunately, you don't describe what you're really trying to accomplish here so I can't point you to a good solution. All, I can really say here is that this scheme will not work. If you want further help with the actual problem, please edit your question to include the problem description in words (not your coding issues). Show the exact sequence of events you want to happen and explain what you're trying to accomplish and why.

how to make to disconect event fires only when users log out from account (node.js + express+ socket.io)

I have a function to go out from account:
exports.logout = function(req,res){
req.session.destroy( err => {
if(err)
console.log(err);
res.redirect("/login");
});
};
To use socket.io and express together I have a middleware:
io.use(function(socket, next) {
sessionMiddleWare(socket.request, socket.request.res, next);
});
And I have function where I use socket.io. I need to disconect event fires only when user log out from application. But now my disconect event is emit always when I refresh a page:
exports.someFunction = function (io) {
io.on('connection', function(socket){
console.log(`Client connected socketId = ${socket.id}`);
console.log("session_id: " + socket.request.session.session_id);
usersMap.set(socket.request.session.session_id, socket.id);
});
socket.on('disconnect', function(){
usersMap.delete(socket.request.session.session_id);
console.log('user disconnected');
console.log("Map size: " + usersMap.size);
});
});
};
Please help, how to make to disconect event fires only when users log out from account. Sorry for my English.
You should not use socket.io's builtin disconnect event for that purpose. It will be emitted whenever user's connection to the server drops (that happens when he leaves your site or refreshes it), which is not equivalent to him or her logging out.
Since you have a server side logout function, that's the place you can see somebody logged out.
The disconnect event simply does not work the way you are asking for. That event will fire any time the user navigates to a new page or refreshes the current page. That's how socket.io connections work in the browser. A given connection belongs to a particular page and when a new page is loaded, the prior connection is disconnected and then a new connection is created. There is nothing you can do to change that - that's part of the browser architecture. So, the socket.io disconnect event is simply not the same thing as when your user logs out and can never be the same.
Instead, you need to have your own function that does a logout which the user can trigger by some action in the page. This can be either a socket.io message, an ajax call from your page or by navigating to the logout page. That's how a user can logout and it has nothing to do with the disconnect event.
Similarly, a connect event just means that a user (who may or may not already be logged in) has connected. The login state will be stored in either a cookie or in a server-side session (that is indexed by a cookie).

Using Node, Express, (and Socket.io?) to update a page every 1 second

I am a bit new to Node.js and Express, and am currently working on a page where I would like to generate and send messages (from the server) to the client page every 1 second (1250ms, actually). When a user visits the site, I would like the latest message to be broadcasted, with new messages coming in every second after. In other words, every user would see the same message at the same time on the web page, regardless of when they connected to the server.
I have done some searching and have unfortunately have not had any luck playing with code samples online. Here is a ROUGH IDEA to explain:
app.js
'use strict';
var express = require('express');
var app = express();
var http = require( "http" ).createServer( app );
var io = require( "socket.io" )( http );
app.get('/', function (req, res) {
res.sendFile(__dirname + '/public/index.html');
});
http.listen(3000, function(){
/* someFunction to generate new LATESTMESSAGE every 1s */
io.on('connection', function (socket) {
socket.emit('news', { messages: LATESTEMESSAGE })
});
});
I assume I would need to send the message via socket.io from the function that generates the LATESTMESSAGE (every 1s when message is generated, send via socket?)? If that is the case, I am unfamiliar with how I would require socket.io in a page that is NOT the app.js (this function would probably be a class, in its own js file), as socket.io requires app and express (see code above).
I appreciate the help! I have spent a good amount of time pondering this today and would appreciate any direction or assistance. Please let me know if I have not supplied enough information.
p.s. the code above definitely would not accomplish what is needed. just a rough outline to show what i am attempting to accomplish
What you're doing looks like half-duplex communication i.e. Only the server sends data to the client, and not the other way around. Socket.io is full duplex communication, i.e. Server and client send data to each other. So technically what would be best for your requirements is Server Sent Events (SSE) using EventStream. Socket.io might be slightly excessive.
Having said that, what you want is to write a Middleware, to which you pass the application. Please take a look at https://expressjs.com/en/guide/using-middleware.html
Basically, your io would be passed in to the middleware functions, so they'd have access to Socket. And the middleware functions in turn would be imported into your app.js.

How to send a temporary chunk of html and js to a running app on node.js

I just got my first app up and running on Node.js. As of now, it is simply serving up a static file. But I have a few ideas that will be implemented down the road so I'm going ahead and getting Node setup so I'll be ready for it when that time comes.
// get modules
var express = require('express');
var fs = require('fs');
// create app instance
var app = express();
// static files middleware
app.use("/assets", express.static(__dirname + '/assets'));
// main route
app.get('/', function(req, res) {
var html = fs.readFileSync('assets/views/main.html', 'utf8');
res.send(html);
});
// make web server listen on specific port
app.listen(8080);
NOTE: I realize I don't need Express to serve one route, but I figured what's the harm in getting a head start on that as well?! :)
The first idea i had is to make a way to let all users currently using the app that there has been an update and that they must refresh their browser. There is no need to save the current state of the app as it is pretty basic.
So, if I am a user, I'm using the app and boom, I get a pretty little modal-window-alert-notification thingy letting me know that I need to refresh.
Also, if some user loads the app AFTER I have sent the alert, that user should not see the alert at all (because they should already be seeing the new changes).
I have done some googling on the subject and I'm just not satisfied with the examples i found. I do not think I want to have to update a notifications.xml file (which node.js would be watching) to push a new notification.
One solution I can think of would be to make a command that can be executed from the command line that will (somehow) send a new notification to the app. Perhaps using something like Grunt.js (I'm not sure if it would be a good fit for this as I've never used it). Basically, once the notification has been sent, all traces of it should disappear. That's why I think a command line approach would be good.
Here's my question: How (generally speaking) should I implement something like this? I am a Node.js newbie but I am pretty comfortable with javascript and the command line.
Please feel free to offer up any alternative solution you think would be better for me (other than a command line approach), or any tools that you think could help me.
Thanks!
If you want to push updates to a web client, one way is to use something like Socket.io. This creates a two-way pipe between client and server that you can use to asynchronously push updates through.
Sample server:
var app = require('express').createServer()
, io = require('socket.io').listen(app);
app.listen(80);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
Sample client:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
alert('New news has come in! Please refresh your page!');
socket.emit('my other event', { my: 'data' });
});
</script>
The other option is to poll from the client using Javascript to actively look for updates. You would need to keep some state on the server for each client that would determine if that client required an update. The client would then make a get request on some timer interval (once a second, for example) to a secondary route and the server would reply with 'Yes, you need an update' or 'No, you don't need an update'. If yes, the client Javascript would then pop up your modal box and tell the user to refresh.

How to check if connection was aborted in node.js server

I'm making some long polling with node.js.
Basically, node.js server accepts request from the user and then checks for some updates. If there're no updates, it will check them after the timeout.
But what if user has closed his tab, or went to another page? In my case, the script continues working.
Is there a way in node.js to check or detect or to catch an event when user has aborted his request (closed the connection)?
You need to use req.on('close', function(err) { ... }); instead of req.connection.on('close', function(err) { ... });
There is a very important distinction. req.on() adds a listener to this request while req.connection.on(), you add a listener to the (keep-alive) connection between the client and the server. If you use req.connection.on(), every time the client re-uses a connection, you add one more listener to the same connection. When the connection is finally aborted, all listeners are fired.
Function scoping typically keeps you safe from this screwing up your server logic, but it's a dangerous thing nevertheless. Fortunately at least NodeJS 0.10.26 is smart enough to warn the user of this:
(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.
Trace:
at Socket.EventEmitter.addListener (events.js:160:15)
at Socket.Readable.on (_stream_readable.js:689:33)
...
Thanks to Miroshko's and yojimbo87's answers I was able to catch the 'close' event, but I had to make some additional tweaks.
The reason why just catching 'close' event wasn't fixing my problem, is that when client sends the request to the node.js server, the server itself can't get information if the connection is still open until he sends something back to the client (as far as I understood - this is because of the HTTP protocol).
So, the additional tweak was to write something to the response from time to time.
One more thing that was preventing this to work, is that I had 'Content-type' as 'application/json'. Changing it to 'text/javascript' helped to stream 'white spaces' from time to time without closing the connection.
In the end, I had something like this:
var server = http.createServer(function(req,res){
res.writeHead(200, {'Content-type': 'text/javascript'});
req.connection.on('close',function(){
// code to handle connection abort
});
/**
* Here goes some long polling handler
* that performs res.write(' '); from time to time
*/
// some another code...
});
server.listen(NODE_PORT, NODE_LISTEN_HOST);
My original code is much bigger, so I had to cut it a lot just to show the sensitive parts.
I'd like to know if there are better solutions, but this is working for me at the moment.
Is there a way in node.js to check or detect or to catch an event when
user has aborted his request (closed the connection)?
You can try to use http.ServerRequest close event. Simple example:
var http = require("http"),
util = require("util");
var httpServer = http.createServer(function(req, res) {
util.log("new request...");
// notify me when client connection is lost
req.on("close", function(err) {
util.log("request closed...");
});
// wait with response for 15 seconds
setTimeout(function() {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write("response");
res.end();
util.log("response sent...");
}, 15000);
});
httpServer.listen(8080);
util.log("Running on 8080");
I'm using Express.js (~4.10.6) and the following code is working fine for me:
//GET Request:
app.get('/', function(req, res){
req.on('close', function(){
console.log('Client closed the connection');
});
});
As soon as I close the browser's tab, the browser closes the connection, and the callback function gets executed as expected.
Seems that your question is very similar to this one:
NodeJS HTTP request connection's close event fired twice
try
request.connection.on('close', function () {
...
});

Resources