6

有没有办法让一个单独的 websocket 服务器socket.io在不同的路径上一起工作?

let http = require('http');
let express = require('express');
let socketio = require('socket.io');
let websocket = require('ws');

let httpServer = http.createServer();

let expressApp = express();
httpServer.on('request', expressApp);

let socketioServer = socketio(httpServer, { path: '/aaaaa/socket.io/' });
socketioServer.of('/').on('connect', () => {});

let websocketServer = new websocket.Server({ server: httpServer, path: '/aaaaa/graphql' });

httpServer.listen(2233, () => console.log('started'));

我看到的行为是,当创建单独的 websocket 服务器时,socket.io仍然可以正常运行,但不会升级到 websocket 的连接并失败并出现错误(来自 chrome):

WebSocket connection to 'ws://localhost:2233/aaaaa/socket.io/?EIO=3&transport=websocket&sid=fx4pOT0cegz65JMCAAAB' failed: Invalid frame header

需要明确的是,如果省略了 websocket 服务器行,则socket.io可以正常工作。

apollo-server-express我的具体用例是启用订阅时由包创建一个 websocket 服务器。有没有办法以socket.io更友好的方式进行配置?或者,我相信我可以为 apollo 提供一个 websocket 服务器来使用而不是创建一个......我将如何创建它?

用于复制的软件包版本:

node       8.11.1
express    4.16.4
socket.io  2.1.1
ws         6.1.0
4

2 回答 2

2

如果这对其他人有帮助,这是我的派生解决方案:

let [socketioUpgradeListener, apolloUpgradeListener] = httpServer.listeners('upgrade').slice(0);
httpServer.removeAllListeners('upgrade');
httpServer.on('upgrade', (req, socket, head) => {
  const pathname = url.parse(req.url).pathname;
  if (pathname == '/aaaaa/socket.io/')
    socketioUpgradeListener(req, socket, head);
  else if (pathname == '/aaaaa/graphql')
    apolloUpgradeListener(req, socket, head);
  else
    socket.destroy();
});

有点烦人,因为这两个库已经完全初始化了他们的 websocket 服务器,有大量的事件监听器,然后我才能弄乱它们。但是,我可以挑选出'upgrade'听众并手动委派他们。当然这并不完美,因为它对初始化顺序和新侦听器很敏感,但对于我的用例来说已经足够了。

如果此解决方案有任何明显的缺陷或 websocket 服务器委托有任何其他细微差别,请告诉我。

于 2018-11-07T00:16:29.263 回答
2

NestJs在使用graphql- 和socket.io-modules并行时遇到了同样的问题。作为 Trevor 解决方案的替代方案,您可以绑定socket.io到另一个端口并使用像 nginx 这样的反向代理来解析路径。

应用程序网关.ts

@WebSocketGateway(3001)
export class AppGateway implements OnGatewayConnection {   

  handleConnection(
    client: any,
    payload: any
  ) {
    client.emit('Hi from port 3001');
  }
}

nginx.conf

server {
        listen 80;
        listen [::]:80;
        server_name localhost;

        location /graphql {
                proxy_pass http://127.0.0.1:3000;
        }

        location /socket.io {
                proxy_pass http://127.0.0.1:3001/socket.io/;

                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection 'upgrade';
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $host;

                proxy_http_version 1.1;
        }
}

当然,您可以跳过最后一部分并通过ws://localhost:3001/socket.io客户端直接连接到您的套接字。

于 2019-12-23T14:09:25.883 回答