繁体   English   中英

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

[英]WebSocket connection failed: Error during WebSocket handshake: Unexpected response code: 400

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

这是我的服务器代码的样子:

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

我按以下顺序使用 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,
        ...
    ]
}

然后在我的控制器中:

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

    ...
}

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

socket io 连接错误信息

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

重启后

轮询消息

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

任何帮助,将不胜感激。


更新(2016 年 12 月 30 日):

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

engine.io 数据包

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

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

{transports: ['websocket']}

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

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

在客户端:

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

消息现在显示为帧:

工作网络套接字

这个Github 问题为我指明了正确的方向。 感谢所有帮助过的人!

这对我来说适用于 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";
}

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

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

在希望仅依赖 websocket 的特定情况下,建议直接使用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;
  }
}

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

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

在 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;
    }
}

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

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

从你通过 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- 项目内

您可以:

  1. 删除该模块
  2. 当您创建expressStatusMonitor实例时,将您的 socket.io 实例和端口作为websocket传递,如下所示:

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

有同样的问题,我的应用程序落后于 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;
}

我也遇到过同样的问题,我改进了 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>

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

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

我认为您应该将客户端的origins定义如下:

 //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');

就我而言,我刚刚安装了 express-status-monitor 以消除此错误

这是设置

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

对我来说,问题不是从process.env.PORT获得端口,这非常重要,因为 Heroku 和其他服务正确地使用了随机端口号。

这就是最终对我有用的代码:

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}`)
})

我在 node.js 上遇到了同样的错误 witk socket.io,但原因很愚蠢。 没有正确安装socket.io的所有依赖,即缺少包base64id

在您的控制器中,您使用的是http方案,但我认为您应该使用ws方案,因为您正在使用 websockets。 尝试在您的连接函数中使用ws://localhost:3000

您在客户端使用端口 3000。 我会冒险猜测那是 Angular 端口而不是服务器端口? 它应该连接到服务器端口。

在使用以下负载平衡器设置后,我的问题解决了 wss 但对于特定的一个 ISP,ws 问题仍然存在。

calsic-负载平衡器

我通过删除io.listen(server);解决了这个问题。 . 当我开始集成passport.socketio 并使用passport 中间件时,我开始遇到这个错误。

如果您使用的是 httpd/apache,您可以添加类似 ws.conf 的文件并将此代码添加到其中。 此外,此解决方案可以将类似“http://localhost:6001/socket.io”的内容代理到“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>

使用阿波罗服务器 2。

根据https://github.com/apollographql/apollo-client/issues/4778#issuecomment-509638071 ,这解决了我的问题:

试试 'ws://localhost:4000/graphql'

...因为传入和传出请求现在使用相同的地址。

我的问题出在服务器端

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

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

当我这样做时它给了我错误

app.listen(......)

包括transports: ['websocket']不是最好的方法,因为它删除了适合任何场景的 Sockt.io 功能。

在我使用 Nodejs + Nginx + Vuejs/Vite(Front) 的情况下,我设法通过在 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 ---->

  }
}

您可以在Socket.io网站或Nginx上找到有关此问题的参考资料

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM