Node.js - 当由 Nginx 反向代理提供服务时,Websocket 拒绝打开连接

Node.js - Websocket refuses to open a connection when served by Nginx reverse proxy

目前我正在尝试设置一个简单的聊天应用程序。我正在使用 total.js 框架。我的 Node.js 程序在 [http]://127.0.0.1:8000/ 上运行。只要请求 URI 与加载页面的 HTML 代码中指定的确切 URI 匹配,此方法就有效。例如,转到 [http]://localhost:8000/ 将加载 html 页面,但所有功能将无法正常工作,而 [http]://127.0.0.1:8000 将按预期工作。

我在尝试将 Nginx 设置为反向代理时发现了这一点,因此我可以通过转到 [http]://127.0.0.1:80/ 来访问它。 html 页面会在此处加载,但聊天功能无法使用。

此聊天应用是 total.js 示例的一部分。

这是创建 websocket 的 index.html 文件:

<div class="mb5">
  <button name="open">CONNECT</button>
</div>
<div>
  <button name="close" disabled="disabled">DISCONNECT</button>
</div>
<br />
<div>
  <input type="text" name="message" maxlength="200" style="width:500px" />
  <button name="send" disabled="disabled">SEND</div>
</div>
<br />
<div>
  <textarea id="output" style="width:620px;height:300px" readonly="readonly"></textarea>
</div>
<br />
<div>
  <input type="text" value="" name="username" maxlength="20" style="width:200px" />
  <button name="rename" disabled="disabled">RENAME</div>
</div>

<script type="text/javascript">
  var socket = null;

  $(document).ready(function() {

    $('button').bind('click', function() {

      if (this.name === 'rename') {
        var value = $('input[name="username"]').val();

        if (value.length > 0)
          socket.send(encodeURIComponent(JSON.stringify({
            username: value
          })));

        return;
      }

      if (this.name === 'send') {
        console.log('send');
        send();
        return;
      }

      if (this.name === 'open') {
        connect();
        return;
      }

      console.log('disconnect');
      disconnect();
    });
  });

  function connect() {

    if (socket !== null)
      return;

    $('button[name="open"]').attr('disabled', true);
    $('button[name="close"],button[name="send"],button[name="rename"]').attr('disabled', false);
    socket = new WebSocket('ws://127.0.0.1:8000/');

    socket.onopen = function() {
      console.log('open');
    };

    socket.onmessage = function(e) {
      var el = $('#output');
      var m = JSON.parse(decodeURIComponent(e.data)).message;
      el.val(m + '\n' + el.val());
    };

    socket.onclose = function(e) {
      // e.reason ==> total.js client.close('reason message');
      $('button[name="open"]').attr('disabled', false);
    };
  }

  function send() {
    var el = $('input[name="message"]');
    var msg = el.val();

    if (socket !== null && msg.length > 0)
      socket.send(encodeURIComponent(JSON.stringify({
        message: msg
      })));

    el.val('');
  }

  function disconnect() {

    if (socket === null)
      return;

    $('button[name="close"],button[name="send"],button[name="rename"]').attr('disabled', true);
    $('button[name="open"]').attr('disabled', false);

    socket.close();
    socket = null;
  }
</script>

这听起来像是同源保护。

除非您的 socket.io 服务器支持 COR 并允许从跨源域访问,否则当网页从 localhost:8000 加载时,浏览器将不允许连接到 127.0.0.1:8000,反之亦然反之亦然。您要连接的域必须与网页的域匹配,除非您的服务器支持 CORS 并明确允许跨域连接。

而且,即使 localhost:8000127.0.0.1:8000 实际上可能是同一台服务器,浏览器也不允许。

解决您的特定问题的通常方法是仅使用 Javascript 中的 window.location 来获取当前页面 URL 中的主机名的形式并构建您的 URL 使用该主机名,因此您确保在页面 URL.

中使用相同的主机名

例如:

socket = new WebSocket('ws://' + window.location.host + '/');

我想这不言而喻,您的 nginx 代理也必须专门配置为支持 webSocket 连接。