解决连接问题
常见/已知问题:
问题:socket无法连接
可能的解释:
您正在尝试访问普通的 WebSocket 服务器
如“Socket.IO 不是什么”部分所述,Socket.IO 客户端不是 WebSocket 实现,因此无法与 WebSocket 服务器建立连接,即使transports: ["websocket"]
:
const socket = io("ws://echo.websocket.org", {
transports: ["websocket"]
});
服务器无法访问
请确保 Socket.IO 服务器实际上可以通过给定的 URL 访问。您可以使用以下方法对其进行测试:
curl "<the server URL>/socket.io/?EIO=4&transport=polling"
它应该返回如下内容:
0{"sid":"Lbo5JLzTotvW3g2LAAAA","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":20000}
如果不是这种情况,请检查 Socket.IO 服务器是否正在运行,并且两者之间没有任何东西阻止连接。
客户端与服务器版本不兼容
这是JS 客户端的兼容性表:
JS 客户端版本 | Socket.IO 服务器版本 | |||
---|---|---|---|---|
1.x | 2.x | 3.x | 4.x | |
1.x | YES | NO | NO | NO |
2.x | NO | YES | YES1 | YES1 |
3.x | NO | NO | YES | YES |
4.x | NO | NO | YES | YES |
[1] 使用allowEIO3: true
以下是Java 客户端的兼容性表:
Java 客户端版本 | Socket.IO 服务器版本 | ||
---|---|---|---|
2.x | 3.x | 4.x | |
1.x | YES | YES1 | YES1 |
2.x | NO | YES | YES |
[1] 使用allowEIO3: true
这是Swift 客户端的兼容性表:
Swift 客户端版本 | Socket.IO 服务器版本 | ||
---|---|---|---|
2.x | 3.x | 4.x | |
v15.x | YES | YES1 | YES2 |
v16.x | YES3 | YES | YES |
[1] 使用 allowEIO3: true(服务器)和.connectParams(["EIO": "3"])
(客户端):
SocketManager(socketURL: URL(string:"http://localhost:8087/")!, config: [.connectParams(["EIO": "3"])])
[2] 使用 allowEIO3: true(服务器)
[3] 使用 .version(.two)
(客户端):
SocketManager(socketURL: URL(string:"http://localhost:8087/")!, config: [.version(.two)])
服务器未发送必要的 CORS 标头
如果您在控制台中看到以下错误:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ...
大概意思是:
- 要么你实际上没有到达 Socket.IO 服务器(见上文)
- 或者您没有在服务器端启用跨域资源共享(CORS)。
请在此处查看文档。
您没有启用粘性会话(在多服务器设置中)
当扩展到多个 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
服务器端的值:
const io = new Server({
pingTimeout: 60000
});
请注意,升级到 Socket.IO v4(至少socket.io-client@4.1.3
,由于这个原因)应该可以防止此类问题,因为心跳机制已被反转(服务器现在发送 PING 数据包)。
客户端与服务器版本不兼容
由于通过 WebSocket 传输发送的数据包格式在 v2 和 v3/v4 中相似,因此您可能能够连接不兼容的客户端(见上文),但连接最终会在给定延迟后关闭。
因此,如果您在 30 秒后遇到定期断开连接(这是Socket.IO v2 中pingTimeout 和 pingInterval值的总和),这肯定是由于版本不兼容。
你正试图发送一个巨大的有效载荷
如果您在发送大量有效负载时断开连接,这可能意味着您已达到maxHttpBufferSize默认值为 1 MB 的值。请根据您的需要进行调整:
const io = require("socket.io")(httpServer, {
maxHttpBufferSize: 1e8
});
上传时间超过pingTimeout选项值的巨大负载也可能触发断开连接(因为在上传期间heartbeat mechanism失败)。请根据您的需要进行调整:
const io = require("socket.io")(httpServer, {
pingTimeout: 60000
});
问题: socket 卡在 HTTP 长轮询中
在大多数情况下,您应该会看到如下内容:
- Engine.IO 握手(包含会话 ID — 此处
zBjrh...AAAK
— 用于后续请求) - tSocket.IO 握手请求(包含
auth
选项的值) - Socket.IO 握手响应(包含Socket#id)
- WebSocket 连接
- 第一个 HTTP 长轮询请求,一旦建立 WebSocket 连接就关闭
如果您没有看到第四个请求的HTTP 101 Switching Protocols响应,这意味着服务器和浏览器之间的某些东西阻止了 WebSocket 连接。
请注意,这不一定是阻塞的,因为连接仍然是通过 HTTP 长轮询建立的,但效率较低。
您可以通过以下方式获取当前传输的名称:
客户端
socket.on("connect", () => {
const transport = socket.io.engine.transport.name; // 在大多数情况下, "polling"
socket.io.engine.on("upgrade", () => {
const upgradedTransport = socket.io.engine.transport.name; // 在大多数情况下, "websocket"
});
});
服务器
io.on("connection", (socket) => {
const transport = socket.conn.transport.name; // 在大多数情况下, "polling"
socket.conn.on("upgrade", () => {
const upgradedTransport = socket.conn.transport.name; // 在大多数情况下, "websocket"
});
});
可能的解释:
服务器前面的代理不接受 WebSocket 连接
请在此处查看文档。