socket.io in useEffect produces Proxy Error (ECONNRESET) - node.js

Summary
I've built a prototype for an app that is going to have some sort of chat functionality. As of right now the frontend React just pulls data via useEffect. To implement a more dynamic chat feeling I thought about using socket.io. When instantiating socket.io client-side as suggested (in useEffect) I am getting an ECONNRESET error.
Setup:
Backend: Node.js + express.js (listening on Port 5000)
Frontend: React
Frontend: Proxy for local development in package.json like this "proxy": "http://localhost:5000"
Problem:
When initializing my socket client-side like this:
const Flow = () => {
...
const fetchsocketData = () => {
const socket = io();
console.log("trying socket stuff");
socket.on("FromBackEnd", data => {console.log(data)});
};
useEffect(() => fetchsocketData(),[])
...
return (<div>Yolo</div>)
}
The proxying (as defined in the package.json) works nicely with e.g. Axios-calls, however, with socket.io, I get the following error on the server-side:
Proxy error: Could not proxy request
/socket.io/?EIO=3&transport=polling&t=N5v5GOe&sid=yugXlgWYsoqJRqcxAAAT
from localhost:3000 to http://localhost:5000. See
https://nodejs.org/api/errors.html#errors_common_system_errors for
more information (ECONNRESET).
And the following error on the client-side:
websocket.js:116 WebSocket connection to
'ws://localhost:3000/socket.io/?EIO=3&transport=websocket&sid=QKMDK2qmVGT3eud2AAAA'
failed: Error during WebSocket handshake: Unexpected response code:
400
This seems to be a temporal thing though, as the socket.io-connection is ultimately established and emits the test messages.
To make things a little weirder: If I move the socket instantiation into a user-triggered event:
const manuallyInstantiateSocket = () => {
const socket = io();
console.log("trying socket stuff");
socket.on("FromBackEnd", data => {console.log(data)});
}
And call this on a click of a button all works as expected. No error on the back-end side of things. The front-end error persists though.
What am I missing here?

Related

Unable to connect to NodeJS standalone socket.io with ReactJS

I am looking for the best WS solution for IoT project. I am currently testing my options with Web Sockets. I have tried so far two NPM libraries 'ws' and 'websockets'. They worked great both NodeJS and ReactJS implementation was simple. I am now trying websocket.io. Reading the documentation I struggle to create even a simple working example copying the code directly from the documentation. Since the test code is so simple, I am really confused especially after the positive experience with two previous packages. I am pretty sure I am doing something wrong but I am unable to spot my mistake. I am really thankful for anyone helping to spot what am I not doing right.
NodeJS server instance listening on port 8000 (based on example here: https://socket.io/docs/v4/server-initialization/) :
const io = require("socket.io")();
io.on("connection", socket => {
console.log('blip')
});
io.listen(8000);
React client trying to connect to the NodeJS server from port 2000:
import React from "react";
import { io } from "socket.io-client";
class Io extends React.Component {
state = {
wsConnected: false
}
componentDidMount() {
const socket = io.connect('http://localhost:8000');
socket.on("connect", () => {
console.log('connect',socket.id);
});
}
render() {
const { wsConnected } = this.state;
return (
<div>{wsConnected ? 'on' : 'off'}</div>
)
}
}
export default Io
It seems you have CORS problem when in polling transport mode, So you can use Socket.io standalone server like this when you are using polling:
const io = require("socket.io")(
{
cors: {
origin: '*',
}
}
);
io.on("connection", socket => {
console.log(`${socket.handshake.headers.origin} connected`);
});
io.listen(8000);
But it's better use websocket transport if you need persistent http connection. When you are using websocket transport mode, there's no CORS policy.
websocket transport doesn't support http headers like cookies or etc but it is better to use it when there's energy consumption concerns in client side. Also you can force socket.io server to only supports determined transport modes. See socket.io documentations.

Node js with express return connection closed before receiving a handshake response

I have a socket running in nodejs and using this socket in html page this is working fine and some times I'm receiving the error on developer console as like
failed: Connection closed before receiving a handshake response. In this time my update not getting reflect on the user screen. Actually whenever the changes updated in admin screen I written the login in laravel to store this values into the redis and I have used the laravel event broadcast and in node js socket.io read the redis value change and push the values into the user screens.
I have code in laravel as like,
Laravel Controller,
public function updatecommoditygroup(Request $request)
{
$request_data = array();
parse_str($request, $request_data);
app('redis')->set("ssahaitrdcommoditydata", json_encode($request_data['commodity']));
event(new SSAHAITRDCommodityUpdates($request_data['commodity']));
}
In this above controller when the api call receives just store the values into this redis key and broadcast the event.
In my event class,
public $updatedata;
public function __construct($updatedata)
{
$this->updatedata = $updatedata;
}
public function broadcastOn()
{
return ['ssahaitrdupdatecommodity'];
}
Finally I have written my socket.io file as like below,
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis({ port: 6379 } );
redis.subscribe('ssahaitrdupdatecommodity', function(err, count) {
});
io.on('connection', function(socket) {
console.log('A client connected');
});
redis.on('pmessage', function(subscribed, channel, data) {
data = JSON.parse(data);
io.emit(channel + ':' + data.event, data.data);
});
redis.on('message', function(channel, message) {
message = JSON.parse(message);
io.emit(channel + ':' + message.event, message.data);
});
http.listen(3001, function(){
console.log('Listening on Port 3001');
});
When I have update the data from admin I'm passing to laravel controller, and controller will store the received data into redis database and pass to event broadcast.And event broadcast pass the values to socket server and socket server push the data whenever the redis key get change to client page.
In client page I have written the code as like below,
<script src="../assets/js/socket.io.js"></script>
var socket = io('http://ip:3001/');
socket.on("novnathupdatecommodity:App\\Events\\NOVNATHCommodityUpdates", function(data){
//received data processing in client
});
Everything working fine in most of the time and some times issue facing like
**VM35846 socket.io.js:7 WebSocket connection to 'ws://host:3001/socket.io/?EIO=3&transport=websocket&sid=p8EsriJGGCemaon3ASuh' failed: Connection closed before receiving a handshake response**
By this issue user page not getting update with new data. Could you please anyone help me to solve this issue and give the best solution for this issue.
I think this is because your socket connection timeout.
new io({
path:,
serveClient:,
orgins:,
pingTimeout:,
pingInterval:
});
The above is the socket configuration. If you are not configuring socket sometime it behaves strangely. I do not know the core reason, but i too have faced similar issues that implementing the socket configuration solved it.
Socket.io Server
Similar configuration should be done on the client side. There is an option of timeout in client side
Socket.io Client
For example.
Say this is your front-end code
You connect to the socket server using the following command:
io('http://ip:3001', { path: '/demo/socket' });
In your server side when creating the connection:
const io = require("socket.io");
const socket = new io({
path: "/demo/socket",
serveClient: false /*whether to serve the client files (true/false)*/,
orgins: "*" /*Supports cross orgine i.e) it helps to work in different browser*/,
pingTimeout: 6000 /*how many ms the connection needs to be opened before we receive a ping from client i.e) If the client/ front end doesnt send a ping to the server for x amount of ms the connection will be closed in the server end for that specific client*/,
pingInterval: 6000 /* how many ms before sending a new ping packet */
});
socket.listen(http);
Note:
To avoid complication start you http server first and then start you sockets.
There are other options available, but the above are the most common ones.
I am just describing what i see in the socket.io document available in github.socket_config. Hope this helps

Socket.IO, SSL Problems With cloudflare

I'm having a socket.io app that basically receives signals from a frontend in order to kill and start a new ffmpeg process (based on .spawn()).
Everything works like expected, but often I get a 525 error from cloudflare. The error message is: Cloudflare is unable to establish an SSL connection to the origin server.
It works like 9 out of 10 times.I noticed that more of these errors pop up whenever a kill + spawn is done. Could it be the case that something block the event loop and because of this blocks all incoming requests and cloudflare logs these as a handshake failed error?
Contacting cloudflare support gives me back this info (this is the request they do to my server):
Time id host message upstream
2017-08-16T09:14:24.000Z 38f34880faf04433 xxxxxx.com:2096 peer closed connection in SSL handshake while SSL handshaking to upstream https://xxx.xxx.xxx.xxx:2096/socket.io/?EIO=3&transport=polling&t=LtgKens
I'm debugging for some time now, but can't seem to find a solutions myself.
This is how I initialize my socketIO server.
/**
* Start the socket server
*/
var startSocketIO = function() {
var ssl_options = {
key: fs.readFileSync(sslConfig.keyFile, 'utf8'),
cert: fs.readFileSync(sslConfig.certificateFile, 'utf8')
};
self.app = require('https').createServer(ssl_options, express);
self.io = require('socket.io')(self.app);
self.io.set('transports', ['websocket', 'polling']);
self.app.listen(2096, function() {
console.log('Socket.IO Started on port 2096');
});
};
This is the listener code on the server side
this.io.on('connection', function (socket) {
console.log('new connection');
/**
* Connection to the room
*/
socket.on('changeVideo', function (data) {
//Send to start.js and start.js will kill the ffmpeg process and
start a new one
socket.emit('changeVideo');
});
});
Another thing that I observer while debugging (I only got this a few times):
The text new connection displayed on the server and the connected client emits the changevideo event but nothing happens on the server side instead the client just
keeps reconnecting.
This is a simplified version of the nodejs code. If you have more questions, just let me know.
Thanks!

Socket.io: Namespace & WS protocol

Step 1: server
I've created a simple server with Node & Socket.io which declares a namespace under /my-namespace. Once somebody connects, emit a confirmation msg as CONNECT_ACK and emit 3 seconds later another event (for example SOME_EVENT) with a payload:
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
let app = express();
let server = http.createServer(app);
server.listen(3000, () => {
console.log('server listening on port 3000');
let io = new socketIO(server);
io.of('/my-namespace').on('connection', (socket) => {
console.log('welcome!');
socket.emit('CONNECT_ACK');
setTimeout(() => {
io.of('/my-namespace').emit('SOME_EVENT', { a: 4 });
}, 3000);
});
});
Step 2: client
Then, I created the smallest client side which just connects to the namespace and logs when it receives CONNECT_ACK or SOME_EVENT
<!doctype html>
<html>
<head>
<title>example</title>
<script src="./node_modules/socket.io-client/socket.io.js"></script>
<script>
const endPoint = "http://localhost:3000/my-namespace";
io(endPoint)
.on('CONNECT_ACK', () => { console.log("I've connected"); })
.on('SOME_EVENT', (data) => { console.dir(data); });
</script>
</head>
<body>
</body>
</html>
Step 3: Checking everything is awesome
Running the client node index.js and serving the html (I use Python Simple Server) I got the desired in both consoles:
Step 4. Understanding whats going on here
Now, when I opened the Network Chrome tab I started writing this long post. These are the requests:
[WebSocket Protocol]: GET to /socket.io (not to /my-channel) receiving some confirmation bits; POST again to /socket.io including those confirmation bits. OK.
[I don't get this]: again a GET to /socket.io including the confirmation bits which now resolves to the CONNECT_ACK event: ÿ40ÿ40/my-namespaceÿ42/my-namespace,["CONNECT_ACK"]. This is the only event I'm going to receive this way.
[WS]: A GET to /socket.io indicating it's a websoket returns me a 101 (Switching Protocols) and I can recieve the msgs as: 42/my-namespace,["SOME_EVENT",{"a":4}]
which is the event I send from the server & some 2s or 3s periodically
[I don't get this too]: again a GET to /socket.io including the confirmation bits which now resolves to this thing: ÿ6
Why does the client asks for socket.io instead of /my-channel?
Why there is a GET after the WS handshake which receives CONNECT_ACK msg?
I understand that the "inmortal" request which resolves in 101 is the socket itself, and those 2s and 3s are just periodical checks.
Why does all the events start by 42 (I've checked this does not change)
What is that final GET? is it part of the WS protocol?
Why does the client asks for socket.io instead of /my-channel?
When setting up socket.io-server, socket.io will set itself to intercept any request to /socket.io in order to work. Namespaces use the same notation as paths in HTTP, but mean completely different things, and connecting to a namespace performs the same HTTP request to /socket.io, but with a different namespace argument in it.
Why there is a GET after the WS handshake which receives CONNECT_ACK msg?
I can't be sure of this one, but this probably arrived to the server before the WS request, and sent the CONNECT_ACK via polling instead.
Why does all the events start by 42 (I've checked this does not change)
According to this GitHub issue, it defines the packet as a message (4) of type event (2). Personally, I suspect the 4 is actually the protocol version, currently 4, as it's the only reference to that number in the docs except in packet types (which must then be 2).
What is that final GET? is it part of the WS protocol?
Not sure again, but possibly a confirmation that the WS connection has been established and a way for socket.io to confirm that it should switch from polling to WS and start sending the events there.

Spring websocket over stomp

Im new to websocket and have been exploring spring websocket solution, I've implemented the hello world application from the following url: Spring websocket.
Instead of using the index.html page, I would like to call the server from nodejs. Here is my implementation with SockJS and Stompjs.
var url = 'http://localhost:8080'
var SockJS = require('sockjs-client'),
Stomp = require('stompjs'),
socket = new SockJS(url + '/hello'),
client = Stomp.over(socket)
function connect(){
client.connect({}, function(frame){
console.log(frame)
client.subscribe(url + '/topic/greetings', function(greeting){
console.log(greeting)
})
})
}
function sendName(){
var name = 'Gideon'
client.send(url + '/app/hello', {}, JSON.stringify({ 'name': name }))
}
function disconnect(){
if(client)
client.disconnect()
}
function start(){
connect()
sendName()
}
start();
I run the script with node --harmony index.js
This are the errors i'm getting when trying different url:
url :var socket = new SockJS('http://localhost:8080/hello')
Error: InvalidStateError: The connection has not been established yet
url: var socket = new SockJS('/hello')
Error: The URL '/hello' is invalid
url: var socket = new SockJS('ws://localhost:8080/hello')
Error: The URL's scheme must be either 'http:' or 'https:'. '" + parsedUrl.protocol + "' is not allowed.
My dependencies
"dependencies": {
"sockjs-client": "^1.0.3",
"stompjs": "^2.3.3"
}
Project can be found here: https://bitbucket.org/gideon_o/spring-websocket-test
The expected endpoint URL for SockJS is an HTTP endpoint. SockJS will check if the WebSocket protocol is available before using it or falling back to other options like long polling. Your first option is the correct one:
var socket = new SockJS('http://localhost:8080/hello')
The STOMP client connect method is non-blocking, that's why you provide a callback that will be executed when the connection is stablished. You are trying to send a message over that connection right after calling the connect method. The connection hasn't been stablished yet (too fast), and you get the error message:
Error: InvalidStateError: The connection has not been established yet
You'll have to move the sending of the message to the callback provided to the connect method to make sure it is already stablished. The same applies to subscriptions (which you already do in your example).
One more thing to notice is that a STOMP destination is not a URL. There's no need to prefix the destination with http://localhost:8080, the destination should be simply /topic/greetings

Resources