SSE:
使用场景
- 实时更新:如股票行情、聊天应用中的实时消息推送、监控系统的实时数据等。
- 轻量级推送:与 WebSocket 相比,SSE 的实现更加简单,只需要 HTTP 协议,并且浏览器支持较好。
关键点
- 数据格式:SSE 使用纯文本格式,数据通过事件流推送。服务器将不断发送数据流,客户端可以根据事件监听到每条消息。
- 单向通信:SSE 是单向的,意味着服务器可以主动向客户端推送数据,而客户端无法通过相同的连接向服务器发送数据(客户端需要发起独立的 HTTP 请求)。
- 自动重连:浏览器原生支持自动重新连接功能,当连接中断时,客户端会自动尝试重新建立连接
SSE 的工作原理
- 服务器端 通过 HTTP 响应头
Content-Type: text/event-stream表示这是一个 SSE 连接。 - 客户端 可以通过 JavaScript 的
EventSourceAPI 来接收服务端持续推送的事件。
示例:服务端响应(Node.js 示例)
在服务器端,配置响应头并定期推送消息:
// node.js
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
// 发送事件
setInterval(() => {
res.write(`data: ${new Date().toLocaleTimeString()}\n\n`);
}, 1000);
}).listen(3000, () => {
console.log("SSE server is running on port 3000");
});
客户端接收(HTML + JS)
客户端使用 EventSource 来建立 SSE 连接并接收事件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSE Example</title>
</head>
<body>
<h1>Server-Sent Events Example</h1>
<div id="time"></div>
<script>
const eventSource = new EventSource('http://localhost:3000');
eventSource.onmessage = function(event) {
document.getElementById('time').textContent = event.data;
};
eventSource.onerror = function() {
console.error("Error receiving events.");
};
</script>
</body>
</html>
主要事件
onmessage:接收服务端发送的消息。onopen:当连接建立时触发。onerror:当出现错误或连接中断时触发。
SSE 与 WebSocket 区别
- 通信方向:SSE 是服务端到客户端的单向通信,WebSocket 是双向通信。
- 连接复杂度:SSE 使用 HTTP 协议,较为简单;WebSocket 是独立的协议,复杂性稍高。
- 浏览器支持:SSE 在大多数现代浏览器中支持得较好,但不支持 IE;WebSocket 也有广泛的支持。
- 自动重连:SSE 具有内置的自动重连机制,WebSocket 需要手动处理重连逻辑。
SSE 是非常适合用于实时数据推送的轻量级解决方案。
fetchEventSource
fetchEventSource 是一个用于处理 Server-Sent Events (SSE) 的 JavaScript 函数,可以简化接收服务器端流式事件的操作。
fetchEventSource 通常是在实现 SSE 时作为替代 EventSource 使用的库或方法,如一些库(比如 fetch-sse 或 @microsoft/fetch-event-source),为你提供更灵活的 SSE 处理方案。
主要功能
- 接收流数据:
fetchEventSource与EventSource类似,用于处理服务器推送的文本流数据。 - 自动重连:可以实现自动重连机制,当连接断开时,它会尝试自动重连。
- 自定义控制:相比原生的
EventSource,fetchEventSource可以提供更多对请求和响应的控制,如更灵活的请求头、控制自动重连的行为等。
示例:@microsoft/fetch-event-source 使用
安装:
首先通过 npm 安装这个包:
npm install @microsoft/fetch-event-source
基本用法:
下面是一个使用 fetchEventSource 接收 SSE 的示例:
import { fetchEventSource } from '@microsoft/fetch-event-source';
async function connectToSSE() {
await fetchEventSource('http://localhost:3000/sse', {
method: 'GET',
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Accept': 'text/event-stream'
},
// SSE链接成功时触发
onopen(response) {
if (response.ok && response.headers.get('content-type') === 'text/event-stream') {
console.log("Connection established.");
} else if (response.status >= 400 && response.status < 500 && response.status !== 429) {
console.error("Client error, cannot establish connection.", response.status);
}
},
// 当服务端推送新消息时触发。`event.data` 包含服务器发送的数据
onmessage(event) {
console.log("New message:", event.data);
// Process the received event data here
},
// 当发生错误或连接失败时触发。
onerror(err) {
console.error("Error:", err);
},
// 当连接被关闭时触发
onclose() {
console.log("Connection closed by the server.");
}
});
}
connectToSSE();
自动重连机制:
fetchEventSource 还可以处理自动重连,当连接中断时,它会在合适的时间重新连接。这一点和原生的 EventSource 一样,但更可定制化。
与原生 EventSource 的对比:
- 灵活性:
fetchEventSource基于fetch,比原生EventSource更灵活,允许你传递自定义请求头、控制自动重连的逻辑。 - 更好控制:通过事件钩子(如
onopen,onmessage,onclose等),开发者可以更灵活地控制流式数据接收过程。 - 更好的错误处理:可以对连接状态进行更详细的错误处理,特别是客户端和服务器错误之间的区分。
-
适用场景:
- 长连接数据流:例如接收实时股票数据、消息推送、进度条更新等。
- 更好的控制流:需要对请求头、重连策略等有更多自定义需求的情况,比
EventSource提供了更多控制力。
fetchEventSource 是原生 SSE 的一种增强实现,适合需要更多灵活性和自定义行为的场景,尤其是在复杂前端应用中接收服务端推送数据的场景。
WebSocket:
WebSocket 是一种在客户端与服务器之间进行全双工通信的协议,允许双方在单个连接上同时发送和接收数据。它设计用于解决传统 HTTP 协议下的双向通信瓶颈,特别是在需要实时数据更新的应用场景中,如聊天应用、股票行情、游戏和通知系统。
WebSocket 的特点:
- 全双工通信:允许客户端和服务器
在一个连接上同时发送和接收消息,而不像 HTTP 需要轮询或开启多个连接。 - 持久化连接:连接一旦建立后,客户端与服务器之间保持连接,无需像 HTTP 那样每次请求都要重新建立连接。
- 低开销:WebSocket 头部信息较少,相较于 HTTP 的大量头部数据,每次通信的带宽占用更少,尤其在频繁数据交互时有优势。
- 实时性强:WebSocket 提供了低延迟的实时通信能力,特别适合于需要快速更新数据的场景。
WebSocket 的工作原理
1. 建立连接(握手)
WebSocket 是基于 HTTP 的,但在 HTTP 连接建立之后,客户端会发送一个特定的 WebSocket 握手请求,服务器接收并返回相应的握手应答,确认 WebSocket 连接。
握手的过程类似于 HTTP 请求:
- 客户端发送一个
Upgrade请求:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
- 服务器返回一个
Switching Protocols的响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
这一步骤完成后,HTTP 协议将被升级为 WebSocket 协议,客户端和服务器可以在同一个连接上进行双向通信。
2. 数据传输
一旦 WebSocket 连接建立,客户端和服务器就可以使用 WebSocket 帧格式传输数据。每个 WebSocket 消息被分成多个帧,帧是 WebSocket 协议中的基本数据单位。
WebSocket 的数据传输有以下两种主要格式:
- 文本帧:可以传递 UTF-8 编码的文本数据。
- 二进制帧:可以传递二进制数据(如文件、图片等)。
每个 WebSocket 帧的结构如下:
- FIN(1 位):表示这是不是消息的最后一帧。
- Opcode(4 位):定义帧的类型(文本帧、二进制帧、关闭帧、Ping/Pong)。
- Payload Length:数据负载的长度。
- Payload Data:实际的数据内容。
3. 关闭连接
当通信结束时,任意一方(客户端或服务器)都可以发送一个关闭帧来请求关闭连接。收到关闭帧后,另一方必须响应一个关闭帧,然后断开连接。
WebSocket vs HTTP
| 特性 | WebSocket | HTTP |
|---|---|---|
| 连接模型 | 全双工连接,持续连接 | 半双工,客户端发起请求,服务器响应后关闭连接 |
| 协议层 | 位于 TCP 层之上,协议是独立的 | 基于请求-响应模式的应用层协议 |
| 实时性 | 适合实时、低延迟的通信场景 | 需通过轮询或长连接等机制模拟实时性 |
| 传输数据 | 允许传输文本和二进制数据 | 主要用于传输文本(如 JSON,HTML,XML 等) |
| 头部开销 | 一次握手,后续数据传输开销很小 | 每个请求/响应都带有完整的头部信息 |
| 长连接 | 是的,长时间保持连接 | 默认不支持长连接,需通过 HTTP/2 或长轮询等方式 |
| 常见应用场景 | 聊天应用、游戏、实时通知、金融行情等实时应用 | REST API、资源请求、网页加载等 |
WebSocket 应用场景
- 在线聊天/消息传递: WebSocket 是聊天应用和消息系统的理想选择,因为它支持实时的消息传递,延迟低且不需要轮询。
- 实时通知: 像股票行情、体育比赛比分等应用,要求数据能够实时推送给用户,WebSocket 可以保持长连接,服务器可以主动推送数据给客户端。
- 多人协作/游戏: 在多人协作应用(如 Google Docs)或多人在线游戏中,WebSocket 可以用于保持多个客户端的同步,支持低延迟的双向通信。
- 物联网(IoT) : WebSocket 在 IoT 设备之间进行通信也有广泛应用,它允许设备与服务器之间保持实时的数据传输。
- 实时数据流: 比如实时视频流或音频流,WebSocket 提供了数据的低延迟传输方式。
WebSocket API 使用示例
以下是浏览器中使用 WebSocket 的基本流程,主要分为创建连接、发送消息、接收消息、关闭连接。
// 创建 WebSocket 连接,`url` 为服务器的 WebSocket 地址。
const socket = new WebSocket('ws://example.com/socket');
// 连接成功时触发,可以在这里发送初始消息。
socket.onopen = function(event) {
console.log('WebSocket connection opened.');
// 通过 WebSocket 连接发送消息
socket.send('Hello Server!');
};
// 接收消息时触发,可以在这里处理服务器发来的数据。
socket.onmessage = function(event) {
console.log('Received message from server:', event.data);
};
// 连接关闭时触发,可以在这里进行清理操作
socket.onclose = function(event) {
console.log('WebSocket connection closed.', event);
};
// 发生错误时触发
socket.onerror = function(error) {
console.log('WebSocket error:', error);
};
WebSocket 服务器端实现
在服务器端,使用 WebSocket 通信也很简单。以下是使用 Node.js 和 ws 库实现 WebSocket 服务器的示例:
- 安装
ws:
npm install ws
- 创建 WebSocket 服务器:
const WebSocket = require('ws');
// 创建 WebSocket 服务器,监听指定端口
const ws = new WebSocket.Server({ port: 8080 });
ws.on('connection', (ws) => {
console.log('Client connected.');
// 监听消息,服务器接收到客户端消息时触发
ws.on('message', (message) => {
console.log('Received:', message);
// 回发消息,服务器向客户端发送消息
ws.send('Message received: ' + message);
});
// 连接关闭时
ws.on('close', () => {
console.log('Client disconnected.');
});
});
console.log('WebSocket server started on ws://localhost:8080');
WebSocket 的局限性
- 浏览器兼容性:虽然现代浏览器都支持 WebSocket,但一些较旧的浏览器(如 IE 10 以下)可能不支持。
- 防火墙和代理:有些防火墙和代理服务器可能不支持 WebSocket,因为 WebSocket 使用的是非标准的 TCP 协议,不是所有的中间设备都能处理。
- 复杂性:与 REST API 相比,WebSocket 需要额外的状态管理和连接管理代码,特别是在长连接、断线重连、错误处理等方面。