独立的websocket服务器中出现Socket.io“Invalid frame header”错误

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?或者,我相信我可以提供一个websocket服务器供apollo使用,而不是创建一个...我该如何创建呢?
用于复制的软件包版本:
node       8.11.1
express    4.16.4
socket.io  2.1.1
ws         6.1.0

这不是预期的目标。我想让一个socket.io客户端连接到socket.io服务器,同时让一个websocket客户端连接到websocket服务器。我不需要跨通信。 - Trevor Wilson
啊,我理解错了。所以问题是 ws 不会将路径 /aaaaa/socket.io 上的套接字连接委托给 socket.io - Patrick Roberts
2
看起来要让 ws 正常工作,你需要手动实现 upgrade 事件来委托 websocket 服务器或基于路径的 socket.io 服务器。 - Patrick Roberts
实际上,与 /aaaaa/graphql 的 WebSocket 连接似乎非常正常,只是 Socket.IO 方面无法升级。 - Trevor Wilson
1
@PatrickRoberts 嘿,那个方法有效!虽然不像链接中描述的那么简单明了,但是手动处理升级事件是关键。非常感谢你! - Trevor Wilson
显示剩余2条评论
2个回答

4
如果有人需要帮助,这是我的解决方案:

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

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服务器委派相关的细微差别,请告诉我。


我不知道你是如何得出这个解决方案的,但我曾经在使用PeerJS和SocketIO组合时遇到了同样的问题,后来我转向Primus并发现了相同的问题。你的解决方案对我使用Primus和PeerJS应用程序时遇到的完全相同的问题起了魔法般的作用。我肯定很想知道你是如何找到这个解决方案的。 - Sameer
@Sameer,上面评论中Patrick Roberts给出的链接让我找对了方向。 - Trevor Wilson

2

在使用graphqlsocket.io-modules并行处理时,我在NestJs中遇到了同样的问题。除了Trevor的解决方案之外,您还可以将socket.io绑定到另一个端口,并使用反向代理(如nginx)来解决路径。

app.gateway.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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接