Skip to main content
Version: 4.x

工作原理

Socket.IO 服务器 (Node.js) 和 Socket.IO 客户端(浏览器、Node.js 或 另一种编程语言)之间的双向通道尽可能使用 WebSocket 连接 建立,并将使用 HTTP 长轮询作为后备。

¥The bidirectional channel between the Socket.IO server (Node.js) and the Socket.IO client (browser, Node.js, or another programming language) is established with a WebSocket connection whenever possible, and will use HTTP long-polling as fallback.

Socket.IO 代码库分为两个不同的层:

¥The Socket.IO codebase is split into two distinct layers:

  • 低层管道:我们所说的 Engine.IO,Socket.IO 内部的引擎

    ¥the low-level plumbing: what we call Engine.IO, the engine inside Socket.IO

  • 高级 API:Socket.IO 本身

    ¥the high-level API: Socket.IO itself

Engine.IO

Engine.IO 负责建立服务器和客户端之间的底层连接。它处理:

¥Engine.IO is responsible for establishing the low-level connection between the server and the client. It handles:

Engine.IO 协议的详细版本可以在 此处 中找到。

¥A detailed version of the Engine.IO protocol can be found here.

参考实现的源代码(用 TypeScript 编写)可以在这里找到:

¥The source code of the reference implementation (written in TypeScript) can be found here:

传输

¥Transports

目前有两种已实现的传输:

¥There are currently two implemented transports:

HTTP 长轮询

¥HTTP long-polling

HTTP 长轮询传输(也简称为 "polling")由连续的 HTTP 请求组成:

¥The HTTP long-polling transport (also simply referred as "polling") consists of successive HTTP requests:

  • 长时间运行的 GET 请求,用于从服务器接收数据

    ¥long-running GET requests, for receiving data from the server

  • 短期运行的 POST 请求,用于向服务器发送数据

    ¥short-running POST requests, for sending data to the server

由于传输的性质,连续的触发可能会在同一 HTTP 请求中串联并发送。

¥Due to the nature of the transport, successive emits may be concatenated and sent within the same HTTP request.

WebSocket

WebSocket 传输由 WebSocket 连接 组成,它在服务器和客户端之间提供双向且低延迟的通信通道。

¥The WebSocket transport consists, well, of a WebSocket connection, which provides a bidirectional and low-latency communication channel between the server and the client.

由于传输的性质,每个触发都在其自己的 WebSocket 帧中发送(某些触发甚至可能导致两个不同的 WebSocket 帧,更多信息 此处)。

¥Due to the nature of the transport, each emit is sent in its own WebSocket frame (some emits may even result in two distinct WebSocket frames, more information here).

握手

¥Handshake

在 Engine.IO 连接开始时,服务器发送一些信息:

¥At the beginning of the Engine.IO connection, the server sends some information:

{
"sid": "FSDjX-WRwSA4zTZMALqx",
"upgrades": ["websocket"],
"pingInterval": 25000,
"pingTimeout": 20000
}
  • sid 是会话的 ID,它必须包含在所有后续 HTTP 请求的 sid 查询参数中

    ¥the sid is the ID of the session, it must be included in the sid query parameter in all subsequent HTTP requests

  • upgrades 数组包含服务器支持的所有 "better" 传输的列表

    ¥the upgrades array contains the list of all "better" transports that are supported by the server

  • pingIntervalpingTimeout 值用于心跳机制

    ¥the pingInterval and pingTimeout values are used in the heartbeat mechanism

升级机制

¥Upgrade mechanism

默认情况下,客户端与 HTTP 长轮询传输建立连接。

¥By default, the client establishes the connection with the HTTP long-polling transport.

但为什么?

¥But, why?

虽然 WebSocket 显然是建立双向通信的最佳方式,但经验表明,由于企业代理、个人防火墙、防病毒软件等原因,并不总是能够建立 WebSocket 连接......

¥While WebSocket is clearly the best way to establish a bidirectional communication, experience has shown that it is not always possible to establish a WebSocket connection, due to corporate proxies, personal firewall, antivirus software...

从用户的角度来看,不成功的 WebSocket 连接可能需要等待实时应用开始交换数据长达 10 秒。这明显损害了用户体验。

¥From the user perspective, an unsuccessful WebSocket connection can translate in up to 10 seconds of waiting for the realtime application to begin exchanging data. This perceptively hurts user experience.

总而言之,Engine.IO 首先关注可靠性和用户体验,其次关注潜在的用户体验改进和服务器性能的提高。

¥To summarize, Engine.IO focuses on reliability and user experience first, marginal potential UX improvements and increased server performance second.

要升级,客户端将:

¥To upgrade, the client will:

  • 确保其传出缓冲区为空

    ¥ensure its outgoing buffer is empty

  • 将当前传输设置为只读模式

    ¥put the current transport in read-only mode

  • 尝试与其他传输建立连接

    ¥try to establish a connection with the other transport

  • 如果成功,关闭第一个传输

    ¥if successful, close the first transport

你可以在浏览器的网络监视器中查看:

¥You can check in the Network Monitor of your browser:

Successful upgrade

  1. 握手(包含会话 ID — 此处为 zBjrh...AAAK — 用于后续请求)

    ¥handshake (contains the session ID — here, zBjrh...AAAK — that is used in subsequent requests)

  2. 发送数据(HTTP 长轮询)

    ¥send data (HTTP long-polling)

  3. 接收数据(HTTP 长轮询)

    ¥receive data (HTTP long-polling)

  4. 升级(WebSocket)

    ¥upgrade (WebSocket)

  5. 接收数据(HTTP 长轮询,4.中的 WebSocket 连接成功建立后关闭)

    ¥receive data (HTTP long-polling, closed once the WebSocket connection in 4. is successfully established)

断线检测

¥Disconnection detection

在以下情况下,Engine.IO 连接被视为已关闭:

¥The Engine.IO connection is considered as closed when:

  • 一个 HTTP 请求(GET 或 POST)失败(例如,当服务器关闭时)

    ¥one HTTP request (either GET or POST) fails (for example, when the server is shutdown)

  • WebSocket 连接关闭(例如,当用户关闭浏览器中的选项卡时)

    ¥the WebSocket connection is closed (for example, when the user closes the tab in its browser)

  • socket.disconnect() 在服务器端或客户端调用

    ¥socket.disconnect() is called on the server-side or on the client-side

还有一个心跳机制,用于检查服务器和客户端之间的连接是否仍然正常运行:

¥There is also a heartbeat mechanism which checks that the connection between the server and the client is still up and running:

在给定的时间间隔(握手中发送的 pingInterval 值),服务器发送 PING 数据包,客户端有几秒钟的时间(pingTimeout 值)发回 PONG 数据包。如果服务器没有收到返回的 PONG 数据包,就会认为连接已关闭。反之,如果客户端在 pingInterval + pingTimeout 时间内没有收到 PING 包,就会认为连接关闭。

¥At a given interval (the pingInterval value sent in the handshake) the server sends a PING packet and the client has a few seconds (the pingTimeout value) to send a PONG packet back. If the server does not receive a PONG packet back, it will consider that the connection is closed. Conversely, if the client does not receive a PING packet within pingInterval + pingTimeout, it will consider that the connection is closed.

断开连接的原因列出为 此处(服务器端)和 此处(客户端)。

¥The disconnection reasons are listed here (server-side) and here (client-side).

Socket.IO

Socket.IO 通过 Engine.IO 连接提供了一些附加功能:

¥Socket.IO provides some additional features over the Engine.IO connection:

Socket.IO 协议的详细版本可以找到 此处

¥A detailed version of the Socket.IO protocol can be found here.

参考实现的源代码(用 TypeScript 编写)可以在这里找到:

¥The source code of the reference implementation (written in TypeScript) can be found here: