Why no response socket.io with react.js - javascript

server side:
const express = require("express");
const http = require("http");
const socketIo = require("socket.io");
const axios = require("axios");
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);
let interval;
io.on("connection", socket => {
console.log("New client connected");
if (interval) {
clearInterval(interval);
}
interval = setInterval(() => getApiAndEmit(socket), 10000);
socket.on("disconnect", () => {
console.log("Client disconnected");
});
});
const getApiAndEmit = async socket => {
try {
const res = await axios.get(
"https://api.darksky.net/forecast/db057094f57ede5e1f8d33d5e528e4b3/30.9871097,34.9408864"
);
let temp = (((res.data.currently.temperature - 32) * 5) / 9).toFixed(2);
socket.emit("FromAPI", temp);
} catch (error) {
console.error(`Error: ${error.code}`);
}
};
server.listen(port, () => console.log(`Wee wee i'm on port ${port}`));
client side:
import React, { Component } from "react";
import socketIOClient from "socket.io-client";
class App extends Component {
constructor() {
super();
this.state = {
response: false,
endpoint: "http://127.0.0.1:4001"
};
}
componentDidMount() {
const socket = socketIOClient(this.state.endpoint);
socket.on("FromAPI", data => this.setState({ response: data }));
}
render() {
const { response } = this.state;
return (
<div style={{ textAlign: "center" }}>
{
response ?
(<p>The temperature is {response} degrees</p>)
:
(<p>load load...</p>)
}
</div>
);
}
}
export default App;
2 info:
[HMR] Waiting for update signal from WDS...
Download the React DevTools for a better development experience: fb.me/react-devtools
1 error:
http://127.0.0.1:4001/socket.io/?EIO=3&transport=polling&t=N4qdmGu
Failed to load resource: net::ERR_CONNECTION_REFUSED
What's wrong?

You should try to set endpoint in client side to 'http://localhost:4001'.

Related

send a websocket message in a route, Express.js

The Goal:
Let's say we have a marketplace full of shops. I'm creating a specific page localhost:3000/livePurchases/:storeId for a shop owner to monitor live notifications of when they got a new purchase.
alert('you received a new purchase') should be triggered by a WebSocket when an item is purchased.
The Problem:
I need to set up WebSockets on my express server so that the websocket can be triggered somewhere in my code different from where the websocket server was set up. But I don't understand how to do this.
The route /newPurchase/:storeId would be requested by the browser of a customer after they successfully purchase an item. The websocket should send a message within the code of the route "/newPurchase/:storeId" (backend) to the websocket on "/livePurchases/:storeId" (frontend) where the shop owner can monitor live purchases.
app.js
const express = require("express");
module.exports = (config) => {
const app = express();
app.post("/newPurchase/:storeId", (req, res, next) => {
const { storeId } = req.params;
// trigger websocket message to `localhost:3000/livePurchases/:storeId`
// when client requests this route
});
return app;
};
But app.js is exported and run from another script, www.js. In real scenarios this is to connect a database before running the app.:
www.js
const app = require("../server/app")();
const port = process.env.PORT || "4000";
app.set("port", port);
app
.listen(port)
.on("listening", () =>
console.log("info", `HTTP server listening on port ${port}`)
);
module.exports = app;
So that means that the web socket server needs to be set up in www.js.
Below is a notifier service I got it from this tutorial, which seemed like it was trying to solve the problem I have, but it didn't explain how to implement it. It is a class that handles the websocket.
NotifierService.js
const url = require("url");
const { Server } = require("ws");
class NotifierService {
constructor() {
this.connections = new Map();
}
connect(server) {
this.server = new Server({ noServer: true });
this.interval = setInterval(this.checkAll.bind(this), 10000);
this.server.on("close", this.close.bind(this));
this.server.on("connection", this.add.bind(this));
server.on("upgrade", (request, socket, head) => {
console.log("ws upgrade");
const id = url.parse(request.url, true).query.storeId;
if (id) {
this.server.handleUpgrade(request, socket, head, (ws) =>
this.server.emit("connection", id, ws)
);
} else {
socket.destroy();
}
});
}
add(id, socket) {
console.log("ws add");
socket.isAlive = true;
socket.on("pong", () => (socket.isAlive = true));
socket.on("close", this.remove.bind(this, id));
this.connections.set(id, socket);
}
send(id, message) {
console.log("ws sending message");
const connection = this.connections.get(id);
connection.send(JSON.stringify(message));
}
broadcast(message) {
console.log("ws broadcast");
this.connections.forEach((connection) =>
connection.send(JSON.stringify(message))
);
}
isAlive(id) {
return !!this.connections.get(id);
}
checkAll() {
this.connections.forEach((connection) => {
if (!connection.isAlive) {
return connection.terminate();
}
connection.isAlive = false;
connection.ping("");
});
}
remove(id) {
this.connections.delete(id);
}
close() {
clearInterval(this.interval);
}
}
module.exports = NotifierService;
Where I left off implementing the `NotifierService`
I added the websocket server with the NotifierService in www.js
www.js with websockets added
const app = require("../server/app")();
const NotifierService = require("../server/NotifierService.js");
const notifier = new NotifierService();
const http = require("http");
const server = http.createServer(app);
notifier.connect(server);
const port = process.env.PORT || "4000";
app.set("port", port);
server
.listen(port)
.on("listening", () =>
console.log("info", `HTTP server listening on port ${port}`)
);
module.exports = app;
But now how do I send the websocket message from the /newPurchase route in app.js on the backend? If I create a new instance of NotifierService in app.js in order to use the notifierService.send method in the /newPurchase route, then the new NotifierService instance won't have access to the websocket connections because it would be a different instance than the one initiated on www.js.
Front End:
App.js
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import LiveStorePurchases from "./LiveStorePurchases";
function App(props) {
return (
<div className="App">
<Router>
<Switch>
<Route exact path="/livePurchases/:storeId">
<LiveStorePurchases />
</Route>
</Switch>
</Router>
</div>
);
}
export default App;
LivePurchaseServer.js
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
export default function LiveStorePurchases() {
let { storeId } = useParams();
const URL = "ws://127.0.0.1:4000?storeId=" + storeId;
const [ws, setWs] = useState(new WebSocket(URL));
useEffect(() => {
ws.onopen = (e) => {
newFunction(e);
function newFunction(e) {
alert("WebSocket Connected");
}
};
ws.onmessage = (e) => {
const message = e.data;
alert(message);
};
return () => {
ws.onclose = () => {
alert("WebSocket Disconnected");
setWs(new WebSocket(URL));
};
};
}, [ws.onmessage, ws.onopen, ws.onclose, ws, URL]);
return (
<div
style={{
color: "red",
fontSize: "4rem",
}}
>
store: {storeId}
</div>
);
}
app.js:
I was able to move the websocket instance to app.js instead of www.js. Then I simply passed that instance around to other routes.
const express = require("express");
const NotifierService = require("../server/NotifierService.js");
const notifier = new NotifierService();
const http = require("http");
const routes = require("./routes");
module.exports = (config) => {
const app = express();
const server = http.createServer(app); // websocket created
notifier.connect(server); // and connected here in app.js
// I moved POST /newPurchase to routes.js in order
// to demonstrate how the notifier instance can be
// passed around to different routes
app.use(routes(notifier));
return server;
};
routes.js:
I created a routes file routes.js to show that
you could move the notifier instance around and call it from any route.
const express = require("express");
const router = express.Router();
// I moved "/newPurchase/:id" to this routes.js file to show that
// I could move the notifier instance around.
module.exports = (webSocketNotifier) => {
router.post("/newPurchase/:id", (req, res, next) => {
webSocketNotifier.send(req.params.id, "purchase made");
res.status(200).send();
});
return router;
};
www.js:
const server = require("../server/app")();
const port = process.env.PORT || "4000";
server
.listen(port)
.on("listening", () =>
console.log("info", `HTTP server listening on port ${port}`)
);
module.exports = server;

Socket io delay one event

I have connected socket io to Webhook and it succeeds. It can get information and can express But I'm stuck with one problem. When the desired value is displayed on the client-side, the response is delayed. For example, when the server-side detects an event will show on console.log(); But it won't show on the client-side. But when the server-side detects the event again (second time) The value saved the first time is sent to the client-side, and if the server detects the event again (third time) The second event value is sent to the client-side, which means it will delay the event one time. How can I fix this incident?
// Server
const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io', {
transports: ['websocket', 'polling']
})(http, {
cors: {
origin:'*'
}
});
const bodyParser = require("body-parser");
PORT = 8080;
io.on('connection', socket => {
app.use(bodyParser.json());
app.post('/api', (req, res) => {
let x = req.body[0];
let dates = x.created_date;
let name = x.name;
let watchlist = x.watchlist[Object];
if (watchlist == "Matched"){
console.log("Date :", dates)
console.log("Name :", name)
io.emit('FromAPI', dates, name);
}
else {
console.log("Unmatch")
}
res.status(200).end("Successfully");
});
console.log('Socket connected');
socket.on('disconnect', () => { console.log('Socket disconnected')});
});
http.listen(PORT, () => {
console.log(`Server : http://localhost: ${PORT}`);
});
// Client
import { useEffect, useState } from 'react';
import io from 'socket.io-client';
const socket = io("http://localhost:8080", { transports: ['websocket', 'polling'] });
export default function App() {
const [date, setDate] = useState('');
const [name, setName] = useState('');
const [hook_event, setHook_event] = useState([
{ "date": date, "name": name},
{ "date": date, "name": name}
]);
useEffect(() => {
socket.on('FromAPI', (date, name) => {
setDate(date);
setName(name);
hook_event.unshift({"date": date, "name": name});
hook_event.pop();
})
}, [];
return (
<>
<p>Date : {hook_event[0].date} and {hook_event[0].name}</p>
<p>Date : {hook_event[1].date} and {hook_event[1].name}</p>
</>
);
};
I think I found the problem.
const [countState, setCountState] = useState()
useEffect(() => {
socket.on('FromAPI', (date, name) => {
setDate(date);
setName(name);
hook_event.unshift({"date": date, "name": name});
hook_event.pop();
setCountState(hook_event[0])
})
}, [setCountState]; // update setCountState

multiple sessions established to socket io

I'm new to node and trying to create a server with socketio to send messages to front (react). But when I run the server and many connections begin to be established it shows something like this :
Listening on port 3000
client connectedLv3LqI9EhmYTLBIIAAAA
client connectedrKLlxRPDkRs20L43AAAB
client connectedHAUuOADubCigGNTzAAAC
client connectedFF1dTRFe-HFublfgAAAD
Here is my code :
var express = require("express");
var app = express();
const http = require("http");
const SocketIO = require("socket.io");
//database connections
let user;
const MongoClient = require("mongodb").MongoClient;
const uri = "mongodb://localhost:27017/?replicaSet = rs01";
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
client.connect().then((db) => {
let requests = 0;
let responses = 0;
let transactions = 0;
const changeStream = client
.db("gardesh_pay_silk")
.collection("silk_profile")
.watch();
changeStream.on("change", (next) => {
console.log(next);
});
const silkProfileQueries = client
.db("gardesh_pay_silk")
.collection("silk_profile_queries")
.watch();
silkProfileQueries.on("change", (next) => {
console.log(next);
});
const silkSQLQueries = client
.db("gardesh_pay_silk")
.collection("silk_sqlquery")
.watch();
silkSQLQueries.on("change", (next) => {
console.log(next);
transactions += 1;
});
const silkRequest = client
.db("gardesh_pay_silk")
.collection("silk_request")
.watch();
silkRequest.on("change", (next) => {
console.log(next);
user.broadcast.emit("requested", next);
requests++;
});
const silkResponse = client
.db("gardesh_pay_silk")
.collection("silk_response")
.watch();
silkResponse.on("change", (next) => {
console.log(next);
responses += 1;
});
console.log(requests);
});
const server = http.createServer(app);
const io = SocketIO(server);
io.on("connection", (client) => {
user = client;
console.log("client connected"+client.id);
client.on("disconnect", () => {
console.log("user disconnected");
});
});
const port = process.env.PORT || 3000;
server.listen(port, () => console.log('Listening on port'+port));
I think that the problem is in this part of code:
const server = http.createServer(app);
const io = SocketIO(server);
io.on("connection", (client) => {
user = client;
console.log("client connected"+client.id);
client.on("disconnect", () => {
console.log("user disconnected");
});
});
and here is my connection in client side :
import { socketIOClient, io } from "socket.io-client";
import { type } from "jquery";
class Again extends React.Component {
timing=()=> {
this.props.req_changer(this.requests);
this.props.res_changer(this.responses);
this.props.trans_changer(this.transactions);
this.requests = 0;
this.responses = 0;
this.transactions = 0;
}
constructor(props) {
super(props);
this.state = {
endpoint: "http://127.0.0.1:4000",
requested: "undefined",
};
}
componentDidMount() {
this.count = setInterval(this.timing, 1000);
}
componentWillUnmount() {
clearInterval(this.count);
}
render() {
const socket = socketIOClient(this.state.endpoint);
socket.on("requested", (object) => {
if (object.ns.coll == "silk_request") {
this.requests++;
}
if (object.ns.coll == "silk_response") {
this.responses++;
}
if (object.ns.coll == "silk_sqlquery") {
this.transactions++;
}
});
return <div></div>;
}
}
export default Again;```
There is some issues in your code.
A) Client side:
Don't put the const socket & socket.on into the render method. The render method should contain only the visualizations (UI if you want)...
The creation of the socket constant -can be- in the class's constructor, or, outside of the class:
const socket = socketIOClient(this.state.endpoint);
class Again extends React.Component {
.
.
.
}
The listening of the sockets requests should be under the componentDidMount() function:
componentDidMount() {
socket.on("requested", (object) => {
if (object.ns.coll == "silk_request") {
this.requests++;
}
if (object.ns.coll == "silk_response") {
this.responses++;
}
if (object.ns.coll == "silk_sqlquery") {
this.transactions++;
}
});
}
B) Server side:
this is the important issue, the 'Client' attribute you used is a socket:
io.on("connection", (socket) => {
.
.
});
Every socket has a session. So for the same client we can have multiple sessions at the same time (for example, for every refreshing of the application). So you need to save the client's sessions. To do it, you can use the express session middleware:
io.use((socket, next) => {
app.sessionMiddleware(socket.request, {}, next);
});
io.on("connection", (socket) => {
const session = socket.request.session;
session.save();
});
You need also to save all sessions (if the client use multiple navigators for exemple, the server should send the requests for all of them, bu sockets), and then to delete them when client disconnecting:
var onlineClients = {};
var socketToClient = {};
io.on('connection', (socket) => {
const session = socket.request.session;
session.save();
if(onlineClients[session.clientID] === undefined){
onlineClients[session.clientID] = {
sockets: []
}
}
socketToClient[socket.id] = session.clientID;
onlineClients[session.clientID].sockets.push(socket);
.
.
.
socket.on('disconnect', () => {
let clientId = socketToClient[socket.id]
if(clientId){
let clientSockets = onlineClients[clientId].sockets
clientSockets = clientSockets.filter(s => socket.id !== s.id)
onlineClients[clientId].sockets = clientSockets
if(clientSockets.length === 0){
delete onlineClients[clientId]
}
}
});
Conclusion: your question title should be: multiple sessions established to socket io
Hope it can help you!
here is my node server code :
var app = express();
const http = require("http");
const SocketIO = require("socket.io");
// database connections
let user;
const MongoClient = require("mongodb").MongoClient;
const uri = "mongodb://localhost:27017/?replicaSet = rs01";
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
client.connect().then((db) => {
let requests = 0;
let responses = 0;
let transactions = 0;
const changeStream = client
.db("gardesh_pay_silk")
.collection("silk_profile")
.watch();
changeStream.on("change", (next) => {
console.log(next);
});
const silkProfileQueries = client
.db("gardesh_pay_silk")
.collection("silk_profile_queries")
.watch();
silkProfileQueries.on("change", (next) => {
console.log(next);
});
const silkSQLQueries = client
.db("gardesh_pay_silk")
.collection("silk_sqlquery")
.watch();
silkSQLQueries.on("change", (next) => {
console.log(next);
transactions += 1;
});
const silkRequest = client
.db("gardesh_pay_silk")
.collection("silk_request")
.watch();
silkRequest.on("change", (next) => {
console.log(next);
user.broadcast.emit("requested", next);
requests++;
});
const silkResponse = client
.db("gardesh_pay_silk")
.collection("silk_response")
.watch();
silkResponse.on("change", (next) => {
console.log(next);
responses += 1;
});
console.log(requests);
});
const server = http.createServer(app);
var onlineClients = {};
var socketToClient = {};
const io = SocketIO.listen(4000);
io.use((socket, next) => {
app.sessionMiddleware(socket.request, {}, next);
});
io.on('connection', (socket) => {
const session = socket.request.session;
this.user = socket;
session.save();
if (onlineClients[session.clientID] === undefined) {
onlineClients[session.clientID] = {
sockets: []
}
}
socketToClient[socket.id] = session.clientID;
onlineClients[session.clientID].sockets.push(socket);
socket.on('disconnect', () => {
let clientId = socketToClient[socket.id]
if (clientId) {
let clientSockets = onlineClients[clientId].sockets
clientSockets = clientSockets.filter(s => socket.id !== s.id)
onlineClients[clientId].sockets = clientSockets
if (clientSockets.length === 0) {
delete onlineClients[clientId]
}
}
});
});
const port = process.env.PORT || 3000;
server.listen(port, () => console.log(`Listening on port ${port}`));
it is just a very simple server that watch's for changes in mongodb...

no response socket.io with reactjs

two info: [HMR] Waiting for update signal from WDS...
Download the React DevTools for a better development experience: fb.me/react-devtools
error: http://127.0.0.1:4001/socket.io/?EIO=3&transport=polling&t=N4qdmGu
Failed to load resource: net::ERR_CONNECTION_REFUSED what should be done to make this work?
const express = require("express");
const http = require("http");
const socketIo = require("socket.io");
const axios = require("axios");
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);
let interval;
io.on("connection", socket => {
console.log("New client connected");
if (interval) {
clearInterval(interval);
}
interval = setInterval(() => getApiAndEmit(socket), 10000);
socket.on("disconnect", () => {
console.log("Client disconnected");
});
});
const getApiAndEmit = async socket => {
try {
const res = await axios.get(
"https://api.darksky.net/forecast/db057094f57ede5e1f8d33d5e528e4b3/30.9871097,34.9408864"
);
let temp = (((res.data.currently.temperature - 32) * 5) / 9).toFixed(2);
socket.emit("FromAPI", temp);
} catch (error) {
console.error(`Error: ${error.code}`);
}
};
server.listen(port, () => console.log(`Wee wee i'm on port ${port}`));
than
import React, { Component } from "react";
import socketIOClient from "socket.io-client";
class App extends Component {
constructor() {
super();
this.state = {
response: false,
endpoint: "http://127.0.0.1:4001"
};
}
componentDidMount() {
const socket = socketIOClient(this.state.endpoint);
socket.on("FromAPI", data => this.setState({ response: data }));
}
render() {
const { response } = this.state;
return (
<div style={{ textAlign: "center" }}>
{
response ?
(<p>The temperature is {response} degrees</p>)
:
(<p>load load...</p>)
}
</div>
);
}
}
export default App;
You have to first connect to the socket server on the client side so for that add this
socket.on('connect', function(){});
before your event listener.
socket.on("FromAPI", data => this.setState({ response: data }));

Socket connection in a API route controller retains the data of previous call

I have an API endpoint in my Node/Express app. The endpoint is responsible to upload files. There are several stages involved in the upload process. like image conversion, sending images to another third party API, etc. I am using socket.io to tell the client about the current stage of upload.
The problem is, The socket connection works fine in the first call, but in my second call to the endpoint, the socket connection runs twice and retains the data which I sent in the previous call.
Here's my code:
server.js
import ClientsRouter from './api/routes/clients';
import express from 'express';
import http from 'http';
import io from 'socket.io';
const port = process.env.PORT || 3000;
const app = express();
const server = http.Server(app);
const socket = io(server);
app.set('socket', socket);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.set('view engine', 'ejs');
app.use(express.static('dist'))
app.use('/uploads', express.static('uploads'));
app.use('/api/clients', ClientsRouter);
server.listen(port, () => console.log(`Server Listening on ${process.env.URL}`));
api/routes/clients.js
import express from 'express';
import ClientsController from '../controllers/clients';
ClientsRouter.post('/uploadClientData/', clientDataUpload.array('client_data'), ClientsController.uploadClientData);
controllers/clients.js
const uploadClientData = async (req, res) => {
try {
const files = req.files
const clientFolder = req.body.client_folder
const { dbxUser } = req;
const team_member_id = req.body.team_member_id;
const data = req.files.map( i => ({team_member_id, destination: i.destination.substring(1), filename: i.filename, status: 1 } ))
const io = req.app.get("socket");
console.log("Outside Socket", data); //This contains my currently passed data
io.on('connection', async socket => {
console.log("Socket Connection established");
console.log("Inside Socket", data); //But This contains my current data aling with the data that I passed in previous call
await uploadQueue.collection.insertMany(data)
socket.emit('upload stage', { upload_stage: 2, progress: 33 })
await helpers.convertImagesToWebResolution(team_member_id, req.body.dpi, req.body.resolution);
socket.emit('upload stage', { upload_stage: 3, progress: 66 })
await helpers.uploadImagesToDropbox(team_member_id, dbxUser, clientFolder)
socket.emit('upload stage', { upload_stage: 4, progress: 100 })
})
res.status(200).json({message: "Uploaded"});
} catch (error) {
console.log(error)
res.status(500).json({
error
});
}
}
And in my front-end react component
componentDidMount(){
const { currentFolder } = this.props;
this.setState({ client_folder: currentFolder }, () => this.afterFileSelect())
}
componentDidUpdate(prevProps){
const { selectedFiles } = this.props;
if(prevProps.selectedFiles !== selectedFiles){
this.afterFileSelect()
}
}
afterFileSelect = async () => {
const { selectedFiles, setSelectedFiles, currentFolder, user, uploadSettings} = this.props;
let formData = new FormData()
formData.append('client_folder', currentFolder)
formData.append('team_member_id', user.team_member_id)
formData.append('resolution', uploadSettings.resolution.split("x")[0])
formData.append('dpi', uploadSettings.dpi)
for(let selectedFile of selectedFiles){
formData.append('client_data', selectedFile)
}
let uploadResp = uploadSettings.convert_web_res ? await uploadClientData(formData) : await dropboxDirectUpload(formData)
const endpoint = uploadResp.config.url;
const host = endpoint.substring(0, endpoint.indexOf("api"));
const socket = socketIOClient(host);
socket.on("upload stage", data => {
this.setState({upload_stage: data.upload_stage, progress: data.progress})
data.upload_stage === 4 && this.setState({client_folder: ""})
})
}
Also I want to know if this is the correct way to to track upload progress?

Categories

Resources