解决连接问题

常见/已知问题:

问题:socket无法连接

可能的解释:

您正在尝试访问普通的 WebSocket 服务器

“Socket.IO 不是什么”部分所述,Socket.IO 客户端不是 WebSocket 实现,因此无法与 WebSocket 服务器建立连接,即使transports: ["websocket"]

  1. const socket = io("ws://echo.websocket.org", {
  2. transports: ["websocket"]
  3. });

服务器无法访问

请确保 Socket.IO 服务器实际上可以通过给定的 URL 访问。您可以使用以下方法对其进行测试:

  1. curl "<the server URL>/socket.io/?EIO=4&transport=polling"

它应该返回如下内容:

  1. 0{"sid":"Lbo5JLzTotvW3g2LAAAA","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":20000}

如果不是这种情况,请检查 Socket.IO 服务器是否正在运行,并且两者之间没有任何东西阻止连接。

客户端与服务器版本不兼容

这是JS 客户端的兼容性表:

JS 客户端版本Socket.IO 服务器版本
1.x2.x3.x4.x
1.xYESNONONO
2.xNOYESYES1YES1
3.xNONOYESYES
4.xNONOYESYES

[1] 使用allowEIO3: true

以下是Java 客户端的兼容性表:

Java 客户端版本Socket.IO 服务器版本
2.x3.x4.x
1.xYESYES1YES1
2.xNOYESYES

[1] 使用allowEIO3: true

这是Swift 客户端的兼容性表:

Swift 客户端版本Socket.IO 服务器版本
2.x3.x4.x
v15.xYESYES1YES2
v16.xYES3YESYES

[1] 使用 allowEIO3: true(服务器)和.connectParams(["EIO": "3"])(客户端):

  1. SocketManager(socketURL: URL(string:"http://localhost:8087/")!, config: [.connectParams(["EIO": "3"])])

[2] 使用 allowEIO3: true(服务器)

[3] 使用 .version(.two)(客户端):

  1. SocketManager(socketURL: URL(string:"http://localhost:8087/")!, config: [.version(.two)])

服务器未发送必要的 CORS 标头

如果您在控制台中看到以下错误:

  1. Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ...

大概意思是:

请在此处查看文档。

您没有启用粘性会话(在多服务器设置中)

当扩展到多个 Socket.IO 服务器时,您需要确保给定 Socket.IO 会话的所有请求都到达同一个 Socket.IO 服务器。解释可以在这里找到。

否则将导致 HTTP 400 响应代码:{"code":1,"message":"Session ID unknown"}

请在此处查看文档。

问题: socket 断开连接

首先,请注意,即使在稳定的 Internet 连接上,断开连接也很常见并且是意料之中的:

  • 用户和 Socket.IO 服务器之间的任何事情都可能遇到临时故障或重新启动
  • 作为自动缩放策略的一部分,服务器本身可能会被终止
  • 如果使用移动浏览器,用户可能会失去连接或从 WiFi 切换到 4G
  • 浏览器本身可能会冻结非活动选项卡

话虽如此,除非另有明确说明,否则Socket.IO 客户端将始终尝试重新连接。

断开连接的可能解释:

浏览器选项卡最小化,心跳失败

当浏览器选项卡不在焦点上时,某些浏览器(如Chrome)会限制 JavaScript 计时器,这可能会通过Socket.IO v2 中的 ping 超时导致断开连接,因为心跳机制依赖于setTimeout客户端的功能。

作为一种解决方法,您可以增加pingTimeout服务器端的值:

  1. const io = new Server({
  2. pingTimeout: 60000
  3. });

请注意,升级到 Socket.IO v4(至少socket.io-client@4.1.3,由于这个原因)应该可以防止此类问题,因为心跳机制已被反转(服务器现在发送 PING 数据包)。

客户端与服务器版本不兼容

由于通过 WebSocket 传输发送的数据包格式在 v2 和 v3/v4 中相似,因此您可能能够连接不兼容的客户端(见上文),但连接最终会在给定延迟后关闭。

因此,如果您在 30 秒后遇到定期断开连接(这是Socket.IO v2 中pingTimeoutpingInterval值的总和),这肯定是由于版本不兼容。

你正试图发送一个巨大的有效载荷

如果您在发送大量有效负载时断开连接,这可能意味着您已达到maxHttpBufferSize默认值为 1 MB 的值。请根据您的需要进行调整:

  1. const io = require("socket.io")(httpServer, {
  2. maxHttpBufferSize: 1e8
  3. });

上传时间超过pingTimeout选项值的巨大负载也可能触发断开连接(因为在上传期间heartbeat mechanism失败)。请根据您的需要进行调整:

  1. const io = require("socket.io")(httpServer, {
  2. pingTimeout: 60000
  3. });

问题: socket 卡在 HTTP 长轮询中

在大多数情况下,您应该会看到如下内容:

Network monitor upon success

  1. Engine.IO 握手(包含会话 ID — 此处 zBjrh...AAAK — 用于后续请求)
  2. tSocket.IO 握手请求(包含auth选项的值)
  3. Socket.IO 握手响应(包含Socket#id
  4. WebSocket 连接
  5. 第一个 HTTP 长轮询请求,一旦建立 WebSocket 连接就关闭

如果您没有看到第四个请求的HTTP 101 Switching Protocols响应,这意味着服务器和浏览器之间的某些东西阻止了 WebSocket 连接。

请注意,这不一定是阻塞的,因为连接仍然是通过 HTTP 长轮询建立的,但效率较低。

您可以通过以下方式获取当前传输的名称:

客户端

  1. socket.on("connect", () => {
  2. const transport = socket.io.engine.transport.name; // 在大多数情况下, "polling"
  3. socket.io.engine.on("upgrade", () => {
  4. const upgradedTransport = socket.io.engine.transport.name; // 在大多数情况下, "websocket"
  5. });
  6. });

服务器

  1. io.on("connection", (socket) => {
  2. const transport = socket.conn.transport.name; // 在大多数情况下, "polling"
  3. socket.conn.on("upgrade", () => {
  4. const upgradedTransport = socket.conn.transport.name; // 在大多数情况下, "websocket"
  5. });
  6. });

可能的解释:

服务器前面的代理不接受 WebSocket 连接

请在此处查看文档。