90

I am trying to integrate Socket.io with Angular and I'm having difficulties making a connection from the client-side to the server. I've looked through other related questions but my issue is happening locally, so there's no web server in the middle.

This is what my server code looks like:

const app = express();
const server = http.createServer(app);
const io = require('socket.io').listen(server);

io.on('connection', function(socket) {
  socket.emit('greet', { hello: 'Hey, Mr.Client!' });

  socket.on('respond', function(data) {
    console.log(data);
  });
  socket.on('disconnect', function() {
    console.log('Socket disconnected');
  });
});

I'm loading the client side JavaScript files using Grunt in the following order:

dist: {
    src: [
        public/bower_components/angular/angular.min.js,
        ...
        public/bower_components/socket.io-client/dist/socket.io.min.js,
        public/bower_components/angular-socket-io/socket.min.js,
        ...
    ]
}

Then in my controller:

function MyController($scope) {

    let socket = io.connect(window.location.href);
    socket.connect('http://localhost:3000');
    socket.on('greet', function(data) {
      console.log(data);
      socket.emit('respond', { message: 'Hello to you too, Mr.Server!' });
    });

    ...
}

Before actually using the btford/angular-socket-io library, I want to make sure that I can get a connection correctly, but I get the following error in the console:

socket io connection error message

The interesting thing is that if I restart the Node.js server process, it does manage to send the message but using polling instead of websockets.

after restart

polling message

I tried all sorts of different options in the socket.connect call, but nothing worked.

Any help would be appreciated.


UPDATE (30/12/2016):

I just realized that websockets is working partially. I see a 101 Switching Protocols request in the Chrome developer console. However the only frames I see there are the engine.io protocol packets (ping, pong). However my application socket messages still fall back to polling for some reason...

engine.io packets

ashe540
  • 2,041
  • 2
  • 17
  • 17

21 Answers21

68

Problem solved! I just figured out how to solve the issue, but I would still like to know if this is normal behavior or not.

It seems that even though the Websocket connection establishes correctly (indicated by the 101 Switching Protocols request), it still defaults to long-polling. The fix was as simple as adding this option to the Socket.io connection function:

{transports: ['websocket']}

So the code finally looks like this:

const app = express();
const server = http.createServer(app);
var io = require('socket.io')(server);

io.on('connection', function(socket) {
  console.log('connected socket!');

  socket.on('greet', function(data) {
    console.log(data);
    socket.emit('respond', { hello: 'Hey, Mr.Client!' });
  });
  socket.on('disconnect', function() {
    console.log('Socket disconnected');
  });
});

and on the client:

var socket = io('ws://localhost:3000', {transports: ['websocket']});
socket.on('connect', function () {
  console.log('connected!');
  socket.emit('greet', { message: 'Hello Mr.Server!' });
});

socket.on('respond', function (data) {
  console.log(data);
});

And the messages now appear as frames:

working websockets

This Github issue pointed me in the right direction. Thanks to everyone who helped out!

ashe540
  • 2,041
  • 2
  • 17
  • 17
  • How did you get chrome to show the frames like that? When I click on a sockIO request it does not show a tab for Frames – kiwicomb123 May 17 '18 at 23:25
  • When the browser initiates the connection it sends an upgrade request to which the server responds with a 101 Switching protocols. You have to make sure that you look for this request in the Network tab, as it will be the one that shows the frames sent between the two entities. – ashe540 May 19 '18 at 00:27
  • Yes I've navigated there. There are several cases where the "frames" tab is not there in the Network tab. – kiwicomb123 May 19 '18 at 00:29
  • 1
    Thank you, this worked for me, had same issue as OP – Ryan Rebo Feb 07 '20 at 01:49
  • Thanks a lot, works perfect. I'd switched from Heroku to glitch because of some port binding issue on Heroku, then I got this error on glitch. – Punter Bad Feb 07 '20 at 19:49
  • 4
    Note that adding this option effectively removes long-polling fallback, which is one of the reasons `socket.io` is so robust in the first place. See [https://socket.io/docs/using-multiple-nodes/](https://socket.io/docs/using-multiple-nodes/). – Kathandrax Mar 28 '20 at 13:52
64

This worked for me with Nginx, Node server and Angular 4

Edit your nginx web server config file as:

server {
listen 80;
server_name 52.xx.xxx.xx;

location / {
    proxy_set_header   X-Forwarded-For $remote_addr;
    proxy_set_header   Host $http_host;
    proxy_pass         "http://127.0.0.1:4200";
    proxy_http_version 1.1;
    proxy_set_header   Upgrade $http_upgrade;
    proxy_set_header   Connection "upgrade";
}
Abhijeet_IXR
  • 807
  • 6
  • 15
  • 8
    even though the OP is asking about a local issue where there is no nginx reverse proxy involved this answer is the correct nginx setting when its in play – Scott Stensland Feb 06 '18 at 15:13
  • 1
    Adding the upgrade headers for my configuration was what I needed. `proxy_http_version 1.1;` caused the Nginx service to fail to start. – JDavis Mar 06 '18 at 23:31
  • For those who would like more info regarding nginx header management: https://www.digitalocean.com/community/tutorials/understanding-nginx-http-proxying-load-balancing-buffering-and-caching – JoshuaTree Mar 14 '18 at 05:13
  • 3
    And for those wanting to know why the upgrade header? https://www.nginx.com/blog/websocket-nginx/ – JoshuaTree Mar 14 '18 at 05:15
  • the last two lines for upgrade were missing from mine. – Cybersupernova Sep 17 '19 at 18:25
  • Or you can just follow a wiki to set things up: https://github.com/foundry-vtt-community/wiki/wiki/Ubuntu-VM – imposeren May 17 '20 at 00:51
  • This was my problem, server was working smoothly on local network, but when I made it a public server with nginx random connection fails happened. This fixed my issue. – Macumbaomuerte Feb 26 '23 at 15:49
35

The currently accepted solution is misleading.

According to the official documentation, adding the transports: [ 'websocket' ] option effectively removes the ability to fallback to long-polling when the websocket connection cannot be established. This option is what makes socket.io so robust in the first place because it can adapt to many scenarios.

In that particular case where one wishes to solely rely on websockets, directly using the WebSocket API is recommended.

For other cases (supposedly most users), this is most likely a reverse proxy/server configuration problem.

The official documentation suggests the following depending on your environment:

NginX configuration

http {
  server {
    listen 3000;
    server_name io.yourhost.com;

    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;

      proxy_pass http://nodes;

      # enable WebSockets
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
    }
  }

  upstream nodes {
    # enable sticky session based on IP
    ip_hash;

    server app01:3000;
    server app02:3000;
    server app03:3000;
  }
}

Apache HTTPD configuration

Header add Set-Cookie "SERVERID=sticky.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED

<Proxy "balancer://nodes_polling">
    BalancerMember "http://app01:3000" route=app01
    BalancerMember "http://app02:3000" route=app02
    BalancerMember "http://app03:3000" route=app03
    ProxySet stickysession=SERVERID
</Proxy>

<Proxy "balancer://nodes_ws">
    BalancerMember "ws://app01:3000" route=app01
    BalancerMember "ws://app02:3000" route=app02
    BalancerMember "ws://app03:3000" route=app03
    ProxySet stickysession=SERVERID
</Proxy>

RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) balancer://nodes_ws/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) balancer://nodes_polling/$1 [P,L]

ProxyTimeout 3

HAProxy configuration

listen chat
  bind *:80
  default_backend nodes

backend nodes
  option httpchk HEAD /health
  http-check expect status 200
  cookie io prefix indirect nocache # using the `io` cookie set upon handshake
  server app01 app01:3000 check cookie app01
  server app02 app02:3000 check cookie app02
  server app03 app03:3000 check cookie app03

Also worth reading this on upgrading connections in HAProxy.

For more details please refer to the official documentation link above.

EDIT:

Varnish (source here)

sub vcl_recv {
    if (req.http.upgrade ~ "(?i)websocket") {
        return (pipe);
    }
}

sub vcl_pipe {
    if (req.http.upgrade) {
        set bereq.http.upgrade = req.http.upgrade;
        set bereq.http.connection = req.http.connection;
    }
}
Kathandrax
  • 914
  • 14
  • 26
  • My websocket connection upgrade fails. Don't know why. I have this config for nginx `location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwared-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_pass http://127.0.0.1:3000; proxy_redirect off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; client_max_body_size 100M; }` – invider Jul 02 '20 at 19:33
  • Sorry for the late response @invider @Norbert. Have you fixed your problem? I see there's a typo in `proxy_set_header X-Forwared-For $proxy_add_x_forwarded_for;` - it should be `X-Forwarded-For`. Also have you double-checked whether your proxy_pass target is reachable (and specifically via port 3000)? – Kathandrax Jun 18 '21 at 14:48
  • Don't remember actually – invider Jun 19 '21 at 08:53
12

I solved this by changing transports from 'websocket' to 'polling'

   var socket = io.connect('xxx.xxx.xxx.xxx:8000', {
      transports: ['polling']
   });
  • 6
    This is more of a band-aid than a real fix. Socket.io uses a poll to connect initially then "upgrades" to more reliable transports (i.e. websockets). This answer forces socketio to only use polling (and not more efficient transports) which will fix the error but isn't a long-term solution if you prefer to avoid endless polling in larger apps. I believe socketio knows to fail to polling if the upgrade fails as well, so you're more or less saving yourself the console error. – sofly Oct 28 '19 at 20:59
9

Judging from the messages you send via Socket.IO socket.emit('greet', { hello: 'Hey, Mr.Client!' });, it seems that you are using the hackathon-starter boilerplate. If so, the issue might be that express-status-monitor module is creating its own socket.io instance, as per: https://github.com/RafalWilinski/express-status-monitor#using-module-with-socketio-in-project

You can either:

  1. Remove that module
  2. Pass in your socket.io instance and port as websocket when you create the expressStatusMonitor instance like below:

    const server = require('http').Server(app);
    const io = require('socket.io')(server);
    ...
    app.use(expressStatusMonitor({ websocket: io, port: app.get('port') })); 
    
halfer
  • 19,824
  • 17
  • 99
  • 186
Kuriakin Zeng
  • 121
  • 2
  • 3
7

Had the same issue, my app is behind nginx. Making these changes to my Nginx config removed the error.

location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
Hitesh Subnani
  • 589
  • 8
  • 11
4

I had faced same issues, I refined apache2 virtual host entery and got success.

Note: on server I had succesful installed and working on 9001 port without any issue. This guide line for apache2 only no relavence with nginx, this answer for apache2+etherpad lovers.

<VirtualHost *:80>
  ServerName pad.tejastank.com
  ServerAlias pad.tejastank.com
  ServerAdmin snippetbucket@gmail.com

  LoadModule  proxy_module         /usr/lib/apache2/modules/mod_proxy.so
  LoadModule  proxy_http_module    /usr/lib/apache2/modules/mod_proxy_http.so
  LoadModule  headers_module       /usr/lib/apache2/modules/mod_headers.so
  LoadModule  deflate_module       /usr/lib/apache2/modules/mod_deflate.so

  ProxyVia On
  ProxyRequests Off
  ProxyPreserveHost on

    <Location />
        ProxyPass http://localhost:9001/ retry=0 timeout=30
        ProxyPassReverse http://localhost:9001/
    </Location>
    <Location /socket.io>
        # This is needed to handle the websocket transport through the proxy, since
        # etherpad does not use a specific sub-folder, such as /ws/ to handle this kind of traffic.
        # Taken from https://github.com/ether/etherpad-lite/issues/2318#issuecomment-63548542
        # Thanks to beaugunderson for the semantics
        RewriteEngine On
        RewriteCond %{QUERY_STRING} transport=websocket    [NC]
        RewriteRule /(.*) ws://localhost:9001/socket.io/$1 [P,L]
        ProxyPass http://localhost:9001/socket.io retry=0 timeout=30
        ProxyPassReverse http://localhost:9001/socket.io
    </Location>


  <Proxy *>
    Options FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    allow from all
  </Proxy>
</VirtualHost>

Advance tips: Please with help of a2enmod enable all mod of apache2

Restart apache2 than will get effect. But obvious a2ensite to enable site required.

Tejas Tank
  • 1,100
  • 2
  • 16
  • 28
  • 1
    I changed all of your `http://` to `https://` and `ws://` to `wss://` -- works fine. thank you so much for sharing this. Using localhost:### in this context is very interesting, i have never seen that before. – edwardsmarkf Jan 15 '19 at 23:12
  • 1
    Yea this worked for me. Thanks. The rewrite rules was the problem for me maybe – Devender Gupta Oct 01 '20 at 12:20
  • 1
    That worked for me - the `` I did not wrap my settings in that... but yeah helped a lot. Thanks Dude! – Robert Nov 12 '20 at 23:41
  • Reason: Error during SSL Handshake with remote server – Nevermore Mar 08 '21 at 11:16
1

In your controller, you are using an http scheme, but I think you should be using a ws scheme, as you are using websockets. Try to use ws://localhost:3000 in your connect function.

TeaMonkie
  • 169
  • 9
1

I think you should define your origins for client side as bellow:

//server.js
const socket = require('socket.io');
const app = require('express')();
const server = app.listen('port');

const io = socket().attach(server);
io.origins("your_domain:port www.your_domain:port your_IP:port your_domain:*")

io.on('connection', (socket) => {
  console.log('connected a new client');
});

//client.js
var socket = io('ws://:port');
Heartbit
  • 1,698
  • 2
  • 14
  • 26
  • extend your origins by adding other patterns like `http://your_IP:port` or `http://your_domain:port` – Heartbit Dec 29 '16 at 16:49
  • Nope, unfortunately that didn't seem to do the trick. Any other ideas why this could be rejecting the use of websockets with a 400 Bad Request? What boggles me is that this is all in localhost, so there shouldn't be any problem. – ashe540 Dec 29 '16 at 22:15
1

In my similar scenario, express-status-monitor was installed to remove and remediate this error

The following are the steps / settings / configuration to install express-status-monitor:

npm i express-status-monitor --save
const expressStatusMonitor = require('express-status-monitor');
app.use(expressStatusMonitor({
    websocket: io,
    port: app.get('port')
}));
djmonki
  • 3,020
  • 7
  • 18
Talha Noyon
  • 824
  • 7
  • 13
1

The problem for me was not got the port from process.env.PORT it is very important because Heroku and other services properly do a random port numbers to use.

So that is the code that work for me eventuly :

var app = require('express')();
var http = require('http').createServer(app);
const serverPort = process.env.PORT ; //<----- important 

const io = require('socket.io')(http,{
  cors: {
    origin: '*',
    methods: 'GET,PUT,POST,DELETE,OPTIONS'.split(','),
    credentials: true
  }
});

http.listen(serverPort,()=>{
  console.log(`server listening on port ${serverPort}`)
})
Đức
  • 28
  • 1
  • 6
pery mimon
  • 7,713
  • 6
  • 52
  • 57
1

I had the same error witk socket.io on node.js but the reason was quite silly. There wasn't all socket.io's dependencies installed correctly, namely package base64id was missed

pav
  • 61
  • 6
1

Including transports: ['websocket'] is not the best approach as it removes Sockt.io functionality from suiting any scenario.

In my case using Nodejs + Nginx + Vuejs/Vite(Front) I managed to solve it by configuring the reverse proxy in the site configuration in Nginx.

http {
  server {
    listen 80;
    root /var/www/html;

    // <---- start ---->
    location /socket.io/ { // <--- Set your custom path
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;

      proxy_pass http://localhost:3000; // <--- Set your Port

      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
    }
    // <---- End ---->

  }
}

You can find references on this issue on the Socket.io website or on Nginx

1

If you tried every option given above then there is no problem with your code, try to stop the ad blocker of your browser. It worked for me

My Output after disabling blocker Output

My server Server

0

You're using port 3000 on the client-side. I'd hazard a guess that's the Angular port and not the server port? It should be connecting to the server port.

martinedwards
  • 5,577
  • 1
  • 33
  • 35
0

After using following load balancer setting my problem solved for wss but for ws problem still exists for specific one ISP.

calssic-load-balancer

0

I solved this by removing io.listen(server);. I started running into this error when I started integrating passport.socketio and using passport middleware.

David
  • 944
  • 1
  • 13
  • 20
0

if you are using httpd/apache, you can add a file something like ws.conf and add this code to it. Also, this solution can proxy something like this "http://localhost:6001/socket.io" to just this "http://localhost/socket.io"

<VirtualHost *:80>
    RewriteEngine on

    #redirect WebSocket
    RewriteCond %{REQUEST_URI}  ^/socket.io            [NC]
    RewriteCond %{QUERY_STRING} transport=websocket    [NC]
    RewriteRule /(.*)           ws://localhost:6001/$1 [P,L]

    ProxyPass        /socket.io http://localhost:6001/socket.io
    ProxyPassReverse /socket.io http://localhost:6001/socket.io
</VirtualHost>
0

Using Apollo Server 2.

Per https://github.com/apollographql/apollo-client/issues/4778#issuecomment-509638071, this solved my problem:

try 'ws://localhost:4000/graphql'

...since incoming and outgoing requests now use the same address.

Ben
  • 54,723
  • 49
  • 178
  • 224
0

my problem was with server side

const app = require("express")();
const http = require("http").Server(app);
const io = require("socket.io")(http);

listen with

http.listen(PORT,()=> console.log('listening'))

it was giving me error when i did

app.listen(......)
Om Fuke
  • 482
  • 1
  • 7
  • 14
0

If you're using flask, gunicorn and nginx as your reverse proxy, configuring adding these options under the endpoint where socket.io is being used is not enough.

This is what I mean, consider that you have an endpoint '/chat' configured as follows:

location /chat/ {
    proxy_pass http://backend;
    # Enable websockets
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

After I did this, I still kept on encountering the error: WebSocket connection to 'ws://x.x.x.x/socket.io/?EIO=4&transport=websocket&sid=LNcNgeauE7z6dYBiAAAK' failed: Error during WebSocket handshake: Unexpected response code: 200.

To solve this, I added another endpoint in my conf i.e.,

location /socket.io/ { 
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header Host $host;

     proxy_pass http://localhost:3000; # Remember to change to your configured port

     proxy_http_version 1.1;
     proxy_set_header Upgrade $http_upgrade;
     proxy_set_header Connection "upgrade";
}

After adding and configuring this endpoint, I did not get a single ws error.

Further references: http://nginx.org/en/docs/http/websocket.html

LW001
  • 2,452
  • 6
  • 27
  • 36
tykoon
  • 11
  • 4