Web 实时通信技术对比:HTTP vs SSE vs WebSocket

Web 实时通信技术对比:HTTP vs SSE vs WebSocket

引言

在现代 Web 应用开发中,客户端与服务器之间的实时通信变得越来越重要。从简单的数据获取到实时聊天、股票行情推送、协作编辑等场景,我们需要根据具体需求选择合适的通信方式。本文将深入探讨三种主流的 Web 通信技术:HTTP、Server-Sent Events (SSE) 和 WebSocket,帮助你在实际项目中做出最佳选择。

HTTP:经典的请求-响应模式

基本原理

HTTP(超文本传输协议)是 Web 的基础协议,采用典型的客户端-服务器模式和请求-响应机制。客户端发起请求,服务器返回响应,一次通信结束后连接即关闭(HTTP/1.0)或可复用(HTTP/1.1+ 的 Keep-Alive)。

核心特点

  • 单向通信:只能由客户端主动发起请求
  • 无状态:每个请求都是独立的,服务器不保存客户端状态
  • 短连接:传统 HTTP 一次请求-响应后即断开
  • 轮询开销:实现”实时”需要不断轮询,效率低下

适用场景

  • 传统页面加载和刷新
  • RESTful API 调用
  • 文件下载上传
  • 不需要实时性的数据获取

代码示例

1
2
3
4
5
6
// 短轮询实现伪实时
setInterval(async () => {
const response = await fetch('/api/messages');
const data = await response.json();
updateUI(data);
}, 3000); // 每 3 秒请求一次

缺点:频繁请求造成大量资源浪费,实时性差。

SSE:服务器向客户端的单向推送

基本原理

Server-Sent Events 是 HTML5 引入的一种服务器推送技术,允许服务器通过 HTTP 连接向客户端持续推送数据。它基于 HTTP 协议,使用特殊的 text/event-stream MIME 类型。

核心特点

  • 单向推送:服务器 → 客户端,客户端只能接收
  • 基于 HTTP:无需特殊协议,防火墙友好
  • 自动重连:连接断开后浏览器会自动尝试重连
  • 事件 ID:支持断线续传,不会丢失消息
  • 轻量简单:比 WebSocket 实现更简单

技术细节

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

data: {"message": "Hello World"}

data: {"message": "Second message"}

id: 12345
event: customEvent
data: {"custom": "data"}

适用场景

  • 股票行情、实时报价推送
  • 新闻订阅、社交媒体更新
  • 服务器日志实时监控
  • 进度条更新(文件处理、任务进度)
  • 通知推送

代码示例

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const eventSource = new EventSource('/api/stream');

eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
};

eventSource.addEventListener('customEvent', (event) => {
console.log('自定义事件:', event.data);
});

eventSource.onerror = (error) => {
console.error('连接错误:', error);
};

服务端(Node.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
app.get('/api/stream', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');

const sendEvent = (data) => {
res.write(`data: ${JSON.stringify(data)}\n\n`);
};

const interval = setInterval(() => {
sendEvent({ time: new Date().toISOString() });
}, 1000);

req.on('close', () => {
clearInterval(interval);
});
});

WebSocket:全双工双向通信

基本原理

WebSocket 是一种独立的、基于 TCP 的协议,通过 HTTP 握手升级后建立持久连接,实现全双工通信。客户端和服务器都可以主动发送消息。

核心特点

  • 双向通信:客户端 ↔ 服务器,双方都可主动推送
  • 持久连接:一次握手,长期保持
  • 低延迟:无需 HTTP 头开销,数据帧轻量
  • 实时性强:毫秒级延迟
  • 二进制支持:可传输文本和二进制数据

连接建立过程

// 1. 客户端发起升级请求
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

// 2. 服务器确认升级
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

适用场景

  • 即时聊天应用
  • 多人在线游戏
  • 协作编辑(Google Docs、Figma)
  • 实时音视频通话信令
  • 物联网设备通信
  • 交易系统

代码示例

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const socket = new WebSocket('ws://example.com/socket');

socket.onopen = () => {
console.log('连接已建立');
socket.send(JSON.stringify({ type: 'join', user: 'Alice' }));
};

socket.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
};

socket.onclose = () => {
console.log('连接已关闭');
};

socket.onerror = (error) => {
console.error('连接错误:', error);
};

// 发送消息
function sendMessage(text) {
socket.send(JSON.stringify({ type: 'message', text }));
}

服务端(Node.js + ws)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
console.log('新客户端连接');

ws.on('message', (message) => {
console.log('收到:', message);

// 广播给所有客户端
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});

ws.on('close', () => {
console.log('客户端断开');
});
});

三者详细对比

特性 HTTP SSE WebSocket
通信方向 单向(客户端 → 服务器) 单向(服务器 → 客户端) 双向(客户端 ↔ 服务器)
协议 HTTP HTTP(基于 HTTP) 独立协议(基于 TCP)
连接类型 短连接/Keep-Alive 长连接 持久连接
实时性 差(需轮询) 优秀
服务器推送 不支持 支持 支持
浏览器兼容性 所有浏览器 IE 不支持,其他主流浏览器支持 现代浏览器均支持
数据格式 任意 文本(UTF-8) 文本 + 二进制
重连机制 无需 自动重连 需手动实现
协议开销 高(每次请求都有完整 HTTP 头) 中(初始建立连接) 低(握手后仅数据帧)
实现复杂度 简单 简单 中等
防火墙友好 取决于配置
代理支持 完全支持 支持 部分代理可能有问题

性能对比

延迟

  • HTTP 轮询:取决于轮询间隔,通常秒级延迟
  • SSE:毫秒到秒级,接近实时
  • WebSocket:毫秒级,最低延迟

带宽消耗

假设每秒发送一条消息:

HTTP 轮询:
- 请求头: ~800 bytes
- 响应头: ~200 bytes
- 每秒消耗: 1 KB × 2 = 2 KB/s (即使无数据更新)

SSE:
- 初始连接: ~1 KB
- 每条消息: 数据 + "\n\n" ≈ 数据大小
- 每秒消耗: 数据大小

WebSocket:
- 初始握手: ~500 bytes
- 每条消息: 数据 + 2-14 bytes(帧头)
- 每秒消耗: 数据大小 + 最小开销

服务器负载

  • HTTP 轮询:最高(频繁建立连接)
  • SSE:中等(维持长连接)
  • WebSocket:最低(连接复用,轻量数据帧)

如何选择?

选择 HTTP 当:

  • ✅ 不需要实时性(如传统 CRUD 操作)
  • ✅ 简单的 API 调用
  • ✅ 需要最大兼容性
  • ✅ 无状态的请求

选择 SSE 当:

  • ✅ 只需要服务器单向推送
  • ✅ 需要自动重连机制
  • ✅ 追踪事件历史(Event ID)
  • ✅ 实现简单优先
  • ✅ 防火墙/代理环境严格
  • ❌ 需要客户端频繁向服务器发送数据

选择 WebSocket 当:

  • ✅ 需要双向实时通信
  • ✅ 低延迟要求(如游戏、交易)
  • ✅ 高频率消息传输
  • ✅ 需要传输二进制数据
  • ✅ 客户端需要主动向服务器推送
  • ❌ 对老旧浏览器兼容性要求高

实际案例分析

案例 1:社交媒体通知

推荐:SSE

  • 服务器单向推送通知
  • 自动重连确保不丢消息
  • 实现简单,维护成本低

案例 2:在线聊天应用

推荐:WebSocket

  • 用户需要频繁发送和接收消息
  • 低延迟要求
  • 支持图片等二进制内容

案例 3:股票行情显示

推荐:SSE

  • 服务器持续推送价格更新
  • 客户端只需展示,无需发送
  • 断线重连自动恢复

案例 4:多人协作白板

推荐:WebSocket

  • 多个用户同时绘制
  • 需要实时同步每个操作
  • 双向高频通信

案例 5:数据仪表盘

推荐:HTTP + SSE 组合

  • 初始数据加载用 HTTP
  • 后续更新用 SSE 推送
  • 平衡实现复杂度和性能

案例 6:AI 对话系统(ChatGPT、Claude 等)

推荐:SSE

  • 服务器实时流式输出 AI 生成的文本
  • 用户体验更佳,无需等待完整响应
  • 单向推送符合 AI 对话场景
  • 实现简单,成本低

为什么 AI 对话系统选择 SSE?

现代 AI 对话系统(如 ChatGPT、Claude、Gemini 等)普遍采用 SSE 技术实现流式响应,这是因为:

  1. 改善用户体验:AI 模型生成长文本可能需要几秒甚至十几秒,传统 HTTP 请求会让用户等待很久才能看到结果。使用 SSE 可以逐字逐句地流式输出,用户能实时看到生成过程,体验类似打字效果。

  2. 单向通信特性:在对话场景中,用户发送问题后,AI 需要持续输出答案,这是典型的服务器向客户端单向推送场景,SSE 完美匹配。

  3. 自动重连机制:如果网络出现短暂中断,SSE 会自动重连,确保用户不会丢失部分回答。

  4. 实现简单:相比 WebSocket,SSE 不需要处理双向消息协调,代码更简洁,服务器负载更低。

  5. 兼容性好:基于 HTTP 协议,与现有基础设施(负载均衡、CDN、代理服务器)完美兼容。

实际代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// AI 对话客户端实现
async function chatWithAI(userMessage) {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: userMessage })
});

const reader = response.body.getReader();
const decoder = new TextDecoder();
let aiResponse = '';

while (true) {
const { done, value } = await reader.read();
if (done) break;

const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('\n');

for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') break;

try {
const json = JSON.parse(data);
const token = json.choices[0].delta.content || '';
aiResponse += token;

// 实时更新 UI,逐字显示
updateChatUI(aiResponse);
} catch (e) {
console.error('解析错误:', e);
}
}
}
}
}

// OpenAI 风格的调用方式
const eventSource = new EventSource('/api/chat/stream?prompt=你好');

eventSource.onmessage = (event) => {
if (event.data === '[DONE]') {
eventSource.close();
return;
}

const token = JSON.parse(event.data).token;
appendToMessage(token); // 逐个追加 token
};

服务端流式返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Node.js + Express 实现 AI 流式响应
app.post('/api/chat', async (req, res) => {
const { message } = req.body;

res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');

try {
// 调用 AI 模型(如 OpenAI API)
const stream = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: message }],
stream: true, // 开启流式模式
});

// 逐个 token 推送给客户端
for await (const chunk of stream) {
const token = chunk.choices[0]?.delta?.content || '';
if (token) {
res.write(`data: ${JSON.stringify({ token })}\n\n`);
}
}

// 发送结束信号
res.write('data: [DONE]\n\n');
res.end();
} catch (error) {
res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
res.end();
}
});

性能优势对比

方式 首字节时间 总响应时间 用户感知
传统 HTTP 10 秒 10 秒 等待 10 秒后一次性显示
SSE 流式 200ms 10 秒 200ms 后开始逐字显示

通过 SSE 流式输出,虽然总响应时间相同,但用户感知到的等待时间大幅降低,交互体验显著提升。

实际应用案例

  • ChatGPT:使用 SSE 实现打字机效果的流式回答
  • Claude:采用类似技术提供实时响应体验
  • GitHub Copilot Chat:代码解释和生成使用流式输出
  • Perplexity AI:搜索结果和答案实时流式展示
  • 各类 AI 编程助手:代码生成过程实时可见

这也是为什么你在使用这些 AI 工具时,能看到文字一个个”打”出来,而不是等待很久后突然出现一大段文本。这种技术选择极大地改善了 AI 交互的用户体验。

进阶话题

HTTP/2 Server Push

HTTP/2 引入了服务器推送功能,但它与 SSE 不同:

  • HTTP/2 Push:主动推送资源(CSS、JS)到客户端缓存
  • SSE:持续推送数据流

两者解决的问题不同,不是替代关系。

混合方案

实际项目中可以组合使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 使用 HTTP 进行初始化数据加载
const initData = await fetch('/api/init').then(r => r.json());

// 使用 WebSocket 进行实时更新
const ws = new WebSocket('ws://example.com/realtime');
ws.onmessage = (event) => {
updateData(event.data);
};

// 使用 HTTP 进行文件上传
const formData = new FormData();
formData.append('file', file);
await fetch('/api/upload', { method: 'POST', body: formData });

总结

  • HTTP 是 Web 的基石,适合传统请求-响应场景
  • SSE 简单高效,适合服务器向客户端的单向推送
  • WebSocket 强大灵活,适合需要双向实时通信的场景

没有”最好”的技术,只有”最合适”的选择。根据实际需求权衡实时性、复杂度、兼容性和成本,才能做出明智的技术决策。

在实践中,还需要考虑:

  • 服务器架构(是否支持大量长连接)
  • 网络环境(代理、防火墙策略)
  • 浏览器兼容性要求
  • 团队技术栈和维护能力

希望这篇文章能帮助你在下一个项目中选择最合适的通信方案!


:D 一言句子获取中...