getting 404 error repeatedly when integrating socket.io with Mean - node.js

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.

Related

Implement socket.io in node.js application controller

good afternoon. I am new to programming sockets in node.js and I need to implement socket.io in a controller of my application. The architecture I have is the following:
The file that starts the server is index.js
const express = require('express');
const app = express();
const port = 3000;
const socketRouter = require('./routes/socket')
app.use(express.json());
//Route
app.use('/socket', socketRouter);
app.listen(port, () => {
console.log(`Server connection on http://127.0.0.1:${port}`); // Server Connnected
});
The file where I define the routes is socket.js
const { Router } = require('express');
const { showData } = require('../controllers/socket');
const router = Router();
router.post('/send-notification', showData);
module.exports = router;
And my controller is:
const { response } = require('express');
const showData = (req, res = response) => {
const notify = { data: req.body };
//socket.emit('notification', notify); // Updates Live Notification
res.send(notify);
}
module.exports={
showData
}
I need to implement socket.io in this controller to be able to emit from it but I can't get it to work. Could you tell me how to do it?
Thanks a lot
CLARIFICATION: if I implement socket.io in the main file it works, but I want to have some order and separate things. This is how it works:
const express = require('express');
const app = express();
const port = 3000;
app.use(express.json());
app.post('/send-notification', (req, res) => {
const notify = { data: req.body };
socket.emit('notification', notify); // Updates Live Notification
res.send(notify);
});
const server = app.listen(port, () => {
console.log(`Server connection on http://127.0.0.1:${port}`); // Server Connnected
});
const socket = require('socket.io')(server);
socket.on('connection', socket => {
console.log('Socket: client connected');
});
Move your socket.io code to its own module where you can export a method that shares the socket.io server instance:
// local socketio.js module
const socketio = require('socket.io');
let io;
modules.exports = {
init: function(server) {
io = socketio(server);
return io;
},
getIO: function() {
if (!io) {
throw new Error("Can't get io instance before calling .init()");
}
return io;
}
}
Then, initialize the socketio.js module in your main app file:
const express = require('express');
const app = express();
const port = 3000;
app.use(express.json());
const server = app.listen(port, () => {
console.log(`Server connection on http://127.0.0.1:${port}`); // Server Connnected
});
// initialize your local socket.io module
const sio = require('./socketio.js');
sio.init(server);
// now load socket.io dependent routes
// only after .init() has been called on socket.io module
const socketRouter = require('./routes/socket')
app.use('/socket', socketRouter);
Then, anywhere you want to access the socket.io server instance, you can
require("./socketio.js") and use the .getIO() method to get the socket.io instance:
// use correct path to socketio.js depending upon where this module
// is located in the file system
const io = require("../../socketio.js").getIO();
// some Express route in a controller
const showData = (req, res) => {
const notify = { data: req.body };
// send notification to all connected clients
io.emit('notification', notify);
res.send(notify);
};
module.exports= {
showData
};
Note: A typical socket.io usage convention on the server is to use io as the server instance and socket as an individual client connection socket instance. Please don't try to use socket for both. This makes it clear that io.emit(...) is attempting to send to all connected clients and socket.emit() is attempting to send to a single connected client.
Also note that if your route is triggered by a form post where the browser itself sends the form post, then that particular client will not receive the results of io.emit(...) done from that form post route because that browser will be in the process of loading a new web page based on the response of the form post and will be destroying its current socket.io connection. If the form post is done entirely via Javascript using an Ajax call, then that webpage will stay active and will receive the results of the io.emit(...).
You can use the same socket and app (if you need to expose APIs as well) in other files if you want to separate socket messages and REST endpoints by functionality or however you choose to organize it. Here's an example of how this can be done:
Create a new file, let's say controller1.js:
function initialize(socket, app) {
socket.on('some-socket-message', socket => {
// Whatever you want to do
});
app.get('/some-endpoint', (req, res) => {
// whatever you want to do
});
}
module.exports = {initialize}
And then add the following to your controller.js
const controller1 = require('path/to/controller1');
...
// At some point after socket and app have been defined
controller1.initalize(socket, app);
This will be the bases of separating your controller however you want, while still using the same socket connection and API port in all of your controllers. You can also refactor the initialize method into different methods, but that would be at your own discretion and how you want to name functions, etc. It also does not need to be called initalize, that was just my name of preference.

Debian server, Node js Port 3000 listening But cant access via browser

On my debian server, I installed node and then started node server on port 3000. The server is running, but it isn't visible from the browser
Now when I try to get it running via my domain or via my ip(for example xx.xxx.xx.xx:3000) or my domain (my-domain.com:3000) in both cases it doesn't work. I think I don't quite get the concept and I tried to search for a billion different things, but I can't find the solution to my problem. Could someone tell me, if I need to setup something else, too?
My server js code is
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
server.listen(3000, () => {
console.log('listening on *:3000');
});
io.on('connection', function (socket) {
socket.on( 'new_message', function( data ) {
io.sockets.emit( 'new_message', {
message: data.message,
date: data.date,
msgcount: data.msgcount
});
});
});
Error i got
You need to listen for GET requests in order to respond to them.
Try adding something like:
app.get('/', function (req, res) {
res.send('GET request test.')
})
In your case make sure you add the route before passing the app to the http.createServer() method, or otherwise just use something like app.listen(3000).
More info in the docs: https://expressjs.com/en/guide/routing.html
why are you using express and http both packages.
you can run server by either of them.
and then add a get route for it.
import { createServer } from "http";
import { Server } from "socket.io";
const httpServer = createServer();
const io = new Server(httpServer, {
// ...
});
io.on("connection", (socket) => {
// ...
});
httpServer.listen(3000);
I hope this will work!

socket.io failing after refresh page in client side

I am using socket.io in a react - node project. I have a problem with the socket not refreshing when the page refreshes.
It works first as the server and the react dev server run for the first time. After using the socket ( emitting something from the server ), refreshing the browser page would result in an error in the web socket.js file :
WebSocket connection to
'ws://localhost:4000/socket.io/?EIO=3&transport=websocket&sid=XTE63CeWdp676cRXAAAF'
failed: Error during WebSocket handshake: Unexpected response code:
400
here is the code I use in client and server :
SERVER :
const express = require('express');
const socketconfig = require('./socket.io');
class Server {
constructor({ config, router, logger, }) {
this.config = config;
this.logger = logger;
this.express = express();
this.express.disable('x-powered-by');
this.express.use(router);
}
start() {
return new Promise((resolve) => {
const http = this.express
.listen(this.config.web.port, () => {
const { port } = http.address();
this.logger.info(`[p ${process.pid}] Listening at port ${port}`);
resolve();
});
var io = require('socket.io').listen(http,function () {
console.log("I AM CONNECTIONG")
});
this.freshio=io.sockets;
socketconfig.setOnConnection(this.freshio, ()=>{
console.log('Connexion COnditions are set');
});
socketconfig.setOnDisconnect(this.freshio, ()=>{
console.log('client disconnected');
});
this.clients = socketconfig.clients;
});
}
}
module.exports = Server;
the start() method would be called when the server is initiated.
the socketConfig file is just a toolkit for saving clients and setting conditions, it doesn't interfere with anything.
CLIENT :
import openSocket from 'socket.io-client';
let url = Store.config.socketserverurl + ":" + Store.config.socketserverport;
const socket = openSocket.connect(url);
Store is just the flux store that has the config files linked to it.
I have tried adding the webSockets method of using socket.io instead of the http method but that was in vain as an other problem spiraled.
what should I do ?

Web socket doesn't open on other machines

Web soket connection error
I've got a problem with my server. It uses express and express-ws for web sockets. The problem is that this server works fine on the local host. But when i run it with the help of ssh (see localhost.run) and access the site by the given link from another computer (through Chrome), the web socket doesn't open and the next error shows up in the console
main.js:12 WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
even though i added cert and key to the server connection. P.S. The site loads too, it's only that the socket doesn't work.
here is the server.js code:
"use strict";
const fs = require("fs");
const credentials = {
key: fs.readFileSync("./key.pem"),
cert: fs.readFileSync("./cert.pem")
};
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const https = require("https");
const PORT = process.env.PORT || 3000;
app.use(express.static(__dirname + "/public/Messenger"));
app.use(express.static(__dirname + "/public/Login"));
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const server = new https.createServer(credentials);
const expressWs = require("express-ws")(app, server);//if i take the
//second argument (server) away, it works fine on localhost:3000, but
//with this it fires the error:
//WebSocket connection to 'ws://localhost:3000/' failed: Connection
//closed before receiving a handshake response
const routes = require("./MVC/Router/router.js"); //importing route
routes(app);
app.listen(PORT, () => {
console.log("listening on port " + PORT);
});
here is the router.js:
"use strict";
module.exports = function(app) {
const database = require("../Controller/controller.js");
// database Routes
app.route("/").get(database.loadPage);
app.route("/login").get(database.loginPageLoad);
app
.route("/signIn")
.get(database.signInPageLoad)
.post(database.signIn);
app
.route("/submitLogin")
.post(database.loginSubmit)
.get(database.showUsers);
app.ws("/", database.sendmsg);
};
which redirects the processing flow to the next part of controller.js:
const CLIENTS = [];
let counter = 0;
exports.sendmsg = (ws, req) => {
console.log(cache.get("lorem"));
ws.on("message", msg => {
if (msg === "connected") {
console.log("connected");
CLIENTS.push([ws, counter]);
ws.send(JSON.stringify({ counter }));
counter++;
} else if (JSON.parse(msg).msg && JSON.parse(msg).ID) {
CLIENTS.forEach(box => {
if (box[1] === msg.ID) {
console.log(`user ${box[1]} is closed`);
box.push("closed");
box[0].close();
} else {
return;
}
});
} else {
sendAll(msg);
}
ws.on("close", () => {
console.log("disconnected");
ws.close();
});
});
};
function sendAll(message) {
for (let i = 0; i < CLIENTS.length; i++) {
if (CLIENTS[i][0].readyState === 1) {
CLIENTS[i][0].send(message);
}
}
}
The last piece of code is just what it does on the server, don't care of it too much. The problem is that the web socket doesn't get open when i enter the link from another computer. How can i solve it?

Nodejs Socket.io Express failed to handshake after calling HTTP API

I am using SocketIo with Nodejs, Express server and MongoDB, I followed the documentation . it works fine when connecting multiple clients they can send messages to each other without any problem . when I made an Http request, I cannot connect any new clients and get this error.
socket.io.js:7370 WebSocket connection to
'ws://localhost:28232/socket.io/?userId=userAmr&EIO=3&transport=websocket&sid=wNTTgrUD-PSeNaIcAAAF'
failed: Error during WebSocket handshake: Unexpected response code:
400
the other connected users before the Http request can continue sending messages without any problem.
I debugged the Socket library and found the client socket request go to connect function then fire errorCode:1
This this my code
/**
* Create Express server.
*/
const app = express();
// API endpoint
app.get('/api/test',(req,res)=>{
res.status(200).send({test:"test"});
});
/**
* Init socket
*/
// the following line not working too
// const server = require('http').createServer(app);
const server = require('http').Server(app);
const io = require('socket.io')(server);
io.on('connection', (socket) => {
// emit message to group
socket.on('emitMessage', (data) => {
io.emit('emitMessage', data);
});
});
The Client side code
import { Injectable } from '#angular/core';
import * as io from "socket.io-client/dist/socket.io.js"
import { BehaviorSubject } from 'rxjs';
#Injectable()
export class AppSocketService {
private url = 'http://localhost:28232';
private socket;
constructor() {
}
connect(){
this.socket = io(this.url,{
query:{userid:"123"},
forceNew:true,
'force new connection': true,
autoConnect: true,
reconnectionDelay: 1000,
timeout: 100000,
reconnectionDelayMax: 5000,});
this.socket.on('connect', () => {
console.log("connect",{"socketId":this.socket.id});
this.startListening();
});
}
startListening(){
this.socket.on('emitMessage', (data) => {
console.log(data);
});
}
emitMessage(message){
this.socket.emit('emitMessage', {message});
}
}
Client version:"socket.io-client": "^1.7.3"
Server version: "socket.io": "^1.7.3"
i found the problem, the package express-status-monitor making this wrong behavior .
try to remove it, and it will work perfectly
// comment these lines, as they making the issue
// const expressStatusMonitor = require('express-status-monitor');
// app.use(expressStatusMonitor());
The final code:
let app = require('express')();
// these two lines were making the problem, please comment them. if you want to reproduce the problem enable them again
// const expressStatusMonitor = require('express-status-monitor');
// app.use(expressStatusMonitor());
let http = require('http').Server(app);
let io = require('socket.io')(http);
let port = process.env.PORT || 3000;
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
app.get('/api/v0/availabilities',(req,res)=>{
res.status(200).send({test:"test"});
});
io.on('connection', (socket) => {
// emit message to group
socket.on('emitMessage', (data) => {
io.emit('emitMessage', data);
});
});
http.listen(port, function(){
console.log('listening on *:' + port);
});

Resources