Hundreds of polling requests coming from socket.io, app really slow - javascript

I'm creating a real time chat app with socket.io, express and React.
My issue is that I get hundreds of polling requests until my browser basically crashes, I have no idea why.
I've tried to put a polling duration, a close timeout, a heartbeat interval, I've checked, both my socket.io and socket.io-client are on the same version.. I've tried everything could find on the web but nothing works.
I'm sure it's just a stupid little mistake that I just can't find, if you could help that would be great, thanks!
Here's my code :
import express from "express";
import socketio from 'socket.io';
import path from 'path';
import ioCookieParser from 'socket.io-cookie-parser'
import http from 'http';
const app = express()
const port = process.env.PORT || 8000
app.set("port", port)
const httpServer = new http.Server(app);
const io = socketio(httpServer);
io.use(ioCookieParser(secret));
io.on('connection', function (client) {
const userId = client.request.signedCookies._session;
const clients = new Map();
client.on('login', () => {
clients.set(userId, { client })
console.log("clients :", clients)
})
client.on('message', (message) => {
User.findById(userId, function(err, obj) {
if(err) {
console.log(err);
return null
}
let currentUser = obj["email"];
client.broadcast.emit("received", { message, currentUser });
Connect.then(db => {
console.log("connected correctly to the server");
let chatMessage = new Chat({ message: message, sender: currentUser});
chatMessage.save();
});
})
})
client.on('error', function (err) {
console.log('received error from client:', client.id)
console.log(err)
})
});
Here is an example of a request :
GET localhost:8000 /socket.io/?EIO=3&transport=polling&t=Mideit5&sid=OxvoE0uJbi9DZyk-AAt8 xhr
Thanks!

My issue was that, in the React component, I was declaring :
const socket = io.connect('http://localhost:8000')
inside the component.
I've moved this constant outside of the component and now the issue is solved!

Related

Unable to connect front end to back end while following a tutorial

I am following this tutorial https://www.youtube.com/watch?v=ZwFA3YMfkoc&ab_channel=JavaScriptMastery (github: https://github.com/adrianhajdin/project_chat_application) trying to create a real time chat app and I am unable to make a connection and I am unable to find what the issue is especially since my code looks exactly the same as the video.
import React, { useState, useEffect } from "react"; enter code hereimport queryString from "query-string"; import io from "socket.io-client";
import "./Chat.css";
let socket;
export default function Chat({ location }) { const [name, setName] = useState(""); const [room, setRoom] = useState(""); const ENDPOINT
= "localhost:5000"; console.log("logging!"); useEffect(() => {
const { name, room } = queryString.parse(location.search);
socket = io(ENDPOINT);
setRoom(room);
setName(name);
console.log(name,room)
socket.emit("join", { name, room }, (error) => {
if (error) {
alert(error);
}
}); }, [ENDPOINT, location.search]);
return <div>Hello World</div>; }
The console.log I put in isnt logging either and when I check the console this is the message I get repreatedly:
polling-xhr.js:202 GET http://localhost:5000/socket.io/?EIO=4&transport=polling&t=NSV19AI net::ERR_FAILED
The message I am expecting to get is an io object with "name" and "room" and also a "we have a new connection!!!" on the back end. This is the back end I am trying to connect to which as far as I've seen is working fine:
const express = require("express");
const socketio = require("socket.io");
const http = require("http");
const PORT = process.env.PORT || 5000;
const router = require("./router");
const { callbackify, isRegExp } = require("util");
const app = express();
const server = http.createServer(app);
const io = socketio(server);
io.on("connection", (socket) => {
console.log("we have a new connection!!!");
socket.on("join", ({ name, room }) => {
console.log(name, room);
const error = true;
if (error) {
callback({ error: "error" });
}
});
socket.on("disconnect", () => {
console.log("user disconnected :O!");
});
});
app.use(router);
server.listen(PORT, () => console.log(`server is running on port ${PORT}`));
I've been away from React and Express for a few months now so its likely there is something obvious I'm missing but when the useEffect console.log isnt logging anything I know something strange is happening.
This is the first time I will have posted a question on here so any feedback on how I can better phrase things is welcome.
You have defined,
const ENDPOINT= "localhost:5000";
It should be,
const ENDPOINT = "http://localhost:5000";

How to disable socket.io when the tab is not in use as it is occupying a lot of RAM and Too many connections issue

I just want to get live data from Mysql DB on the UI Reactjs. So that the user need not to refresh it always. After looking over some posts end up creating a socket.io connection so that the client can speak to the server. This is what I tried to get into:
server.js
const express = require("express");
const http = require("http");
const socketIo = require("socket.io");
var assert = require('assert');
const port = process.env.PORT || 4001;
const index = require("./routes/index");
const app = express();
app.use(index);
const server = http.createServer(app);
const io = socketIo(server);
const mysql = require('mysql');
var startDate ;
var endDate ;
var loopVariable = 1;
io.on("connection", (socket) => {
console.log("New client connected");
const con = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'root',
database: 'localstatus',
debug: false,
});
console.log('Connection established ',(loopVariable++));
socket.on("FromUI", (data) => {
startDate = data.startDate;
endDate = data.endDate;
});
var initial_result;
setInterval(() => {
con.query('SELECT * FROM table where start_time BETWEEN ? and ?', [ startDate, endDate ],(err,rows) =>
{
if(err) {
console.log ('error', err.message, err.stack)
}else {
}
if(JSON.stringify(rows) === JSON.stringify(initial_result)){
}else{
if(Changed(initial_result, rows)) {
var result = [];
for (var row in rows) {
var results = [];
results.push({
Id: rows[row].id,
status: rows[row].t_status,
});
result.push({ returnValue:"true",
object: {Id: rows[row].id,
status: rows[row].t_status,
}});
}
socket.emit('FromAPI', result);
}
initial_result = rows;
}
})
function Changed(pre, now) {
if (pre != now)
{
return true
}else{
return false
}}
}, 1000);
socket.on('disconnect', function() {
socket.disconnect();
loopVariable--;
});
});
server.listen(port, () => console.log(`Listening on port ${port}`));
client.js
import React, { useState, useEffect, Component } from "react";
import socketIOClient from "socket.io-client";
import TableUsingReactTable from "./TableUsingReactTable.js";
const ENDPOINT = "http://localhost:4001";
export default function App(){
const [response, setResponse] = useState([]);
useEffect(() => {
const socket = socketIOClient(ENDPOINT);
try{
socket.on("FromAPI", data => {
setResponse(data);
});
}catch (error) {
console.log(error);
}
return () => {
socket.on("disconnect")
socket.disconnect();
};
}, []);
console.log(response)
return (<TableUsingReactTable response={response}></TableUsingReactTable>)
}
I think the socket gets disconnected when the tab gets closed, but what happens when is tab is not in use? And how to disable it when not in use? Even when all the tabs are closed then also the RAM increases. How to reduce the RAM when sockets get closed? And how does socket.io behave when at the same time many hit the URL? Moreover, sometimes I did face the issue as:
code: 'ER_CON_COUNT_ERROR',
errno: 1040,
sqlMessage: 'Too many connections',
sqlState: undefined,
fatal: true
How to handle this case too? I m new to this and not understanding how to proceed further. Can someone help me with this? Thanks a lot.
Firstly, you don't actually need Socket.IO for this use case. Server-Sent Events/EventSource API are fine for this, as you're only sending data in one direction. This gives you the benefit of not needing to load Socket.IO libraries.
Now, the real problem is that you're creating a separate MySQL connection for each individual client. Rather than calling mysql.createConnection() every time a new client connects, you can connect to your database once. (There are situations where this isn't appropriate, but since you're just doing some basic SELECT queries, this is fine.)

How to make a socket.io connection between two different interfaces?

I'm actually trying to make a real-time connection between two different apps. I've found a bunch of tutorials about how to make a chat using socket.io, but that doesn't really help me since it's just the same app duplicated in multiple windows.
I'm making a pick & ban overlay for League of Legends in local development. My first thought was to display the empty overlay on one hand and create an interface to manually update it on the other hand. Socket.io seems to be the right thing to use in my case since it can provide new data without having to reload the component.
This is what I wrote in both apps :
const express = require('express');
const socket = require('socket.io');
// App setup
const app = express();
const server = app.listen(4200, function () {
console.log('Listening to requests on port 4200')
});
// Static files
app.use(express.static('public'));
// Socket setup
const io = socket(server);
io.on('connection', function (socket) {
console.log('Made socket connection', socket.id);
socket.on('change', function (data) {
io.sockets.emit('change', data);
});
});
But I fail to connect them as they have to listen to the same port. What am I doing wrong?
(Forgive my bad English and lack of syntax, I'm doing my best here. :p)
I am certainly not an expert on network programming, but as far as I know you need to have one listening app (backend) and another one to connect to it (client). And you define what happens with all the data (messages) that backend recieves (for example sending the messages it recieves to all the clients in the same chat room).
If I am correct to assume you are trying to connect two listening apps?
simple google search of "nodejs socket server client example" revealed this https://www.dev2qa.com/node-js-tcp-socket-client-server-example/ might wanna take your research in this direction
u can try something like this way
var express = require('express');
var socket = require('socket.io');
// App setup
var app = express();
var server = app.listen(8080, () => {
console.log('App started')
})
// Static file
app.use(express.static('public'))
// Socket SetUp
var io = socket(server);
io.on('connection', socket => {
console.log('made the connection')
socket.on('chat',data => {
io.sockets.emit('chat',data)
});
socket.on('typing',data => {
socket.broadcast.emit('typing',data);
});
})
create another file and
var socket = io.connect('http://localhost:8080')
// Elenment
var message = document.getElementById('message');
handle = document.getElementById('handle');
btn = document.getElementById('send');
output = document.getElementById('output');
feedback = document.getElementById('feedback');
// Emit Events
btn.addEventListener('click', () => {
socket.emit('chat', {
message: message.value,
handle: handle.value
})
})
message.addEventListener('keypress', () => {
socket.emit('typing', handle.value)
})
socket.on('chat',data => {
feedback.innerHTML = '';
output.innerHTML += '<p><strong>' + data.handle +': </strong>' +
data.message + '</p>'
})
socket.on('typing', data => {
feedback.innerHTML = '<p><emp>' + data + ' is typing a message... </emp></p>'
})
details are given here node socket chat app
Ok, figured it out. Here's how it works using express and vue together :
First, setup socket.io in your express server js file :
const express = require('express')
const { Server } = require('socket.io')
const http = require('http')
const app = express()
const server = http.createServer(app)
const io = new Server(server, {
cors: {
origin: '*',
methods: ['GET', 'POST', 'REMOVE']
}
})
const PORT = process.env.PORT || 8080
io.on('connection', (socket) => {
console.log('New socket user')
socket.on('SEND_MESSAGE', data => {
console.log('received message in back')
io.emit('MESSAGE', data)
})
})
server.listen(PORT, () => { console.log(`Server started on port : ${PORT}`)})
As you can see we received from the client "SEND_MESSAGE" and we trigger MESSAGE from the server to forward the information to all the clients. The point I was missing is that we bind SEND_MESSAGE on the socked created from the connection but we emit from the io server.
Now you vue part :
import io from 'socket.io-client'
export default {
data() {
return {
messages: [],
inputMessage: null,
socket: io('http://localhost:8080')
}
},
mounted() {
this.socket.on('MESSAGE', data => {
this.messages.push(data)
})
},
methods: {
sendMessage() {
const message = {
senderID: this.myID,
message: this.inputMessage,
sendAt: new Date()
}
this.socket.emit('SEND_MESSAGE', message)
this.inputMessage = null
},
},
}

socket.io connect issue

I keep getting this GET /socket.io/?EIO=3&transport=polling&t=MfRfeJD 404 4.438 ms - 149 error and I don't know where it's coming from.
I'm trying to integrate a live chat into my application using react, socket.io and express and I keep getting this not found error with the sockets. I'm not sure if the problem is on the client or server side. It appears to be trying to continuously poll the server, but is getting 404's back. That sounds like socket.io isn't running, but it all looks okay to me. It may also have something to do with paths, but I don't really know. I've tried adding different route to the io like "http://localhost:5000/" but still it still can't find the socket.
I get the page to show up and when I click send the message shows up but I can't get the sockets to connect.
In app.js
const express = require('express');
const http = require('http')
const bodyParser = require('body-parser')
const socketIo = require('socket.io')
var app = express();
const server = http.createServer(app)
const io = socketIo(server)
var PORT = process.env.PORT || 5000;
app.post('/', (req, res) => {
const { Body, From} = req.body
const message = {
body: Body,
from: From.slice(8),
}
io.emit('message', message)
res.send(`
<Response>
<Message>Thanks for texting!</Message>
</Response>
`)
})
io.on('connection', socket => {
socket.on('message', body => {
socket.broadcast.emit('message', {
body,
from: socket.id.slice(8)
})
})
})
server.listen(PORT);
In Chat.js
import React from "react";
import io from "socket.io-client";
class Chat extends React.Component {
constructor (props) {
super(props)
this.state = { messages: [] }
}
componentDidMount () {
this.socket = io('http://localhost:5000/')
this.socket.on('message', message => {
this.setState({ messages: [message, ...this.state.messages] })
})
}
handleSubmit = event => {
const body = event.target.value
if (event.keyCode === 13 && body) {
const message = {
body,
from: 'Me'
}
this.setState({ messages: [message, ...this.state.messages] })
this.socket.emit('message', body)
event.target.value = ''
}
}
render () {
const messages = this.state.messages.map((message, index) => {
return <li key={index}><b>{message.from}:</b>{message.body} </li>
})
return (
<div>
<h1>Admin Chat</h1>
<input type='text' placeholder='Enter a message...' onKeyUp={this.handleSubmit} />
{messages}
</div>
)
}
}
export default Chat;
404 is clearly saying no such page
149 will be the line number of the failure, your importing other code so it can be on any of the other code that the line 149 exists
i do see a maybe in app.js and the path
"app.post('/', (req, res) => {" Refers to an absolute path
try changing "http://localhost:5000/"
to "http://localhost:5000" or "http://localhost/:5000"
it looks like the "/" on the end puts the 5000 in the path not the port
--- EDIT --- on closer look at GET /socket.io/?EIO=3&transport=polling&t=MfRfeJD
if chat.js is running on the client and connecting to http://localhost:5000 than;
http://localhost/socket.io/?EIO=3&transport=polling&t=MfRfeJD would be the attempted connection
it looks like client is trying to connect back to itself.
how do you have the client / server setup?
if they are separate machines this would be looking for a non existing url.
either way is happening in the socket.io library.

How to export node express app for chai-http

I have an express app with a few endpoints and am currently testing it using mocha, chai, and chai-http. This was working fine until I added logic for a pooled mongo connection, and started building endpoints that depended on a DB connection. Basically, before I import my API routes and start the app, I want to make sure I'm connected to mongo.
My problem is that I'm having trouble understanding how I can export my app for chai-http but also make sure there is a DB connection before testing any endpoints.
Here, I am connecting to mongo, then in a callback applying my API and starting the app. The problem with this example is that my tests will start before a connection to the database is made, and before any endpoints are defined. I could move app.listen and api(app) outside of the MongoPool.connect() callback, but then I still have the problem of there being no DB connection when tests are running, so my endpoints will fail.
server.js
import express from 'express';
import api from './api';
import MongoPool from './lib/MongoPool';
let app = express();
let port = process.env.PORT || 3000;
MongoPool.connect((err, success) => {
if (err) throw err;
if (success) {
console.log("Connected to db.")
// apply express router endpoints to app
api(app);
app.listen(port, () => {
console.log(`App listening on port ${port}`);
})
} else {
throw "Couldnt connect to db";
}
})
export default app;
How can I test my endpoints using chai-http while making sure there is a pooled connection before tests are actually executed? It feels dirty writing my application in a way that conforms to the tests I'm using. Is this a design problem with my pool implementation? Is there a better way to test my endpoints with chai-http?
Here is the test I'm running
test.js
let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('../server').default;;
let should = chai.should();
chai.use(chaiHttp);
//Our parent block
describe('Forecast', () => {
/*
* Test the /GET route
*/
describe('/GET forecast', () => {
it('it should GET the forecast', (done) => {
chai.request(server)
.get('/api/forecast?type=grid&lat=39.2667&long=-81.5615')
.end((err, res) => {
res.should.have.status(200);
done();
});
});
});
});
And this is the endpoint I'm testing
/api/forecast.js
import express from 'express';
import MongoPool from '../lib/MongoPool';
let router = express.Router();
let db = MongoPool.db();
router.get('/forecast', (req, res) => {
// do something with DB here
})
export default router;
Thank you for any help
After receiving some good feedback, I found this solution works best for me, based on Gomzy's answer and Vikash Singh's answer.
In server.js I'm connecting to the mongo pool, then emitting the 'ready' event on the express app. Then in the test, I can use before() to wait for 'ready' event to be emitted on the app. Once that happens, I'm good to start executing the test.
server.js
import express from 'express';
import bodyParser from 'body-parser';
import MongoPool from './lib/MongoPool';
let app = express();
let port = process.env.PORT || 5000;
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
(async () => {
await MongoPool.connect();
console.log("Connected to db.");
require('./api').default(app);
app.listen(port, () => {
console.log(`Listening on port ${port}.`)
app.emit("ready");
});
})();
export default app;
test.js
//Require the dev-dependencies
import chai from 'chai';
import chaiHttp from 'chai-http';
import server from '../src/server';
let should = chai.should();
chai.use(chaiHttp);
before(done => {
server.on("ready", () => {
done();
})
})
describe('Forecast', () => {
describe('/GET forecast', () => {
it('it should GET the forecast', (done) => {
chai.request(server)
.get('/api/forecast?type=grid&lat=39.2667&long=-81.5615')
.end((err, res) => {
res.should.have.status(200);
done();
});
});
});
});
Express app is an instance of EventEmitter so we can easily subscribe to events. i.e app can listen for the 'ready' event.
Your server.js file will look like below,
import express from 'express';
import api from './api';
import MongoPool from './lib/MongoPool';
let app = express();
let port = process.env.PORT || 3000;
app.on('ready', function() {
app.listen(3000, function() {
console.log('app is ready');
});
});
MongoPool.connect((err, success) => {
if (err) throw err;
if (success) {
console.log('Connected to db.');
// apply express router endpoints to app
api(app);
// All OK - fire (emit) a ready event.
app.emit('ready');
} else {
throw 'Couldnt connect to db';
}
});
export default app;
Just create a function below to connect to mongo and, make it returns a promise.
then use await to wait for it to connect and return. the function could be like that
function dbconnect(){
return new Promise(function(resolve, reject){
MongoPool.connect((err, success) => {
if (err) reject(err);
if (success) {
resolve({'status' : true})
} else {
reject(new Error({'status' : false}))
}
})
})
}
And then, use
await dbconnect();
api(app);
app.listen(port, () => {
console.log(`App listening on port ${port}`);
})
now await line will wait for the function to connect to DB and then return success or error in case of failure.
This is a kind of solution you can use, but I would not recommend you to do this, what we actually do is.
create services and use those services in routes, don't write DB code directly in routes.
and
while writing tests for routes mock/stub those services, and test services separately in other test cases, where you just pass DB object and service will add functions on that DB objects, so in tests you can connect to DB and pass that object to those services to test functions, it will give you additional benefit, if you want to use dummy/test DB for testing you can set that in test cases.
Use Before function in your tests like below :
describe('Forecast', () => {
before(function(done){
checkMongoPool(done); // this function should wait and ensure mongo connection is established.
});
it('/GET forecast', function(cb){
// write test code here ...
});
});
And you can check mongodb connection like this below methods:
Method 1: just check the readyState property -
mongoose.connection.readyState == 0; // not connected
mongoose.connection.readyState == 1; // connected`
Method 2: use events
mongoose.connection.on('connected', function(){});
mongoose.connection.on('error', function(){});
mongoose.connection.on('disconnected', function(){});
You can use running server instead of a express instance.
Start your server with a private port, then take tests on the running server.
ex: PORT=9876 node server.js
In your test block, use chai.request('http://localhost:9876') (replace with your protocol, server ip...) instead of chai.request(server).
If you're using native mongodb client you could implement reusable pool like:
MongoPool.js
// This creates a pool with default size of 5
// This gives client; You can add few lines to get db if you wish
// connection is a promise
let connection;
module.exports.getConnection = () => {
connection = MongoClient(url).connect()
}
module.exports.getClient = () => connection
Now in your test you could,
const { getConnection } = require('./MongoPool')
...
describe('Forecast', () => {
// get client connection
getConnection()
...
In your route:
...
const { getClient } = require('./MongoPool')
router.get('/forecast', (req, res) => {
// if you made sure you called getConnection() elsewhere in your code, client is a promise (which resolves to mongodb connection pool)
const client = getClient()
// do something with DB here
// then you could do something like client.db('db-name').then(//more).catch()
})

Categories

Resources