React and Express - Error during WebSocket handshake on localhost - node.js

I'm having trouble setting up a WebSocket connection using socket.io on my localhost. I'm using express on the server side and React on the client side.
I get the following message whenever I try to open a connection:
WebSocket connection to 'ws://localhost:5000/socket.io/?EIO=4&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 404
I've seen some people having similar issues related to nginx or other web servers, but my problem is happening on localhost, which is why I am asking this question
Server-side code (I am using modules for the import, and I would like to keep it that way)
import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import { createServer } from 'http';
import * as socketio from 'socket.io';
const app = express();
app.use(bodyParser.json({ limit: '30mb', extended: true }));
app.use(bodyParser.urlencoded({ limit: '30mb', extended: true }));
app.use(cors());
app.use('/foo', bar);
const server = createServer(app)
const io = new socketio.Server(server);
io.on('connect', socket => {...});
const PORT = process.env.PORT || 5000
app.listen(PORT, () => console.log(`Server running on port ${PORT}`)))
Client-side code (I've already tried passing different values to the "transports" array but it did not help) :
import io from 'socket.io-client';
let socket;
const Component = () => {
...
const { id } = useParams();
const ENDPOINT = 'ws://localhost:5000';
useEffect(() => {
socket = io(ENDPOINT, { transports: ['websocket', 'polling', 'flashsocket'] });
return () => {
socket.emit('disconnect');
socket.off();
}
}, [ENDPOINT, id]);
return (...)
})

Change last line of Server side code to
server.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Related

Limit of making PUT request

I have a weird problem while I try to make the same PUT request multiple times, it works for about 8,9 times but after I have to refresh the page to work again.
I use React with Node.js (using Sequelize) for request:
React code:
return await axios
.put(`${API_URL}/api/user/createUser`, {
id_game_table: gameTableId,
name: nickName,
})
.then((response) => {
return [response.data, null];
})
.catch((error) => {
return [null, error.response];
});
My Node.js body destructuring:
What my Node.js looks like:
And the requests I was making:
Here, we can see that the first 9 PUT request was a success - this can be seen even in Node.js - but after 9 PUT requests was made all requests that I made was just OPTIONS and no other request was made. As another mention, there is not output in console.
The config for Node.js:
const express = require("express");
const app = express();
const cors = require("cors");
const http = require("http");
const server = http.createServer(app);
const socketIo = require("socket.io");
const index = require("./routes/index");
const port = process.env.PORT || 4001;
app.use(express.json());
app.use(cors());
app.use(index);
app.use("/api/gameTable", require("./routes/gameTable.routes"));
app.use("/api/user", require("./routes/user.routes"));
const io = socketIo(server, { cors: { origin: "*" } });
server.listen(port, () => console.log(`Listening on port ${port}`));
Note: The socket connection doesn't matter, I know that I had this request limit behaviour as well when using Angular with Spring in the past (without socket).

Socket.io is not connecting to my Express.js server

I am trying to get Socket.io to connect to my Express.js server but I haven't been able to successfully connect them despite closely following the examples given on the Socket.io website and other code I have been using for reference.
When I try socket.on('connect', console.log(socket.connected)) on the client side it always returns false and when I log the connect_error it returns server error with the following traceback:
Error: server error
at Socket.onPacket (socket.js:317:1)
at XHR.push../node_modules/#socket.io/component-emitter/index.js.Emitter.emit (index.js:143:1)
at XHR.onPacket (transport.js:100:1)
at callback (polling.js:83:1)
at Array.forEach (<anonymous>)
at XHR.onData (polling.js:86:1)
at Request.push../node_modules/#socket.io/component-emitter/index.js.Emitter.emit (index.js:143:1)
at Request.onData (polling-xhr.js:188:1)
at Request.onLoad (polling-xhr.js:229:1)
at XMLHttpRequest.xhr.onreadystatechange (polling-xhr.js:147:1)
These are the relevant parts of my index.js server file on the backend:
const express = require('express');
const cors = require('cors');
const app = express();
// /db/session is express-session related code
const session = require('./db/session');
const { createServer } = require('http');
const { Server } = require('socket.io');
const PORT = process.env.PORT || 5000;
const httpServer = createServer(app);
app.use(cors({
// DEV_ORIGIN is http://localhost:3000
origin: process.env.DEV_ORIGIN,
credentials: true,
}));
const io = new Server(httpServer, {
cors: {
origin: process.env.DEV_ORIGIN,
methods: ["GET", "POST"],
credentials: true
}
});
app.use(session);
io.use((socket, next) => {
session(socket.request, {}, next);
});
httpServer.listen(PORT, () => console.log(`Server running on port ${PORT}`));
// this never seems to do anything
io.on('connection', function() {
console.log('Connected')
});
Here is the main file I have on the client side for the Socket.io connection:
import io from 'socket.io-client';
// API_ORIGIN is http://localhost:5000
export let socket = io(process.env.API_ORIGIN, {
withCredentials: true,
autoConnect: true,
});
I am testing using the socket from this file in a component like so:
import { socket } from '../../services/socket.js';
import React, { useEffect } from 'react';
export default function Lobby() {
useEffect(() => {
socket.on('connect',
console.log(socket.connected)
)
socket.on('connect_error', (err) => {
console.log(err)
});
}, [])
}
But as I mentioned above, socket.connected always returns false. I would really appreciate your help!
I managed to resolve it. The issue was caused by process.env.API_ORIGIN. By changing the code as follows, I was able to get it to work.
The new client side socket.io file:
import { io } from "socket.io-client";
import { serverAddress } from "../settings";
export let socket = io(serverAddress, {
withCredentials: true,
autoConnect: true,
});
and the settings file I created:
const LOCAL_ADDRESS = 'http://localhost:5000';
const PRODUCTION_ADDRESS = [PRODUCTION URL HERE]
export const devEnv = process.env.REACT_APP_ENV === 'development';
export const serverAddress = devEnv? LOCAL_ADDRESS : PRODUCTION_ADDRESS;

Socket.io + TypeScript: Socket.io not listening for new connections

I've a basic express application which. I'm new to socket.io, so i was reading the docs on how to intergrate it with typescript and express server. Here is what i have for now:
server.ts
import express from "express";
import { Server, Socket } from "socket.io";
import { createServer, Server as S } from "http";
import router from "./routes";
// ----
const app: express.Application = express();
app.use(router);
const PORT: any = 3001 || process.env.PORT;
const server: S = createServer(app);
const io = new Server(server, {});
io.on("connection", (socket: Socket) => {
console.log("we have a new connection");
console.log(socket.id);
});
//
server.listen(PORT, () => {
console.log(`The server is running on port: ${PORT}`);
});
routes/index.ts
import { Request, Response, Router } from "express";
const router: Router = Router();
router.get("/", (_req: Request, res: Response) => {
res.status(200).json({
name: "backend",
language: "typescript",
message: "hello world!",
});
});
export default router;
What i understand is that when i visit the port 3001. I should see a message on the server console telling me that i have a new connection. Unfortunately there's nothing that is being loged on the console. What maybe possibly my mistake?
If i visit http://localhost:3001/, I'm getting the following response
{
"name": "backend",
"language": "typescript",
"message": "hello world!"
}
This is because of the get(/) route in the routes/index.ts file, but no socket.io connection fired after i visited that route.
The websocket is not running on the http protocol but on the websocket protocol. So the URL will be ws://localhost:3001/. But socket.io redirects automatically to ws://localhost:3001/socket.io when using the client API. The client API is pretty easy to use in my opinion.
You could start with the get-started from socket.io for an easy example; https://socket.io/get-started/chat. This will cover the basics of the server and client which you could then apply to your app.

Socket.io keep connection up when server get refreshed / turned down

Premising I'm new to sockets, I'm trying to set up the first configuration on server (Node w/Express).
While doing so, I encountered an issue where nodemon crashes every time I save, by returning Error: listen EADDRINUSE: address already in use :::8080.
Here the server implementation:
import express from "express";
import cors from "cors";
import { Server, Socket } from "socket.io";
import { createServer } from "http";
import morgan from "morgan";
// App configuration
const app = express();
const server = createServer(app);
const io = new Server(server);
const port = process.env.PORT || 8080;
// Middlewares
app.use(cors());
app.use(express.json());
app.use(morgan("combined"));
// Socket connection
io.on("connect", (socket: Socket) => {
socket.send("test");
});
server.listen(port, () => {
console.log(`Server is up and running on ${process.env.NODE_ENV} - port ${port}`);
});
I would think that happens as the socket.io connection never get closed / disconnected, leaving the server instance up listening.
I had a look around and found many q/a that were suggesting commands to find and kill the process, but I'd need a solution able to disconnect the socket automatically every time the express server gets turned down.
I tried the following:
io.on("disconnect", () => {
io.sockets.disconnect();
}
As suggested here, but TS complains about io.sockets.disconnect() with a Property 'disconnect' does not exist on type 'Namespace<DefaultEventsMap, DefaultEventsMap>'.ts(2339).
Any idea how this should be tackled?
I think there may be an issue in socket io initialization. Can you try this type of initialization showed at https://socket.io/get-started/chat#Integrating-Socket-IO:
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const io = require('socket.io')(server);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', (socket) => {
console.log('a user connected');
});
server.listen(3000, () => {
console.log('listening on *:3000');
});
So basically pass server object to the default import from socket io like this(right now, you are using "Server" inside socket.io ):
import express from "express";
import cors from "cors";
import ioDefault, { Server, Socket } from "socket.io";
import { createServer } from "http";
import morgan from "morgan";
// App configuration
const app = express();
const server = createServer(app);
const io = ioDefault(server);
const port = process.env.PORT || 8080;
// Middlewares
app.use(cors());
app.use(express.json());
app.use(morgan("combined"));
// ...
UPDATE:
The issue was another server running on the same port. To check which process is running on a specific port you can run
netstat -anpe | grep "8080" | grep "LISTEN"
if netstat is not installed run sudo apt install net-tools in debian based systems(ubuntu, fedora and others)
you can then kill it with its pid:
kill -9 5454

getting 404 error repeatedly when integrating socket.io with Mean

I'm trying to automatically refresh list when a change is happend in database. so far i'm getting this error in console reapeatedly
so can't find the bug.
app.js
//importing modules
const express = require('express');
const http = require('http');
const path = require('path');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
const socketIO = require('socket.io');
const errorHandler = require('./_helpers/error-handler');
const app =express();
const notice = require('./controllers/noticeController');
const employee = require('./controllers/employeeController');
const users = require('./users/users.controller');
//connect mongoDb
//on connection
mongoose.connection.on('connected',()=>{
console.log('Connected to database');
});
mongoose.connection.on('error',(err)=>{
if(err){
console.log('Error in Database Connection '+err);
}
});
const port = 3000;
//adding middleware
app.use(cors());
//body-parser
app.use(bodyParser.json());
//routes
app.use('/api', notice);
app.use('/api', employee);
app.use('/users', require('./users/users.controller'));
app.use(errorHandler);
const server = http.createServer(app);
const io = socketIO(server);
app.set('io',io);
//static files
app.use(express.static(path.join(__dirname, 'public')));
app.listen(port,()=>{
console.log('Server started at port: '+port);
});
and here is the post and get API with socket.io
noticeController.js
//retrieving notice list
router.get('/notices/get',(req,res)=>{
notice.find({}).then((notices)=>{
res.send(notices)
});
});
//add notice
router.post('/notice/add',(req,res,next)=>{
const io = req.app.get('io');
let newNotice = new notice({
title : req.body.title,
description : req.body.description,
image : req.body.image
});
newNotice.save().then(()=>{
io.emit('newNoticeAdded');
});
});
so can anyone help with this matter?
to client side. I have use socket-io-client package.
ts file.
ngOnInit(): void {
this.socket.on('newNoticeAdded',()=>{
this.noticeService.getNotices()
.subscribe(notices => {
this.notices = notices;
});
});
}
notices is the list that want to update automatically on change.
Right away, I could spot something fishy with your code. Look at the following lines:
const server = http.createServer(app);
const io = socketIO(server);
app.set('io', io);
//static files
app.use(express.static(path.join(__dirname, 'public')));
app.listen(port, ()=>{
console.log('Server started at port: '+ port);
});
What is happening here? Well, let's analyze:
You are creating a HTTP using http.createServer(app), then,
You are passing the server to the socketIO() constructor, after that,
You set up some static file routes for your app, finally,
You call app.listen on your express app to start the express app.
What is missing here? You never called server.listen on your HTTP server!
Why is that important, you ask? Because your Socket.IO server is bound to your HTTP server, not your express app. Since you only told your express app to start accepting connections, your Socket.IO server hasn't been started.
To solve this, you could just call server.listen on your HTTP server instead of you express app, like this:
const server = http.createServer(app);
const io = socketIO(server);
app.set('io', io);
//static files
app.use(express.static(path.join(__dirname, 'public')));
// Notice we called the listen function on your HTTP server
// instead of your express app. Your express app will still work
// because you passed your app to the http.createServer method
server.listen(port, ()=>{
console.log('Server started at port: '+ port);
});
Oh, and also, you should make sure your client-side code is connecting to the correct address. Like, make sure you connect to the address that your server is listening on, not some other address. I'm saying this because your error pictures show that you were trying to connect to port 4200 instead of 3000, which is what your server is listening on.
EDIT Since I saw you weren't sure how to connect your client to the same port as your server is running on, here's some code to help you out.
// You could just do this, and the socket.io client
// will connect to the ```window.location```, which
// is usually what you want.
// This is good because you don't hard-code the URL
// into your code, making it easier for you to put the
// script into production.
const socket = io();
// You could also do ```io.connect```, but BEWARE,
// you have to change the URL that the socket.io client
// connects to manually, so that's why I prefer the above
// method.
const socket2 = io.connect("http://localhost:3000");
You can see the default behaviour of the io() function here
Hope this helps.
You need to use the same port on both sides. My client side typescript service (server is using port 8090):
import { Injectable } from '#angular/core';
// rxjs
import { Observable } from 'rxjs';
// other
import { NGXLogger } from 'ngx-logger';
import { Event } from '../model/event';
import { environment } from '../../../environments/environment';
import * as socketIo from 'socket.io-client';
export let SERVER: string = "";
if (environment.production) {
SERVER = 'http://10.1.1.7:8090'; // EDS Server
} else {
SERVER = 'http://10.1.1.194:8090'; // Portalogic PC
//SERVER = "http://" + window.location.hostname + ":8090";
}
#Injectable({
providedIn: "root"
})
export class SocketService {
debug: boolean = true;
private socket: any;
constructor(
private logger: NGXLogger,
) { }
public initSocket(): void {
if (this.debug) {
this.logger.debug("initialize websocket at " + SERVER);
}
this.socket = socketIo(SERVER);
}
public closeSocket(): void {
this.socket.close();
}
public sendEvent(event: Event, data?: Object): void {
if (this.debug) {
this.logger.debug("sendEvent >> event = " + event.toString() + "; data = " + JSON.stringify(data));
}
this.socket.emit(event.toString(), data);
}
public onEvent(event: Event): Observable<Event> {
return new Observable<Event>(observer => {
this.socket.on(event, (data: any) => observer.next(data));
});
}
}
I call initIoConnection from app.component.ts then subscribe to onEvent events.

Resources