Skip to content

WebSocket 协议 与 SSE

轮询

WebSocket 之前,如果需要在客户端和服务之间双向通信,需要通过 HTTP 轮询来实现, HTTP 轮询分为轮询与长轮询:

轮询是指浏览器通过 JavaScript 启动一个定时器,然后以固定的间隔给服务器发请求,询问服务器有没有新消息,缺点:

  • 实时性不够
  • 频繁的请求会给服务器带来极大的压力

长轮询是指浏览器发送一个请求时,服务器先拖一段时间,等到有消息了再回复。这个机制暂时地解决了实时性问题,相比轮询,长轮询减少了请求的次数,只在有新数据时才响应,从而节省了带宽和服务器资源。但是它带来了新的问题:

  • 以多线程模式运行的服务器会让大部分线程大部分时间都处于挂起状态,极大地浪费服务器资源
  • 一个HTTP连接在长时间没有数据传输的情况下,链路上的任何一个网关都可能关闭这个连接,而网关是我们不可控的

因此,HTML5 新增了 WebSocket 协议,能够在浏览器和服务器之间建立一个不受限的双向通信的通道。

WebSocket

WebSocket 是一种在单个 TCP 连接上进行全双工、持久化通信的协议,适用于客户端和服务器之间频繁、实时的数据交互需求,比如:即时聊天、实时游戏、在线协作编辑、股票行情等。

🧩 WebSocket 核心特点

  • 全双工通信:客户端和服务器都可以随时发送消息,不必等待对方请求。
  • 持久连接:建立连接后不会每次通信都进行 HTTP 握手,降低了开销。
  • 基于 TCP:通过标准的 HTTP 协议发起握手请求,握手成功后切换为 WebSocket 协议。
  • 跨平台跨语言支持广泛。

📡 WebSocket 工作原理

WebSocket 的连接始于一个标准的 HTTP 请求,客户端请求服务器“升级协议”。

1. 📤 客户端请求

客户端会向服务器发送一个带有特殊头部的 HTTP GET 请求:

makefile
GET /chat HTTP/1.1
Host: example.com:80
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
  • Upgrade: websocket:表示请求将升级为 WebSocket 协议。
  • Connection: Upgrade:配合 Upgrade 字段使用。
  • Sec-WebSocket-Key:Base64 编码的随机字符串,用于服务器生成响应校验。
  • Sec-WebSocket-Version:表示客户端支持的 WebSocket 协议版本,通常是 13。
  • Sec-WebSocket-Protocol :协议

🧾 2. 服务器响应(HTTP 101 切换协议)

服务器接收到 Upgrade 请求后,会检查并确认支持 WebSocket,并返回如下 HTTP 响应:

txt
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
  • 101 Switching Protocols:表示协议切换成功。
  • Sec-WebSocket-Accept:服务器根据客户端的 Sec-WebSocket-Key 和一个固定魔法字符串拼接后 SHA-1 计算、Base64 编码得出,表示确认。
📐 Sec-WebSocket-Accept 计算过程:
js
const crypto = require('crypto');
const key = 'dGhlIHNhbXBsZSBub25jZQ=='; // 来自客户端
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';

const acceptKey = crypto
  .createHash('sha1')
  .update(key + GUID)
  .digest('base64');

console.log(acceptKey); // s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

🔄 3. 建立 WebSocket 通信通道

  • 协议握手完成后,TCP 连接保持,双方不再通过 HTTP 发送数据,而是使用 WebSocket 数据帧协议。
  • 这时就可以开始全双工通信了:客户端和服务器都可以随时主动发送消息。

🧪 WebSocket 示例

1.🧪 前端

html
<script>
  const socket = new WebSocket('ws://localhost:3000');

  socket.onopen = () => {
    console.log('Connected');
    socket.send('Hello Server!');
  };

  socket.onmessage = (event) => {
    console.log('Message from server:', event.data);
  };

  socket.onclose = () => {
    console.log('Disconnected');
  };

  socket.onerror = (error) => {
    console.error('WebSocket error:', error);
  };
</script>

2.🛠 Node.js 后端示例(使用 ws 库)

js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3000 });

wss.on('connection', (ws) => {
  console.log('Client connected');
  ws.send('Welcome!');

  ws.on('message', (message) => {
    console.log('Received:', message);
    ws.send(`Echo: ${message}`);
  });

  ws.on('close', () => {
    console.log('Client disconnected');
  });
});

SSE

SSE(Server-Sent Events,服务器发送事件)是一种从服务器单向推送数据到浏览器的机制,是 HTML5 标准的一部分。

🧭 一句话理解 SSE

SSE 是一种通过 HTTP 长连接 实现的 服务器向浏览器持续推送文本事件 的机制,只支持 服务器 → 客户端 的单向通信。

工作原理

通过 HTTP 长连接,服务器持续不断地向客户端推送符合特定格式的文本数据客户端通过 EventSource 接口解析这些数据并触发事件

🔄 工作原理流程图

txt
Client (Browser)              Server
    |                            |
    |--- HTTP 请求 /sse -------->|
    |                            |
    |<-- HTTP 响应 200 OK --------
    |<-- text/event-stream -------- (保持连接)
    |<-- data: hello world\n\n ----
    |<-- data: 2025-05-29T12:00Z\n\n
    |<-- (持续推送中...) ---------
    |                            |

🧾 步骤详解

1. 客户端发起 HTTP 请求

使用浏览器原生的 EventSource 发起连接:

js
const source = new EventSource('/sse');

这是一个普通的 GET 请求,不会升级协议,仍然是 HTTP/1.1。

2. 服务器返回特殊的响应头

http
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

一旦这些头部发送完毕,服务器就保持连接不断开。

3. 服务器持续发送数据帧(基于文本协议)

每条消息由文本行组成,主要字段有:

字段说明
data:消息内容(可以有多行)
event:自定义事件名,客户端可通过 .addEventListener 监听
id:消息 ID,浏览器会记录并在重连时发送
retry:重连时间(毫秒)
示例消息格式
yaml
id: 1234
event: progress
data: 45%

\n

4. 客户端解析并触发事件

浏览器持续读取数据流,并解析消息:

js
source.onmessage = (e) => console.log('Data:', e.data);
source.addEventListener('progress', (e) => {
  console.log('Progress:', e.data);
});

5. 自动断线重连

  • 浏览器断网或服务器断开连接后,浏览器会 自动重连(默认间隔 3 秒)。
  • 如果服务器发送 retry: 5000,浏览器会使用这个值作为下一次重连的间隔。
  • 客户端还会自动发送 Last-Event-ID 头,服务器可以据此恢复状态。

6. 关闭连接

浏览器端可以手动关闭连接

js
source.close();

✅ 总结:SSE 核心机制

机制实现方式
持久连接HTTP 长连接(不关闭 TCP)
消息格式基于文本协议,特定字段+换行
重连机制浏览器自动完成
状态续传使用 id: + Last-Event-ID 实现
事件监听onmessage / .addEventListener

📡 与 WebSocket 对比

特性SSEWebSocket
通信方式单向(服务器 → 客户端)双向
协议HTTP(基于文本)自定义协议(基于 TCP)
浏览器原生支持✅(EventSource API)
断线自动重连✅ 自动❌ 需要手动实现
复杂度简单中等
适用场景实时日志、进度更新、通知等聊天、协作、游戏、音视频等

严选 WebSocket 协议

Web 实时推送技术的总结

手摸手教你使用WebSocket

WebSocket探秘

WebSockets 与长轮询的较量