Socket-io client keeps sending connect requests - node.js

I am creating app that includes some real-time features. I use Node.js, SocketIO and Express + React on frontend. My backend handles both http and ws requests. I use webpack-dev-server with proxy option to redirect client's requests to my local server.
Some days ago, although I haven't touch neither webpack configuration nor backend code, single call io({ path: '/socket' }) on client side results in sending A LOT of connect requests to my backend.
The reason is not HMR, component's rerendering or some loop on client side. I am sure client side calls io({ path: '/socket' }) only once. Even when I change branches to some old ones (in which the problem for sure didn't exist), after reinstalling all node_modules, the issue remains. So this can be some network/hardware related even. I use Windows 10, Node.js 6.
Exemplary logs in debug mode that my backend produces in the console:
This is only part of logs, in general it keeps connecting really fast and doesn't stop.
Any idea what is going on here?

The problem was that socket.io kept using long polling even if websocket was already available. To solve this I passed transports: ['websocket'] to connection options like this:
import io from 'socket.io-client';
io({ path: '/socket', transports: ['websocket'] });

Related

Error connecting to socket.io server, changed code from websocket to socket.io

I'm currently using websocket library on both nodejs backend and JS client.
I'm trying to change to socket.io, I managed to write the code for both sides however I'm unable to connect to the server from my client.
On the console of my browser I see the error and realised that the address is being changed by the socketio library.
websocket: wss://domain.com/asset-ws/ ==> works fine
socketio: auto changes the url to wss://domain.com/socket.io/?EIO=4&transport=websocket
The error: WebSocket connection to 'wss://domain.com/socket.io/?IO=4&transport=websocket' failed.
Is it something to do with my nginx config?
Any help would be much appreciated.
Thank you
By default socket.io url adds the /socket.io path.
In my Nginx config I'm already using a custom path add, so I just specified a path on the io server config, like so:
const io = new Server(server, {
path: '/'
});
This way the default path would be overwritten.

Connect to a socket.io server from a Node.js server using the 'ws' package

I have a Node.js server which utilizes the popular ws package for using web sockets. I'd like to use this library to connect to an third party server which is running socket.io.
If I were to use socket.io on my server, the connection code would be something like this:
const socket = socketIo('https://api.example.com/1.0/scores')
I've attempted to connect to the same service using the ws package, and modifying the url:
const wsClient = new WebSocket('wss://api.example.com/1.0/scores');
but this results in the following:
Error: Unexpected server response: 200
Question:
What needs to be done to connect to a third party server running socket.io from a server running the ws package?
Additional Info:
I've noticed in my searches that some people have suggested appending
/socket.io/?EIO=3&transport=websocket to the end of the url. This
does not throw the same error as above (> Error: Unexpected server
response: 200) nor throw any visible error, but does not appear to
work (no data is received from the remote server).
Using new WebSocket('ws://api.example.com/1.0/scores?EIO=3&transport=websocket'); to open the connection (via ws) results in the following stack trace:
{ Error: Parse Error
at Socket.socketOnData
at emitOne
at Socket.emit
// ...
}
The socket.io api utilizes websockets but it also has a lot of other functions built on top of it in order to do things such as HTTP handshakes, session ids, and it can even handle fail overs to other protocols when needed.
You got half of the issue so far. Adding the line socket.io/?EIO=3&transport=websocket you're specifying parameters for the socket.io server to take.
EIO=3 specifies the version number for engine.io in which socket.io is using. In this case you are saying engine.io version = 3
transport=websocket specifies which transport protocol to use. As i said earlier, socket.io uses other protocols in cases such as fail overs. This portion forces socket.io to use websocket as the preferred protocol.
Now the next half is the WebSocket. WebSocket allows for Extensions which includes different kinds of compression that are commonly used when sending data. Which I believe is what is causing your Parse Error
Try this (found here):
const WebSocket = require('ws');
const ws = new WebSocket('ws://server/socket.io/?EIO=3&transport=websocket', {
perMessageDeflate: false
});
By setting perMessageDeflate: false you are specifying "Do not compress data". Since as i said this is a WebSocket Extension there are different variations as well. Try these instead if it doesn't work
x-webkit-deflate-frame
perframe-deflate
As a disclaimer this information is from the research that I have done. Im not a "socket.io specialist" so if there's anything incorrect please comment and i'll edit the post.
Because Socket.IO doesn't guarantee that there will be a WebSockets server hosted like you're seeming to expect, you should instead use their standard client package.
npm i socket.io-client
Then use the package in your code:
const ioClient = require('socket.io-client')('https://example.com/1.0/scores')
The full docs for socket.io-client are available on their GitHub repo.
Note: Honestly, though, it's just better at this point to use WebSockets instead if possible. WebSockets has become well-supported in browsers and is quite standard. Socket.IO is rarely necessary and could add some overhead.

Can't set headers after they are sent error happens with socket.io

I am writing a very simple nodejs socket.io app. Somehow this returns "can't set headers after they are sent" error. but I can't see the point where the header is set again. I've only called server.listen once and I believe socket.listen works independently so it shouldn't conflict.
I am aware that can't set headers error had been posted a multiple times. I've read them briefly and I also understand how does response.writeHead works under normal circumstances(I've experimented with some node.js apps with response.writeHead, they worked fine most cases) it seems I am missing something from here. is it due to socket.io?
const fs = require("fs");
const server = require("http").createServer();
const io = require("socket.io").listen(server);
server.listen(52273,function(){
console.log("server up");
});
server.on("request",function(request,response){
fs.readFile("mainpage.html",function(error,data){
response.writeHead(200,{"Content-Type":"text/html"});
response.end(data);
});
});
io.sockets.on("connection",function(socket){
var roomName = null;
socket.on("join", function(data){
roomName = data;
socket.join(data);
console.log("client joined" + data);
});
socket.on("message",function(data){
io.sockets.in(roomName).emit("message","test");
});
});
It probably helps a bit here to understand a little bit about how socket.io works with your web server. socket.io uses the webSocket protocol as it's base protocol. A webSocket connection starts with an HTTP request that has special header set in it to indicate the start of a webSocket connection. A properly functioning web server will see this webSocket header and turn the request over to the webSocket handler to initiate the webSocket connection.
But, this handler you have:
server.on("request",function(request,response){
fs.readFile("mainpage.html",function(error,data){
response.writeHead(200,{"Content-Type":"text/html"});
response.end(data);
});
});
Looks like it is responding to every single request that arrives and sending a response, no matter what the request was. So, I can imagine that when the webSocket request comes in you're sending two responses, one from the webSocket server code and one from your request handler above.
Probably, you need to be able to only send your mainpage.html response for particular URLs that are not your webSocket request.
To see a little more about what's going in, insert a console.log() into here:
server.on("request",function(request,response){
console.log(request.url);
fs.readFile("mainpage.html",function(error,data){
response.writeHead(200,{"Content-Type":"text/html"});
response.end(data);
});
});
And, you will probably see you are sending your mainpage.html to a webSocket request which is not what you want to do. You will likely want to add some if logic so that your request handler is avoiding the webSocket requests.
What version of Node you using?
We had the same problem when we were using 0.9.x. I downgraded Node to 0.8.4 and the problem seems to have gone away.
My best guess is something in Node has changed that Socket.io doesnt agree with.
Thanks everyone. I've solved problem by installing express to my experiment app. I've read the manual again and discovered the socket.io v2 is now requiring express app as a dependency. It seems I have been using socket.io v2 but somehow sticked to socket.io v1 style app writing. It also worked when I downgraded it to socket.io v1.
TL;DR - socket.io v2 is not compatible with apps written for socket.io v1. When a socket.io app returns "can't set headers after they are sent" errors, try use proper version of the socket.io or install correct dependencies.

Sails V0.10-rc7 Get a record from the database using REST Blueprints via Socket.IO

Sails 0.10.0-rc7
Sails Socket IO : Client not receiving response from server.
Using sails built in blueprints I am attempting to get information from my server using this functionality. (Im looking to use the default behaviour)
Client
//Client on different server (localhost:8000)
//Sails server
var socket = io.connect('http://localhost:1337');
socket.get('/event',function serverSays(err,events){
if (err)
console.log(err)
console.log(JSON.stringify(events));
});
Server
Event Model
module.exports = {
schema : true,
attributes: {
name : {
type : 'STRING',
maxLength: 50,
required: true
}
}
};
In the server terminal (logs) :
verbose: client authorized
verbose: handshake authorized 4TGNw-ywabWYG9j-AHaC
verbose: setting request GET /socket.io/1/websocket/4TGNw-ywabWYG9j-AHaC?__sails_io_sdk_version=0.10.0&__sails_io_sdk_platform=browser&__sails_io_sdk_language=javascript
verbose: set heartbeat interval for client 4TGNw-ywabWYG9j-AHaC
verbose: client authorized for
verbose: websocket writing 1::
verbose: A socket.io client (4TGNw-ywabWYG9j-AHaC) connected successfully!
BUT the callback on my client is never being called????
It seems as if the client connects with the server..
Any suggestions?
EDIT
I must stress that the client and the sails server are running on different servers. The handshake when performing io.connect(localhost:1337) talks with the server correctly based on the server logs.
Its the subsequent action socket.get("/Event") which does not result in anything. Based on the server logs, I would say that its not ever reaching the server....
I thought I would just leave a note as I have my implementation working now.
So as it turns out, I made a fairly embarrassing mistake/assumption.
using Sails js's browser SDK I was connecting to a remote server using:
io.connect("serverurl")
and then happily went about my business attempting to perform various socket functions such as socket.get..
What I did not do is after
io.connect("url")
I still had to ensure that my app had indeed connected to the server by listening on the socket for the connect event:
socket.on("connect",function())...
Once I had this little piece of the puzzle resolved all went and is going swimmingly!
I must also state that I believe the reason I was running into to this issue was because I was attempting to perform the initial connection and subsequent requests in the sails run (init) function. So my subsequent actions were more than likely executing before the app and the server had successfully established a connection.
I believe had the initial connect (io.connect) and the subsequent actions been executed in separate user flows, all would have been as the connection would have surely been established already.

Websocket server running fine but cannot connect from client (what url should I use?)

OK this is very simple to anyone who's used websocket and nodejs.
I have created a websocket server named ws_server.js and put it in C:\Program Files (x86)\nodejs where I have installed the nodejs framework. I started the server and it is running and it says it's listening on port 8080. So far so good, I have the server running.
Now I simply want to connect to it from client code so that I can do all that lovely stuff about capturing events using event listeners etc. The problem is, embarassingly, I cannot figure out what URL to use to connect to my websocket server.
function init() {
testWebSocket();
}
function testWebSocket() {
websocket = new WebSocket("ws://localhost:8080/"); // WHAT URL SHOULD BE USED HERE?
websocket.onopen = function(evt) { alert("OPEN") };
websocket.onclose = function(evt) { alert("CLOSE") };
websocket.onmessage = function(evt) { alert("MESSAGE") };
websocket.onerror = function(evt) { alert("ERROR") };
}
function doSend(message) {
// this would be called by user pressing a button somewhere
websocket.send(message);
alert("SENT");
}
window.addEventListener("load", init, false);
When I use ws://localhost:8080 the only events that trigger are CLOSE and ERROR. I cannot get the client to connect. I must be missing something very simple. Do I need to set up my nodejs folder in IIS for example and then use that as the URL?
Just to reiterate, the websocket server is running fine, I just don't know what URL to use to connect to it from the client.
EDIT: The websocket server reports the following error.
Specified protocol was not requested by the client.
I think I have got it working by doing the following.
var websocket = new WebSocket("ws://localhost:8080/","echo-protocol");
The problem being that I needed to specify a protocol. At least now I get the onopen event. ...if nothing much else
I was seeing the same error, the entire web server goes down. Adding the protocol fixes it but leaves me wondering why it was implemented this way. I mean, one bad request should not bring down your server.
You definitely have to encase it a try/catch, but the example code provided here https://www.npmjs.com/package/websocket (2019-08-07) does not. This issue can be easily avoided.
I just wanted to share a crazy issue that I had. I was able to connect to a websocket of an old version of a 3rd party app in one computer, but not to a newer version of the app in another.
Moreever, even in new computer with the new version of the app, The app was able to connect to the websocket, but no matter what I did, when I tried to connect with my own code, I kept getting the error message that the websocket connection failed
Long story short, They changed an apache configuration that allowed connecting to the websocket via a proxy.
In the old version, apache config was:
ProxyPass /socket/ ws://localhost:33015/ retry=10
ProxyPass /socket ws://localhost:33015/ retry=10
In the new version, apache config was changed to:
ProxyPass /socket/ ws://localhost:33015/ retry=10
By bad luck, I was trying to connect to ws://localhost/socket and not to ws://localhost/socket/. As a result, proxy was not found, and connection returned an error.
Moral of the story: Make sure that you are trying to connect to a websocket url that exists.
For me, the solution was to change the URL from ws:// to wss://. This is because the server I was connecting to had updated its security, and now only accepted wss.

Resources