目录
- 一、先说结论
- [二、为什么 SSE 和 WebSocket 容易搞混?](#二、为什么 SSE 和 WebSocket 容易搞混?)
- [三、什么是 HTTP?](#三、什么是 HTTP?)
- [四、普通 HTTP 是什么样的?](#四、普通 HTTP 是什么样的?)
- 五、什么是"长连接"?
- [六、什么是 SSE?](#六、什么是 SSE?)
- [七、SSE 是怎么工作的?](#七、SSE 是怎么工作的?)
- [八、SSE 的通信过程](#八、SSE 的通信过程)
- [九、SSE 为什么特别适合 AI?](#九、SSE 为什么特别适合 AI?)
- [十、AI 流式输出](#十、AI 流式输出)
- [十一、SSE 的前端实现](#十一、SSE 的前端实现)
- [十二、EventSource 有什么限制?](#十二、EventSource 有什么限制?)
- [十三、什么是 @microsoft/fetch-event-source?](#十三、什么是 @microsoft/fetch-event-source?)
- [十四、SSE 和 AbortController](#十四、SSE 和 AbortController)
- [十五、什么是 WebSocket?](#十五、什么是 WebSocket?)
- [十六、WebSocket 像什么?](#十六、WebSocket 像什么?)
- [十七、WebSocket 前端怎么使用?](#十七、WebSocket 前端怎么使用?)
- [十八、WebSocket 最大的特点](#十八、WebSocket 最大的特点)
- [十九、SSE 和 WebSocket 对比](#十九、SSE 和 WebSocket 对比)
- [二十、SSE 和 WebSocket 的核心区别](#二十、SSE 和 WebSocket 的核心区别)
- [二十一、SSE 适合什么场景?](#二十一、SSE 适合什么场景?)
-
- [1. AI 流式输出](#1. AI 流式输出)
- [2. 任务进度](#2. 任务进度)
- [3. 实时日志](#3. 实时日志)
- [4. 消息通知](#4. 消息通知)
- [二十二、WebSocket 适合什么场景?](#二十二、WebSocket 适合什么场景?)
-
- [1. 在线聊天](#1. 在线聊天)
- [2. 在线游戏](#2. 在线游戏)
- [3. 多人协作](#3. 多人协作)
- [4. 实时控制](#4. 实时控制)
- [二十三、为什么 AI 不全部使用 WebSocket?](#二十三、为什么 AI 不全部使用 WebSocket?)
- [二十四、普通 HTTP、SSE、WebSocket 放在一起](#二十四、普通 HTTP、SSE、WebSocket 放在一起)
- [二十五、还有一个容易混淆的东西:HTTP Streaming](#二十五、还有一个容易混淆的东西:HTTP Streaming)
- [二十六、HTTP Streaming 和 SSE 有什么关系?](#二十六、HTTP Streaming 和 SSE 有什么关系?)
- 二十七、前端应该怎么选择?
-
- [场景一:普通 API](#场景一:普通 API)
- 场景二:服务器不断给我数据
- 场景三:客户端和服务器都需要实时发送
- 二十八、一个非常实用的判断方法
- 二十九、最终记忆图
- 三十、一句话总结
一、先说结论
如果暂时不想看全文,可以先记住这张图:
text
客户端 Client 服务器 Server
│ │
│ │
│──────────── 普通 HTTP ────────────→ │
│←──────────── 返回结果 ────────────── │
│ │
│ 请求结束 │
普通 HTTP:
请求一次,服务器回答一次。
SSE:
text
客户端 Client 服务器 Server
│ │
│──────────── 建立连接 ─────────────→ │
│ │
│←──────────── 数据 1 ────────────────│
│←──────────── 数据 2 ────────────────│
│←──────────── 数据 3 ────────────────│
│←──────────── 数据 4 ────────────────│
│ │
SSE:
客户端发起一次请求,服务器可以持续向客户端推送数据。
也就是:
text
Server → Client
单向通信。
WebSocket:
text
客户端 Client 服务器 Server
│ │
│──────────── 建立连接 ─────────────→│
│ │
│──────────── 消息 ─────────────────→│
│←─────────── 消息 ──────────────────│
│──────────── 消息 ─────────────────→│
│←─────────── 消息 ──────────────────│
│──────────── 消息 ─────────────────→│
│ │
WebSocket:
客户端和服务器双方都可以随时发送消息。
也就是:
text
Client ↔ Server
双向通信。
二、为什么 SSE 和 WebSocket 容易搞混?
因为它们都有一个共同特点:
连接可以持续存在。
例如普通 HTTP:
text
请求
↓
服务器处理
↓
返回
↓
结束
而 SSE:
text
请求
↓
连接保持
↓
服务器不断发送数据
↓
连接结束
WebSocket:
text
建立连接
↓
保持连接
↓
客户端 ↔ 服务器
↓
客户端 ↔ 服务器
↓
客户端 ↔ 服务器
↓
连接关闭
所以很多人会说:
SSE 和 WebSocket 都是长连接。
真正重要的是:
text
SSE:(单向)
Server ─────────→ Client
WebSocket:(双向)
Server ←────────→ Client
三、什么是 HTTP?
在理解 SSE 和 WebSocket 之前,先理解 HTTP。
HTTP 可以简单理解成:
客户端和服务器之间进行通信的一套规则。
例如:
text
浏览器
│
│ 请求
↓
服务器
│
│ 响应
↓
浏览器
前端:
js
fetch('/api/user')
服务器可能返回:
json
{
"name": "张三",
"age": 20
}
四、普通 HTTP 是什么样的?
比如我们有一个:
text
GET /api/user
整个过程:
text
Client
│
│ GET /api/user
↓
Server
│
│ 查询用户
↓
Server
│
│ 返回 JSON
↓
Client
然后这次请求就结束了。
所以普通 HTTP 可以简单理解成:
text
请求一次
↓
响应一次
↓
结束
这特别适合:
text
查询用户
创建订单
修改用户信息
删除数据
查询列表
提交表单
也就是我们平常说的:
CRUD 接口。
五、什么是"长连接"?
这里开始进入重点。
所谓长连接,可以简单理解为:
连接建立之后,不急着关闭,而是保持一段时间。
普通请求:
text
请求
↓
响应
↓
结束
长连接:
text
请求
↓
连接保持
↓
持续通信
↓
最后才关闭
注意:
长连接只是描述"连接保持比较久",并不等于某一种具体技术。
SSE 和 WebSocket 都可以实现持续通信,但是实现方式不同。
六、什么是 SSE?
SSE 全称:
text
Server-Sent Events
中文通常叫:
服务器发送事件
SSE 是一种让服务器可以持续向浏览器推送数据的技术。
核心特点:
text
Server → Client
也就是说:
服务器可以不断给客户端发送数据。
七、SSE 是怎么工作的?
假设前端请求:
text
/api/chat
服务器不会马上结束响应,而是告诉浏览器:
http
Content-Type: text/event-stream
意思是:
"这是一个持续的数据流。"
然后服务器可以不断发送:
text
data: Hello
data: World
data: JavaScript
data: SSE
注意:
SSE 的消息之间通常通过空行进行分隔。
八、SSE 的通信过程
例如:
text
浏览器 服务器
│ │
│────── 请求 /api/chat ──────→│
│ │
│←──── data: Hello ───────────│
│ │
│←──── data: World ───────────│
│ │
│←──── data: SSE ─────────────│
│ │
│←──── data: End ─────────────│
│ │
可以发现:
客户端主要负责:
text
"听"
服务器负责:
text
"不断说"
所以 SSE 特别适合:
服务器不断向客户端推送数据的场景。
九、SSE 为什么特别适合 AI?
你现在看到的很多 AI 产品:
text
ChatGPT
Claude
各种 AI 编程助手
AI 搜索
AI 对话产品
回答的时候都有一个特点:
不是一次性把答案全部返回,而是一点一点生成。
例如:
text
用户:
解释一下 JavaScript 闭包
服务器可能生成:
text
闭包
然后:
text
闭包是
然后:
text
闭包是 JavaScript
然后:
text
闭包是 JavaScript 中一种重要的机制
最后:
text
闭包是 JavaScript 中一种重要的机制,它允许...
十、AI 流式输出
整个过程可以理解成:
text
用户
│
│ "解释一下闭包"
↓
AI服务器
│
├── "闭包"
│
├── "是"
│
├── "JavaScript"
│
├── "中的"
│
├── "一种"
│
└── "机制"
↓
浏览器
前端不断把收到的数据拼接起来:
js
answer.value += event.data
于是用户看到:
text
闭包
变成:
text
闭包是
变成:
text
闭包是 JavaScript
最终:
text
闭包是 JavaScript 中的一种机制...
这就是我们经常看到的:
AI 打字机效果。
十一、SSE 的前端实现
浏览器原生提供了:
js
EventSource
最简单的例子:
js
const eventSource = new EventSource('/api/message')
eventSource.onmessage = (event) => {
console.log(event.data)
}
服务器:
text
data: Hello
data: World
前端:
js
eventSource.onmessage = (event) => {
console.log(event.data)
}
得到:
text
Hello
World
十二、EventSource 有什么限制?
虽然 EventSource 很方便,但是它有一些限制。
例如:
js
new EventSource('/api/message')
主要就是 GET 请求。
但是现在很多 AI 接口可能需要:
text
POST
Authorization
请求体
自定义 Header
AbortController
例如:
js
POST /api/chat
Authorization: Bearer xxx
{
"message": "你好"
}
这时候原生 EventSource 就不太方便。
十三、什么是 @microsoft/fetch-event-source?
text
@microsoft/fetch-event-source
它可以理解成:
一个基于 fetch 思路处理 SSE 的工具库。
安装:
bash
pnpm add @microsoft/fetch-event-source
使用:
js
import { fetchEventSource } from '@microsoft/fetch-event-source'
await fetchEventSource('/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: '你好'
}),
onmessage(event) {
console.log(event.data)
}
})
这样就可以更方便地:
text
POST
Header
Body
Signal
SSE
一起使用。
一键跳转 ➡️ microsoft/fetch-event-source 新手入门!超详细!!【SSE】【JavaScript库】
十四、SSE 和 AbortController
AI 对话还有一个非常常见的需求:
点击停止生成。
例如:
text
AI:
JavaScript 是一种...
↓
停止
这时候可以使用:
js
const controller = new AbortController()
然后:
js
fetchEventSource('/api/chat', {
signal: controller.signal,
onmessage(event) {
console.log(event.data)
}
})
用户点击停止:
js
controller.abort()
于是:
text
客户端
│
│ abort()
↓
停止 SSE 请求
这就是你之前学习的:
text
AbortController
和 SSE 联系起来了。
十五、什么是 WebSocket?
WebSocket 是另一种实时通信技术。
它最大的特点:
客户端和服务器都可以主动发送消息。
也就是:
text
Client ↔ Server
十六、WebSocket 像什么?
可以把 WebSocket 想象成:
打电话。
电话接通:
text
你 ↔ 对方
你可以说话:
text
你 → 对方
对方也可以主动说话:
text
你 ← 对方
而且不需要每次都重新打电话。
WebSocket 就很像这种模式。
十七、WebSocket 前端怎么使用?
浏览器原生提供:
js
WebSocket
例如:
js
const ws = new WebSocket('wss://example.com/chat')
javascript
wss://example.com/chat
│ │ │
│ │ └── WebSocket 路径(API路径)
│ └───────────────── 服务器域名
└──────────────────────── WebSocket 安全协议
| 协议 | 含义 | 类似 |
|---|---|---|
ws:// |
普通 WebSocket | http:// |
wss:// |
加密 WebSocket | https:// |
javascript
const ws = new WebSocket(
'wss://www.xxx.com/ws'
)
连接成功:
js
ws.onopen = () => {
console.log('连接成功')
}
收到服务器消息:
js
ws.onmessage = (event) => {
console.log(event.data)
}
客户端主动发送:
js
ws.send('你好')
连接关闭:
js
ws.onclose = () => {
console.log('连接关闭')
}
发生错误:
js
ws.onerror = (error) => {
console.error(error)
}
十八、WebSocket 最大的特点
注意这个:
js
ws.send('你好')
客户端可以主动发送消息。
服务器也可以主动发送:
text
Server → Client
因此:
text
SSE:
Client ─────────→ Server
请求
Client ←───────── Server
数据
主要是单向推送
WebSocket:
Client ─────────→ Server
Client ←───────── Server
Client ─────────→ Server
Client ←───────── Server
双向通信
十九、SSE 和 WebSocket 对比
这是最重要的一张表。
| 对比 | SSE | WebSocket |
|---|---|---|
| 全称 | Server-Sent Events | WebSocket |
| 通信方向 | 单向 | 双向 |
| Server → Client | ✅ | ✅ |
| Client → Server | ❌* | ✅ |
| 长连接 | ✅ | ✅ |
| 基于 | HTTP | WebSocket 协议 |
| 浏览器 API | EventSource | WebSocket |
| 数据特点 | 事件流 | 双向消息 |
| AI 流式输出 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 在线聊天 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 在线游戏 | ⭐ | ⭐⭐⭐⭐⭐ |
| 多人协作 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| 实现难度 | 较简单 | 相对复杂 |
❌*指的是 SSE 的同一条事件流不是用来做客户端持续双向发送的;客户端当然可以另外通过普通 HTTP 请求向服务器发送数据。
二十、SSE 和 WebSocket 的核心区别
如果面试官问:
SSE 和 WebSocket 有什么区别?
你可以回答:
SSE 是基于 HTTP 的服务器推送技术,主要用于服务器向客户端持续发送数据,是单向通信;WebSocket 是一种双向通信协议,客户端和服务器都可以主动发送消息,更适合实时聊天、在线游戏、多人协作等双向实时场景。
然后补充:
text
SSE:
Server → Client
WebSocket:
Server ↔ Client
基本就已经抓住核心了。
二十一、SSE 适合什么场景?
1. AI 流式输出
text
AI回答
↓
一个字/一段一段返回
非常典型。
2. 任务进度
例如:
text
任务开始
0%
↓
20%
↓
40%
↓
60%
↓
80%
↓
100%
服务器不断通知前端:
text
进度:20%
进度:40%
进度:60%
进度:80%
进度:100%
非常适合 SSE。
3. 实时日志
例如部署系统:
text
开始构建...
安装依赖...
编译中...
构建成功...
部署完成...
服务器不断把日志推给前端。
4. 消息通知
例如:
text
你有一条新消息
服务器可以主动推送给浏览器。
二十二、WebSocket 适合什么场景?
1. 在线聊天
例如:
text
用户 A ↔ 服务器 ↔ 用户 B
双方都可能主动发送消息。
2. 在线游戏
例如:
text
玩家移动
↓
客户端 → 服务器
↓
服务器 → 其他玩家
而且这种通信频率可能非常高。
3. 多人协作
例如在线编辑文档:
text
A 修改内容
↓
服务器
↓
B、C、D
同时:
text
B 修改内容
↓
服务器
↓
A、C、D
这是典型的双向实时通信。
4. 实时控制
例如:
text
客户端
↓
控制服务器
↓
设备
设备状态
↓
服务器
↓
客户端
双方都需要通信。
二十三、为什么 AI 不全部使用 WebSocket?
这是一个很好的问题。
因为很多 AI 对话其实是:
text
用户
↓
发送问题
AI
↓
不断生成答案
↓
不断发送给用户
也就是:
text
Client → Server
Server → Client
Server → Client
Server → Client
Server → Client
实际上主要是:
text
服务器 → 客户端
所以 SSE 非常合适。
没必要为了:
text
Server ↔ Client
的能力引入 WebSocket。
二十四、普通 HTTP、SSE、WebSocket 放在一起
现在把三个放在一起:
text
网络通信
│
┌───────────┼───────────┐
↓ ↓ ↓
HTTP SSE WebSocket
│ │ │
请求/响应 服务端推送 双向通信
│ │ │
↓ ↓ ↓
CRUD AI流式输出 聊天
任务进度 游戏
实时日志 协作
二十五、还有一个容易混淆的东西:HTTP Streaming
除了 SSE 和 WebSocket,还有:
HTTP Streaming
这个概念也非常重要。
HTTP Streaming 可以简单理解成:
服务器通过 HTTP 响应持续发送数据,客户端边接收边处理。
例如:
text
HTTP Response
↓
数据块1
↓
数据块2
↓
数据块3
↓
数据块4
前端可以使用:
js
fetch('/api/stream')
然后读取:
js
const response = await fetch('/api/stream')
const reader = response.body.getReader()
while (true) {
const { done, value } = await reader.read()
if (done) {
break
}
console.log(value)
}
二十六、HTTP Streaming 和 SSE 有什么关系?
这个地方非常重要:
SSE 是一种特定格式的 HTTP 流。
可以简单理解成:
text
HTTP Streaming
│
├── 普通数据流
│
└── SSE
│
└── text/event-stream
SSE 有自己规定的数据格式:
text
data: Hello
data: World
而普通 HTTP Streaming 可以是任意数据块。
所以:
text
HTTP Streaming
是一个更大的概念。
而:
text
SSE
是其中一种具体实现方式。
二十七、前端应该怎么选择?
以后遇到需求,可以按照下面的方式判断。
场景一:普通 API
text
查询用户
查询列表
创建订单
修改数据
使用:
text
fetch / axios
场景二:服务器不断给我数据
例如:
text
AI回答
任务进度
实时日志
优先考虑:
text
SSE
场景三:客户端和服务器都需要实时发送
例如:
text
在线聊天
实时游戏
多人协作
实时控制
考虑:
text
WebSocket
二十八、一个非常实用的判断方法
以后看到需求,直接问自己一个问题:
"谁需要主动说话?"
只有服务器需要不断说话
text
Server → Client
考虑:
text
SSE
客户端和服务器都需要不断说话
text
Client ↔ Server
考虑:
text
WebSocket
只是问一次答一次
text
Client → Server
Server → Client
考虑:
text
HTTP
二十九、最终记忆图
把这张图记住:
text
前端网络通信
│
┌────────────────┼────────────────┐
│ │ │
↓ ↓ ↓
HTTP SSE WebSocket
│ │ │
│ │ │
请求一次 持续推送 双向通信
│ │ │
↓ ↓ ↓
Client → Server Server → Client Client ↔ Server
│ │ │
↓ ↓ ↓
CRUD AI流式输出 在线聊天
任务进度 在线游戏
实时日志 多人协作
三十、一句话总结
最后只记住这三个:
text
普通 HTTP:
"我问你一次,你回答我一次。"
text
SSE:
"我问你一次,你可以一直告诉我后续发生了什么。"
text
WebSocket:
"我们俩建立一条长期通道,谁想说话都可以随时说。"
用程序员的话来说:
text
HTTP
Client → Server → Client
SSE
Client → Server
Server → Client → Client → Client → Client
WebSocket
Client ↔ Server
Client ↔ Server
Client ↔ Server
所以最核心的区别不是"谁是长连接",而是"通信方向和使用场景不同"。