Websockets

🕰️ 什么是长轮询(Long Polling)?

长轮询 是一种在 没有 WebSocket 或 Server-Sent Events(SSE) 的环境下,模拟“服务器推送” 的技术。

客户端发起 HTTP 请求后,服务器不会立即响应,而是挂起连接,直到有新数据可返回超时才发送响应;客户端收到响应后立即重新发起请求,从而实现准实时的双向通信

❌ 但最坏情况是:

  • 前端在 t=0s 发起请求
  • 新数据在 t=0.1s 产生
  • 后端没检测到(比如检查间隔是 1 秒)
  • 后端一直等到 t=10s 超时,才返回“无数据”
  • 前端在 t=10s 收到响应,显示“没更新”
  • 然后立刻发起下一轮请求(t=10s)
  • 后端这次检测到数据(t=10.1s)→ 返回
  • 前端在 t=10.2s 才显示!

延迟 = 10.1 秒!

🧠 核心问题:检测粒度 + 请求对齐

长轮询的延迟主要来自两个地方:

延迟来源 说明
1. 后端检查间隔 如果后端每 1 秒检查一次数据,那么数据产生后最多要等 1 秒才被发现
2. 请求发起时机 如果数据在“上一轮请求刚结束、下一轮还没发”时产生,就要等下一轮超时结束

💡 即使后端“一有数据就返回”,前提是它在“当前这个请求还在挂起时”检测到了数据

🌐 什么是 WebSocket?

WebSocket 是一种全双工通信协议,建立在 TCP 之上,允许客户端和服务器之间实时、双向地传输数据。

对比传统的 HTTP:

  • HTTP 是请求-响应模式:客户端发请求,服务器响应,然后连接关闭。
  • WebSocket 是持久连接:连接建立后,双方可以随时主动发消息,适合聊天、实时通知、在线游戏等场景。

🔁 WebSocket vs HTTP 轮询(举例说明)

假设你要做一个实时股票价格更新页面:

  • HTTP 轮询:前端每 1 秒发一次请求问“价格变了吗?”,服务器回答。浪费带宽,延迟高。
  • WebSocket:连接一次,服务器价格一变就主动推给前端,高效实时。

🧱 WebSocket 通信流程(简化版)

  1. 客户端发起 HTTP 请求,带 Upgrade: websocket 头。
  2. 服务器同意升级协议,返回 101 Switching Protocols
  3. 连接升级为 WebSocket,之后双方通过这个连接发消息(不是 HTTP 了!)。
  4. 任意一方可主动关闭连接。

websocket通信图解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sequenceDiagram
participant Client as 客户端 (React)
participant Server as 服务器 (FastAPI)

Note over Client,Server: 1. 建立 WebSocket 连接
Client->>Server: GET /ws + Upgrade: websocket
Server-->>Client: 101 Switching Protocols

Note over Client,Server: 2. 双向通信阶段
Client->>Server: send("Hello")
Server-->>Client: send("服务器回声: Hello")
Server->>Client: send("主动推送: 2秒过去了!")
Client-->>Server: send("收到推送,谢谢!")

Note over Client,Server: 3. 任意一方可关闭连接
Client->>Server: close()
Server-->>Client: 连接关闭

与长轮询对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sequenceDiagram
participant C as 客户端
participant S as 服务器

Note over C,S: WebSocket(高效)
C->>S: 握手 (HTTP Upgrade)
S-->>C: 101 Switching
loop 实时通信
S->>C: 数据 (任意时刻)
C->>S: 数据 (任意时刻)
end

Note over C,S: 长轮询(低效)
loop 每次都要新建请求
C->>S: GET /poll
S-->>C: 等待...(最多30秒)
S->>C: 响应(有/无数据)
C->>S: GET /poll (立刻重发)
end

参考资料

WebSockets原理,握手和代码实现!用Socket.io制作实时聊天室_哔哩哔哩_bilibili

为什么有http还要有websocket(上)_哔哩哔哩_bilibili