Socket.io 未通过 Cloudflare/Nginx 断开连接

Socket.io not disconnecting through Cloudflare/Nginx

我有一个使用 Express.js 提供服务并使用 Socket.io 连接到后端的小型网络应用程序。为了做到 public,我使用 Nginx 作为反向代理,然后在最前面使用 Cloudflare。该应用程序依赖于 disconnect 在关闭或重新加载时触发的事件来跟踪在线用户等。通过 Nginx 和 Cloudflare 时,断开连接事件永远不会在后端触发。在本地开发时会。

这是我的 Nginx 配置文件:

server {
    listen 80;
    server_name colab.gq;
    server_name www.colab.gq;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header X-Forwarded-For $remote_addr;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/colab.gq/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/colab.gq/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

这是我的服务器端代码片段:

io.on('connection', (socket) => {
  // Stuff here

  socket.on('disconnect', () => {
    console.log('disconnected!')
    // Stuff that relies on the disconnect event
  })
})

当用户关闭选项卡或重新加载页面时,应该触发断开连接事件,但在通过 Nginx 和 Cloudflare 传递连接时它永远不会触发。在此先感谢您的帮助!

更新:似乎在 reloading/closing 断开连接事件终于注册后几秒钟。

你必须像这样在 nginx 配置中添加升级 headers 到你的套接字 io 路径,

    location ~* \.io {
      .. your configuration

      proxy_pass http://localhost:3000;
      proxy_redirect off;

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

由于您要求扩展答案,您可能已经知道的第一件事是 socket.io 是一种在幕后使用 Websockets 协议的协议(因此两者相同)。作为标准,Websockets 协议和 HTTP 协议都在相同的端口 80 或 443 上侦听。默认协议是 HTTP,如果用户想使用 websockets 协议 he/she 必须发送从 HTTP 到 WS 协议的升级请求并且有一些关键的身份验证和步骤。

这就是为什么您需要将它们放入您的 nginx 配置中。

如果您需要有关协议升级机制的更多信息,请参阅this

尽管在我看来,这并不是 this question, I feel obligated to give credits for @Paulo 提供完美答案的完全重复,即使不被接受。

可能发生的是两件事。

1st:Socket.io 超时时间很长(因此有几秒钟)并且在声明已断开连接之前希望重新连接。检查 reconnect_attempt 或重新连接事件(请注意,我已经有一段时间没有使用 socket.io,所以我在这方面的时间可能是错误的)

第二:您没有告诉 socket.io 它在关闭浏览器后断开连接 window。

我建议向您的 javascript 添加一个事件侦听器,它会告诉您的服务器它在 window 关闭时断开连接。

    window.addEventListener('beforeunload', () => {
       socket.disconnect();
    }