long-running applications and socket.io - node.js

I have a web application that accepts an input from the client (browser) and calls an application at the server side with the input. The problem is that the application will run for a long time (about 20s). In order to not block the client (browser) and show the progress to the user, my server side POST function returns immediately and feeds the user
input to the application. Once the application computed the result, I send the result to the client using socket.io. Another twist to the scenario is that the application can not be repeatedly launched and closed (will cause some problem with CUDA) so the whole server side runs just one instance of the application. A sketch of my server-side code looks like the following. Embedded in the code are the three questions that I have searched through the Internet and tried various solutions but could not work out. Any help on them is really appreciated. If this is not the right place to ask, I would be thankful to any feedback about where to ask this question.
var express = require('express');
const{ spawn }= require('child_process');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var darknet = spawn('./darknet', other arguments...) ;
io.on('connection', function(socket){
app.socket = socket;
console.log('A connection has been established');
})
darknet.stdout.on('data',(data) =>{
// This function is called once darknet finished computation and wrote the result to the stdout
// Question 1: now need to send the data back to the client but have no access to any socket here?
})
io.on('connection', function(socket){
//Question 2: how to store this socket so that I can use it in a solution to the Question 1? Can I use session? */
//Question 3: I observed that once the client side refreshed its page (for example, uploaded an image), the previous socket will be closed and a new socket will be established. How do I make sure that I send the output from the application to the right socket since the original one may be invalid when the output is ready to be sent?
})
app.post('/predict', function (req,res){
darknet.stdin.write(user_input+'\n');
res.render();
})

Related

socket.io - .on not working on client side

I have some very basic code that uses socket.io
Client
var socket = io();
// Do some stuff...
socket.emit('hello', 'please like me');
// Wait for a response
socket.on('hello back!', function(msg) {
console.log('Yay, he replied:', msg);
});
Server
var app = require('express');
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.on('connection', function(socket) {
console.log('a user connected');
socket.on('disconnect', function() {
console.log('user disconnected');
});
socket.on('hello', function() {
// Code to decide wether or not I like the client
// ...
// I do like the client!
socket.emit('hello back!', 'how are you');
console.log('I said hello back!');
});
});
Hopefully you managed to get the picture. So ideally, this would happen:
Client connects
After a while, client emits hello
Server sees this, emits hello back!
Client notices the hello back! event
Everyone is happy!
But this isn't the case for me unfortunately. Instead, steps 1-3 work perfectly. But when you reach step 4 and 5, things start to fall apart. Client never logs anything after hello back! event is emitted.
This is not my full code, I figured it would make it easier for you to understand it. It's very possible that I've made some silly mistake somewhere, maybe without including it in this code. But please let me know if there is anything fundamentally wrong with what I am doing.
Cheers
This is a few years old, but in case anyone stumbles across this in the future:
My problem had nothing to do with my code shown above. Instead, it was a configuration error.
I was hosting my project on glitch.com who state that they support websockets, but the error was caused because the server was not able to get across to the client (client never received any data from server at all after websocket connection was opened).
This was an error that I had in 2018 with glitch.com, and I know that other people have had it too. It also seemed to be related somehow to websockets connecting via a domain name and with SSL, but I can't recall the exact circumstances.

What is the proper way to emit an event with socket.io?

I want to emit an event to the client when a long fucntion comes to an end.
This will show a hidden div with a link - on the client side.
This is the approach i tested:
//server.js
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
require('./app/routes.js')(app, io);
//routes.js
app.post('/pst', function(req, res) {
var url = req.body.convo;
res.render('processing.ejs');
myAsyncFunction(url).then(result => {
console.log('Async Function completed');
socket.emit('dlReady', { description: 'Your file is ready!'});
//do some other stuff here
}).catch(err => {
console.log(err);
res.render('error.ejs');
})
});
I get this
ERROR: ReferenceError: socket is not defined
If i change the socket.emit() line to this:
io.on('connection', function(socket) {
socket.emit('dlReady', { description: 'Your file is ready!'});
});
Then i don't receive an error, but nothing happens at the client.
This is the client code:
<script>
document.querySelector('.container2').style.display = "none";
var socket = io();
socket.on('dlReady', function(data) { //When you receive dlReady event from socket.io, show the link part
document.querySelector('.container1').style.display = "none";
document.querySelector('.container2').style.display = "block";
});
</script>
This whole concept is likely a bit flawed. Let me state some facts about this environment that you must fully understand before you can follow what needs to happen:
When the browser does a POST, there's an existing page in the browser that issues the post.
If that POST is issued from a form post (not a post from Javascript in the page), then when you send back the response with res.render(), the browser will close down the previous page and render the new page.
Any socket.io connection from the previous page will be closed. If the new page from the res.render() has Javascript in it, when that Javascript runs, it may or may not create a new socket.io connection to your server. In any case, that won't happen until some time AFTER the res.render() is called as the browser has to receive the new page, parse it, then run the Javascript in it which has to then connect socket.io to your server again.
Remember that servers handle lots of clients. They are a one-to-many environment. So, you could easily have hundreds or thousands of clients that all have a socket.io connection to your server. So, your server can never assume there is ONE socket.io connection and sending to that one connection will go to a particular page. The server must keep track of N socket.io connections.
If the server ever wants to emit to a particular page, it has to create a means of figuring out which exact socket.io connect belongs to the page that it is trying to emit to, get that particular socket and call socket.emit() only on that particular socket. The server can never do this by creating some server-wide variable named socket and using that. A multi-user server can never do that.
The usual way to "track" a given client as it returns time after time to a server is by setting a unique cookie when the client first connects to your server. From then on, every connection from that client to your server (until the cookie expires or is somehow deleted by the browser) whether the client is connection for an http request or is making a socket.io connection (which also starts with an http request) will present the cookie and you can then tell which client it is from that cookie.
So, my understanding of your problem is that you'd like to get a form POST from the client, return back to the client a rendered processing.ejs and then sometime later, you'd like to communicate with that rendered page in the client via socket.io. To do that, the following steps must occur.
Whenever the client makes the POST to your server, you must make sure there is a unique cookie sent back to that client. If the cookie already exists, you can leave it. If it does not exist, you must create a new one. This can be done manually, or you can use express-session to do it for you. I'd suggest using express-session because it will make the following steps easier and I will outline steps assuming you are using express-session.
Your processing.ejs page must have Javascript in it that makes a socket.io connection to your server and registers a message listener for your "dlready" message that your server will emit.
You will need a top-level io.on('connection', ...) on your server that puts the socket into the session object. Because the client can connect from multiple tabs, if you don't want that to cause trouble, you probably have to maintain an array of sockets in the session object.
You will need a socket.on('disconnect', ...) handler on your server that can remove a socket from the session object it's been stored in when it disconnects.
In your app.post() handler, when you are ready to send the dlready message, you will have to find the appropriate socket for that browser in the session object for that page and emit to that socket(s). If there are none because the page you rendered has not yet connected, you will have to wait for it to connect (this is tricky to do efficiently).
If the POST request comes in from Javascript in the page rather than from a form post, then things are slightly simpler because the browser won't close the current page and start a new page and thus the current socket.io connection will stay connected. You could still completely change the page visuals using client-side Javascript if you wanted. I would recommend this option.

Socket IO makes multiple connections when the page is refreshed - Node JS

I have developed a scrapping tool, that scraps jobs from all websites and save them into the database. I have made my own default log where I get messages(errors, info) etc. I am using socket.io to update my view in real time and for database too.
The problem is when I start the app it perfectly get make socket, and database connections. But when I try to refresh the page, the same connection is made again twice with the same message and different ID's. As much I refresh the page the connections are made, and the id get changed, but for all made connection they use one ID,
Below is the Log which shows it :
I have uploaded this video, please check this as well. Try to watch the very beginning, and then at 01:41 and 03:06, before starting scrapping of the first site the connection is established, but when second website scrapping is started, the Internet Connection message is given twice, and the same stands for when third website scrapping is started, the number of messages get doubled every time. I don't know why.
I have tried following the answer of this question, but still no success. The code is 600+ lines on server file, and 150+ lines second file and same on the client side, that's why I can't upload all and it's a bit confidential.
But the socket connection on the client and server is like this:
Server Side
const express = require("express");
const app = express();
const scrap = require("./algorithm");
const event = scrap.defEvent;//imported from another file
const ms_connect = scrap.ms_connect;
const server = app.listen(8000, function(){ console.log('Listening on 8000'); });
const io = require("socket.io").listen(server);
const internetAvailable = require("internet-available");
app.use(express.static(__dirname + "/"));
app.get("/scrap",function(req,res){
res.sendFile(__dirname+"/index.html");//Set the Default Route
io.on("connection",function(socket){ //On Socket Connection
socketSameMess("Socket",'Sockets Connection Made on ID : <span style="color:#03a9f4;">'+socket.id+'<span>');
ms_connect.connect(function(err){//On Connection with Database
if(err) socketSameMess("database_error",err+" "); // If any error in database connection
socketSameMess("Database ",'Connected to MYSQL Database Successfully...');
})
})
})
function eventSameMess(auth,mess){
//hits the custom console
defEvent.emit("hitConsole",{a:auth,m:mess});
}
Client Side
var socket = io.connect('http://localhost:8000');
socket.on('connect',function(){
if(socket.connected){
initDocument();
}
})
Getting multiple messages
Here are some thumb rules for socketio
if you listen to any event once, you'll get the message once in the callback
if you listen to any event twice, you'll get the message twice in the callback
if you listen to any event nth time, you'll get the message nth in the callback
If you're listening to any event on page load, don't forget to listen off that event before you leave the page (if an event is not globally)
If you forgot to listen off and if you again re-visit page. you'll start listening to events multiple times. because on page load you're listening to the event. and the previous event is not yet stopped by listen off
Don't listen to any event in loop, It may listen to it multiple time and you'll get multiple messages on a single change.
connect socket
const socket = io('http://localhost', {
transports: ['websocket'],
upgrade: false
});
listen and listen off an event
let onMessage = (data) => {
console.log(data);
}
//listen message
socket.on('message', onMessage);
//stop listening message
socket.off('message', onMessage);
remove all listeners on disconnect
socket.on('disconnect', () => {
socket.removeAllListeners();
});
Use Firecamp to test/debug SocketIO events visually.
i was having the problem that each client was getting two socket connections. I thought something is wrong with sockets.
but the problem was
FrontEnd -> React
Created the template using create-react-app
In index.js file it uses something called React.StrictMode
This mode renders some of the App.js components two times.
Just remove that React.StrictMode and try to see if your problem is solved.

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.

emitting data via socket on browser close /window close

I need to send data to nodejs server via socket.io when the user closes the browser tab .
I tried doing :
var data={};
window.onbeforeunload = function() {
// i have a object to be sent
data.data1='abcd';
data.data2=1234;
socket.emit("senddata",data);
}
This code works when the user navigates around clicking links on the site but doesnot work when the user closes the browser tab
I also tried configuring the socket io on server side as below .. thinking the error may be due to socket connection being closed before emitting data:
var io = require('socket.io').listen(app.listen(port));
io.configure(function () {
io.set('close timeout',12000);
});
It also didnt work most of the time.
I also tried this on client side:
var socket = require('socket.io').listen(80, {
"sync disconnect on unload":false
});
It also did not work
I had tried receiving data like this
var io = require('socket.io').listen(app.listen(port));
io.sockets.on('connection', function (socket) {
socket.on('senddata', function (data) {
// data processing
});
});
please ..help me with this problem..thanks in advance..
When user connects - register on server side the time it happened.
Socket.IO has heart beating and pinging functionality. So just subscribe to disconnect event on server side and once it happens - means client disconnected - and you have on server time when client had connection. So that way you have timespan of client session.
Do not trust client to tell you any time or important data, as client can simply 'lie' - which leads to hacks/cheats/bugs on your server-side.
There is no reliable way to send data just before disconnect from client-side at all. There is nothing in Socket.IO for that, nor in just one of transports (WebSockets). As well as there is too many different scenarios of disconnection (power off, force close, ethernet cable out, wifi lose, etc).

Resources