Socket.io only successfully connects if window opens before server starts - javascript

I've been trying to resolve a really strange Socket.io bug.
If I open the page on the client while the server is running, it will fail to connect with the message:
universalModuleDefinition:3 WebSocket connection to
'ws://localhost:4000/socket.io/?EIO=3&transport=websocket&sid=f6LwPIDZubiPKE-TAAAA'
failed: Connection closed before receiving a handshake response
If I then restart the server, while leaving the page open, it connects without issue.
app.js
const app = express();
const server = require('http').Server(app);
require('./socket')(server);
// More code here
server.listen(app.get('port'))
socket.js
const io = require('socket.io');
const jackrabbit = require(`jackrabbit`);
const rabbit = jackrabbit(process.env.RABBIT_URI);
const exchange = rabbit.default();
function Socket (app) {
this.io = io(app);
this.io.on('connection', socket => {
socket.emit('sync');
socket.on('room', room => {
socket.join(room);
});
})
this.queue = exchange.queue({ name: 'worker.socket' });
this.queue.consume(this.onMessage.bind(this), { noAck: true });
}
Socket.prototype.onMessage = function (message) {
this.io.to(message.report).emit('photo', message.photo);
}
module.exports = function (app) {
return new Socket(app);
}
client
var socket = io.connect();
socket.on('connect', function () {
// This gets triggered every time (after the error above)
console.log('Connected');
// This is never logged by the server
socket.emit('room', value); // value set by template engine
});
socket.on('sync', function(){
// will not execute first time I connect, but if I restart
// the server, it runs no problem
alert('Synced with server');
})
socket.on('photo', function(data) {
// also will not be run the first time, but works if the
// server is restarted when the page is open
})
Edit:
I've tried rewriting it to
Initialise socket.io within app.js, then pass it to the socket controller
Run server.listen before requiring socket.js
Initialising the client after a timeout
Setting the transport method on the client strictly to websocket
None of these methods have worked

Found the solution to my problem (actually not an issue with any of the code I posted). I was using the compression middleware for Express, which appears to break socket.io. Solution was to add the following:
app.use((req, res, next) => {
// Disable compression for socket.io
if (req.originalUrl.indexOf('socket.io') > -1) {
return next();
}
compression()(req, res, next);
});

Related

Socket.io server not receiving custom headers send from socket.io client connection

Below is my code to make socket connection by using socket.io. The problem with the following code is I am not able to get customer header set with extraHeaders at server end. Nether socket.request.headers nor socket.handshake.headers` works for me.
const socketIO = require("socket.io-client");
const socket = socketIO('wss://domain.com', {
transports: ["websocket"],
extraHeaders: {
build_number: "227"
}
});
socket.on("connect", () => {
console.log("connected");
});

How to fix the delay between a user disconnecting and the disconnect event firing on the server with socket.io?

I am trying to build a small lobby system with socket.io, but I recently encountered a problem when a user closes a tab to leave the lobby.
I want to have instant (or at least close to instant) feedback on the client-side when the user disconnects from the lobby.
Unfortunately, it always takes about 35 sec between closing a browser tab and the socket.on('disconnect') event firing on the server, which is too long for what I need.
I already did some research and found out, that the connection gets closed instantly when the reason of closure is a transport close or a transport error.
In my case it seems to be a ping timeout, which means that socket.io waits some time until the next package gets sent.
If that doesn't happen, the connection will be closed.
So my question now is, why does the ping timeout reason take place here when closing a tab, and how can I change that to a transport close reason?
My current code looks like this:
const path = require('path');
const http = require('http');
const express = require('express');
const socketio = require('socket.io');
const formatMessage = require('./utils/messages');
const {
userJoin,
getCurrentUser,
userLeave,
getRoomUsers
} = require('./utils/users');
const app = express();
const server = http.createServer(app);
const io = socketio(server);
// Run when client connects
io.on('connection', socket => {
socket.on('joinRoom', ({ username, room }) => {
const user = userJoin(socket.id, username, room);
socket.join(user.room);
console.log("ROOMS AFTER JOIN: ", io.sockets.adapter.rooms);
// Send users and room info
io.to(user.room).emit('roomUsers', {
room: user.room,
users: getRoomUsers(user.room)
});
});
// Runs when client disconnects
socket.on('disconnect', (reason) => {
const user = userLeave(socket.id);
console.log("Reason: ", reason)
if (user) {
// Send users and room info
io.to(user.room).emit('roomUsers', {
room: user.room,
users: getRoomUsers(user.room)
});
}
});
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Sails js client native websocket

I'm trying to use websockets with sails-js but I can't make it work with native javascript websockets.
the tutorial example use the sails.io.js library and it goes a little bit like this:
io.socket.on('hello', function (data) {
console.log('Socket `' + data.id + '` joined the party!');
});
function sendHello () {
// And use `io.socket.get()` to send a request to the server:
io.socket.get('/websockets/hello', function gotResponse(data, jwRes) {
console.log('Server responded with status code ' + jwRes.statusCode + ' and data: ', data);
});
}
This does work, but i want to use the native javascript websockets like this:
let ws = new WebSocket("ws://localhost:1337/websockets/hello");
ws.onopen = function (e) {
console.log("[open] Connection established");
console.log("Sending to server");
ws.send("My name is John");
};
ws.onmessage = function (event) {
console.log(`[message] Data received from server: ${event.data}`);
};
ws.onclose = function (event) {
if (event.wasClean) {
console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
console.log('[close] Connection died');
}
};
ws.onerror = function (error) {
console.log(`[error] ${error}`);
console.log(error);
};
Clean and native javascript websockets with no library needed. Unfortunately I can't seem to make it work.
When I try to connect to sails js server using native websockets I get this message:
WebSocket connection to 'ws://localhost:1337/websockets/hello' failed: Connection closed before receiving a handshake response
Impossible to make it connect, it seems like sails js don't even get the message because i make a log when i get a new connection (using the tutorial code):
module.exports = {
hello: function (req, res) {
console.log("web socket received",req.isSocket)
// Make sure this is a socket request (not traditional HTTP)
if (!req.isSocket) {
return res.badRequest();
}
// Have the socket which made the request join the "funSockets" room.
sails.sockets.join(req, 'funSockets');
// Broadcast a notification to all the sockets who have joined
// the "funSockets" room, excluding our newly added socket:
sails.sockets.broadcast('funSockets', 'hello', { howdy: 'hi there!' }, req);
// ^^^
// At this point, we've blasted out a socket message to all sockets who have
// joined the "funSockets" room. But that doesn't necessarily mean they
// are _listening_. In other words, to actually handle the socket message,
// connected sockets need to be listening for this particular event (in this
// case, we broadcasted our message with an event name of "hello"). The
// client-side code you'd need to write looks like this:
//
// io.socket.on('hello', function (broadcastedData){
// console.log(data.howdy);
// // => 'hi there!'
// }
//
// Now that we've broadcasted our socket message, we still have to continue on
// with any other logic we need to take care of in our action, and then send a
// response. In this case, we're just about wrapped up, so we'll continue on
// Respond to the request with a 200 OK.
// The data returned here is what we received back on the client as `data` in:
// `io.socket.get('/say/hello', function gotResponse(data, jwRes) { /* ... */ });`
return res.json({
anyData: 'we want to send back'
});
}
};
How can I make sails js work with native javascript websockets?
Found a simple solution!
Used the npm package ws: npm i ws
making a new hook: sails generate hook customWebSocket
in the hook :
/**
* WS hook
*
* #description :: A hook definition. Extends Sails by adding shadow routes, implicit actions, and/or initialization logic.
* #docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
*/
const WebSocket = require('ws');
module.exports = function defineWsHook(sails) {
return {
/**
* Runs when this Sails app loads/lifts.
*/
initialize: async function () {
sails.log.info('Initializing custom hook (`WS`)');
console.log("custom hook")
const wss = new WebSocket.Server({ port: 3100 });
wss.on('connection', (socket) => {
console.log('New user connected wss');
socket.on('message', function incoming(message) {
console.log(message)
});
});
}
};
};
Done and done, now i can connect to is using native websocket!
now that i have done that i realize that the socket.io library might be better for handling errors.

socket io client lost conection when server restarts

so I am creating a module for the members that are using my services (cloudlist.xyz).
basically, we have a voting system in our service, this module is making a connection using socket io on the server and socket io client on the module, announcing to the user when someone votes on it
Everything is working normally, but when I restart the server to do some maintenance, all users are disconnected from socket io even when the server is on again
Server side code :
var server = app.listen(process.env.PORT || 3000, () => {
console.log("Your app is listening on port " + server.address().port)
});
var io = require('socket.io')(server)
io.on("connection",function(socket) {
console.log("Someone Joined to our server api!")
})
//that's the part that he emits the event when someone votes
io.of(`vote/${bot}`).emit("voted", user_votes.val());
Module/client side:
var https = require('https');
const { EventEmitter } = require("events");
var fetch = require('node-fetch')
const io = require("socket.io-client");
module.exports = class Cloud_client extends EventEmitter {
constructor(id, token) {
super();
if (!id) throw new Error("Missing client instance on contructor");
if (!token) throw new Error("Missing token on constructor");
this.id = id;
this.token = token;
this.socket = io.connect(`https://www.cloudlist.xyz/vote/${id}`, {
reconnect:true,
autoConnect:true,
reconnectionDelay: 1000,
reconnectionDelayMax : 5000,
reconnectionAttempts: Infinity
});
this.socket.on("connect", () => this.emit("connected"));
this.socket.on("disconnect", (...args) => {this.socket.open();
});
this.socket.on("voted", (...args) => this.emit("voted", ...args));
};
this is an example of someone using the module:
var cdl = require("cloud-list")
var cloud_client = new cdl("701456902160121966","5669556617e2a070ada1688")
cloud_client.on("connected", (data) => {
console.log(`Connected to the api Server`)
})
cloud_client.on("voted", (data) => {
console.log(`Thanks,user ${data.user_name} for voting on us :)`)
})
When I connect to the server, it sends the message of this example saying "Connected to the api Server", but when I restart the server, I don't receive anything. Already tried this.socket.on("disconnect", (...args) => {this.socket.open()}); or this.socket.on("disconnect", (...args) => {this.socket.connect()}); ,but still the same thing,user can't reconnect again.
the only way for users to connect again is to restart his project, which is very bad
Socket connections require the server to be serving. Socket.io doesn't seem good for a voting system unless you want it to be real time. It's expected for clients to restart when the server restarts.
As per with working in Socket server we need to restart our node socket server during the restart of the main servers like apache or Nginx.
Because it is not an automatic process on the server.

expressJS: How to push socketIO on get call?

This is how I set up a simple expressJS server with a socket.IO connection.
My application is reading some sensor data every 10 seconds, which gets pushed to every client. This is no problem for me.
But also one client can call /set-status with some parameter to change some status data. In that case the new status data should be send to every client. That's why a simple request/response attempt is not working.
What do I have to do to push the socketIO connection after /set-status has been called?
const express = require('express')
const http = require('http')
const socketIo = require('socket.io')
const app = express()
const server = http.createServer(app)
const io = socketIo(server)
io.on('connection', socket => {
getStatus(socket)
getData(socket)
setInterval(
() => getData(socket),
10000
)
})
app.get('/set-status', (req, res) => {
// Change some data and push new data to every client
// How to get access to socket?
res.send(200, 'new-data')
})
const getStatus = async socket => {
const res = { initial: 'data' }
socket.emit('exampleStatus', res)
}
const getData = async socket => {
// read some sensor data, which is refreshed every 10 sec.
// this is working as expected
const res = { some: 'sensor data' }
socket.emit('sensorData', res)
}
server.listen(port, () => {
if (process.env.NODE_ENV !== 'production') {
console.log(`Listening on port ${port}`)
}
})
If client sockets are listening for exampleStatus events, you can emit an exampleStatus event from inside of your get callback. It would look like this: io.emit('exampleStatus', data). On your client sockets, you can write a listener which looks like socket.on('exampleStatus, data => // do something with data).

Categories