WebSocket连接失败:WebSocket握手期间出错:意外响应代码:400

ash*_*540 41 javascript sockets node.js socket.io angularjs

我正在尝试将Socket.io与Angular集成,并且我很难从客户端到服务器建立连接.我查看了其他相关问题,但我的问题是在本地发生的,所以中间没有Web服务器.

这就是我的服务器代码:

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');
  });
});
Run Code Online (Sandbox Code Playgroud)

我按以下顺序使用Grunt加载客户端JavaScript文件:

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,
        ...
    ]
}
Run Code Online (Sandbox Code Playgroud)

然后在我的控制器中:

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!' });
    });

    ...
}
Run Code Online (Sandbox Code Playgroud)

在实际使用btford/angular-socket-io库之前,我想确保我可以正确获得连接,但是在控制台中出现以下错误:

socket io连接错误消息

有趣的是,如果我重新启动Node.js服务器进程,它确实设法发送消息,但使用轮询而不是websockets.

重启后

投票信息

我在socket.connect调用中尝试了各种不同的选项,但没有任何效果.

任何帮助,将不胜感激.


更新(2016年12月30日):

我刚刚意识到websockets部分工作.我在Chrome开发者控制台中看到了101切换协议请求.但是我看到的唯一框架是engine.io协议包(ping,pong).但是我的应用程序套接字消息仍然由于某种原因而退回到轮询...

engine.io数据包

ash*_*540 42

问题解决了!我只是弄清楚如何解决这个问题,但我仍然想知道这是否是正常行为.

似乎即使Websocket连接正确建立(由101交换协议请求指示),它仍然默认为长轮询.修复就像将此选项添加到Socket.io连接函数一样简单:

{transports: ['websocket']}
Run Code Online (Sandbox Code Playgroud)

所以代码最终看起来像这样:

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');
  });
});
Run Code Online (Sandbox Code Playgroud)

在客户端:

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);
});
Run Code Online (Sandbox Code Playgroud)

消息现在显示为框架:

工作websockets

这个Github问题指出了我正确的方向.感谢所有帮帮忙的人!

  • 请注意,添加此选项可以有效地消除长轮询回退,这也是“socket.io”如此强大的原因之一。请参阅[https://socket.io/docs/using-multiple-nodes/](https://socket.io/docs/using-multiple-nodes/)。 (4认同)

Kat*_*rax 25

当前接受的解决方案具有误导性。

根据官方文档,添加该transports: [ 'websocket' ]选项可以有效去除无法建立 websocket 连接时回退到长轮询的能力。这个选项首先使socket.io如此强大,因为它可以适应许多场景。

在那种希望完全依赖 websockets 的特殊情况下,WebSocket API建议直接使用。

对于其他情况(据说是大多数用户),这很可能是反向代理/服务器配置问题。

官方文档根据您的环境建议以下内容:

NginX 配置

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

Apache HTTPD 配置

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
Run Code Online (Sandbox Code Playgroud)

HAProxy 配置

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
Run Code Online (Sandbox Code Playgroud)

另外值得一读在HAProxy的升级连接。

更多详情请参考上面的官​​方文档链接。

编辑:

清漆(来源在这里

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;
    }
}
Run Code Online (Sandbox Code Playgroud)


Abh*_*eet 22

这适用于Nginx,Node服务器和Angular 4

编辑您的nginx Web服务器配置文件:

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";
}
Run Code Online (Sandbox Code Playgroud)

  • 对于那些想知道为什么升级标头的人?https://www.nginx.com/blog/websocket-nginx/ (3认同)
  • 即使OP询问本地问题,其中没有涉及nginx反向代理,这个答案是正确的nginx设置当它在游戏中 (2认同)

小智 8

从您通过Socket.IO发送的消息判断,您socket.emit('greet', { hello: 'Hey, Mr.Client!' });似乎正在使用hackathon-starter样板.如果是这样,问题可能是该express-status-monitor模块正在创建自己的socket.io实例,如下所示:https://github.com/RafalWilinski/express-status-monitor#using-module-with-socketio-in-project

你可以:

  1. 删除该模块
  2. 传入socket.io实例和端口,就像websocket创建expressStatusMonitor实例一样,如下所示:

    const server = require('http').Server(app);
    const io = require('socket.io')(server);
    ...
    app.use(expressStatusMonitor({ websocket: io, port: app.get('port') })); 
    
    Run Code Online (Sandbox Code Playgroud)


Tej*_*ank 6

我也遇到过同样的问题,我改进了 apache2 虚拟主机输入并获得了成功。

注意:在服务器上我已成功安装并在 9001 端口上工作,没有任何问题。本指南仅适用于 apache2,与 nginx 无关,此答案适用于 apache2+etherpad 爱好者。

<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>
Run Code Online (Sandbox Code Playgroud)

高级提示:请在 a2enmod 的帮助下启用 apache2 的所有 mod

重启apache2即可生效。但显然 a2ensite 需要启用站点。

注意:目前我的后端系统没有 https,所以我保留 http,如果您的后端已经启用了 HTTPS,则使用 https:// 作为后端。如果您的后端已经在 HTTPS 中,我担心 HTTPS 上的开销负载,我的建议是当私有服务器处于整洁的安全状态时,后端应该在 http 中。决定权在你。


小智 6

有同样的问题,我的应用程序在 nginx 后面。对我的 Nginx 配置进行这些更改消除了错误。

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;
}
Run Code Online (Sandbox Code Playgroud)


小智 5

我通过将传输方式从“ websocket”更改为“轮询”解决了这一问题

   var socket = io.connect('xxx.xxx.xxx.xxx:8000', {
      transports: ['polling']
   });
Run Code Online (Sandbox Code Playgroud)

  • 这更像是一个创可贴,而不是真正的解决办法。Socket.io 最初使用轮询进行连接,然后“升级”到更可靠的传输(即 websockets)。这个答案迫使 socketio 仅使用轮询(而不是更有效的传输),这将修复错误,但如果您希望避免在大型应用程序中进行无休止的轮询,则这不是一个长期解决方案。我相信如果升级失败,socketio 也知道无法轮询,因此您或多或少可以避免控制台错误。 (5认同)