2024 年服务端推技术:Server-Sent Events 还是 WebSocket?
今天是坚持日更的第156天,如果本文对您有帮助,记得点击关注、点赞、在看支持我
WebSocket 和 Server-Sent Events(以下简称 SSE)常用于实时应用中,在这些应用中,高效、快速的传输数据是一项关键要求。随着时间的推移,人们对应用程序中实时体验的期望也在不断提高,技术也在不断改进,对可能实现的功能的理解也在不断加深。
本文将深入比较两种流行的实时协议 WebSocket 和 Server-Sent Events。下面将带您了解这两种协议各自的功能、优缺点以及快速实践。
1.什么是 WebSocket?
WebSocket 是一种建立在设备 TCP/IP 协议栈之上的传输层协议,可在服务器和浏览器之间提供全双工、低延迟、事件驱动的连接。这非常适合实时应用程序,因为在最初的 HTTP 握手后,单个 WebSocket 连接就能处理单个会话中的所有信息,而无需进一步握手。会话结束后,连接应作为清理工作的一部分关闭。
WebSocket 的最大特点就是服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
1.1.WebSocket 主要功能概述
使用自定义的 ws 协议传输信息,其工作级别低于 HTTP。 连接是双向的,因此 WebSocket 对于需要从服务器读取和向服务器写入数据的应用程序(如聊天应用程序或多人游戏)非常有用。 从头开始实现 WebSocket 可能比较复杂,但有多种库可以帮助实现。 基于事件;检索信息无需轮询,有助于减少延迟。 RFC 6455 - WebSocket 协议于 2011 年在 IETF 网站上发布,目前所有主流浏览器都支持该协议。
1.2.WebSocket 的优缺点
1)WebSocket 的优点:
回退到 HTTP:如果您需要回退到 HTTP 以克服下面提到的 WebSocket 的缺点,可以使用基于 WebSocket 的流行库(如 Socket.IO)来实现,这些库内置了此类回退功能。 资源效率:由于 WebSocket 是一种低级协议,因此单个 WebSocket 连接可以处理很高的带宽。WebSocket 不使用 "XMLHttpRequest",也不会在每次需要从服务器获取更多信息时发送头信息。这就最大限度地减少了发送到服务器的昂贵数据负载。 WebSocket 提供实时双向通信:由于 WebSocket 提供全双工双向通信通道,因此服务器可以向客户端发送信息,而客户端和服务器也可以同时发送信息。这使得双向、多用户实时应用程序(如聊天室)成为可能,并且性能良好。 数据格式灵活:WebSocket 可以传输二进制数据和 UTF-8,这意味着应用程序可以支持发送纯文本和二进制格式(如图像和视频)。
2)WebSocket 的缺点:
防火墙阻塞:一些具有数据包检查功能的企业防火墙在处理 WebSocket 时会遇到困难(特别是 SophosXG Firewall、WatchGuard 和 McAfee Web Gateway)。 没有重新连接的内置支持:当 WebSocket 连接关闭时(如由于网络问题),客户端不会尝试重新连接服务器,这意味着你需要编写额外的代码来轮询服务器,在服务器可用时重新建立连接。或者,你也可以使用服务器发送的事件(Server-Sent Events),或者使用支持重新连接的库,如 Socket.IO。
1.3.WebSocket 应用实践
1)使用 ws 创建 WebSocket 服务器:
下面的 Node.js 代码片段创建了一个简单的 WebSocket 服务器:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
使用 ws 库创建一个新的 WebSocket 服务器后,我们将连接事件监听器附加到服务器上,以监听连接。一旦发生连接,就会设置一个消息事件监听器来监听消息,并向连接的客户端发送消息。
2)在浏览器中使用 WebSocket:
要从浏览器连接到 WebSocket 服务器,您需要这样做:
const ws = new WebSocket('ws://example.org');
ws.addEventListener('open', () => {
// Send a message to the WebSocket server
ws.send('Hello!');
});
ws.addEventListener('message', event => {
// The `event` object is a typical DOM event object, and the message data sent
// by the server is stored in the `data` property
console.log('Received:', event.data);
});
2.什么是 Server-Sent Events?
Server-Sent Events(SSE)基于服务器发送的 DOM 事件。浏览器可以使用 EventSource 接口订阅由服务器发送的事件流,并在发生新事件时接收更新。EventSource 接受来自特定 URL 的 HTTP 事件流连接,并在检索可用数据时保持连接打开。服务器发送的事件是从服务器推送(而不是拉取或请求)到浏览器的。
Server-Sent Events 是一种标准,它描述了服务器如何在建立初始客户端连接后保持向客户端传输数据。它为 XHR 流提供了一种内存效率高的实现方式。原始 XHR 连接会缓冲整个接收到的响应,直到连接中断,而 SSE 连接则不同,它可以丢弃处理过的信息,而无需将所有信息累积到内存中。
2.1.SSE 主要功能概述
使用 XHR 流通过 HTTP 传输信息。 连接是单向的,因此 SSE 适用于只需从服务器读取数据的应用程序,如实时股票或新闻列表。 实现基于 SSE 的连接并不复杂,但可用的相关库并不多。 基于事件;无需轮询即可截获信息。 服务器发送的事件在 HTML 规范中进行了规定,所有主要浏览器多年来都支持这些事件。
2.2.SSE 的优缺点
1)SSE 的优点:
可多重填充:服务器发送事件可以在尚不支持 JavaScript 的浏览器中使用 JavaScript 进行多填充。这对于向后兼容性非常有用,因为你可以依赖现有的实现,而不必编写替代方案。 内置的重新连接支持:服务器发送事件连接会在连接丢失后重新建立连接,这意味着只需编写较少代码即可实现基本行为。 无防火墙阻挡:在企业防火墙进行数据包检查时,SSE 不会遇到任何问题,这对于支持企业环境中的应用程序非常重要。
2)SSE 的缺点:
数据格式限制。服务器发送事件仅限于传输 UTF-8 消息;不支持二进制数据。 并发连接有限。每个浏览器同一时间只能同时打开六个 SSE 连接。当你想用 SSE 连接打开多个标签页时,这可能会让你特别痛苦。 SSE 是单向的。您只能从服务器向客户端发送信息。虽然这对创建股票行情等只读实时应用程序很有用,但对许多其他类型的实时应用程序来说却很有局限性。
2.3.SSE 应用实践
1)Node.js 服务器:
下面的代码片段创建了一个简单的 Node.js SSE 服务器:
const express = require('express');
const app = express();
const port = 3600;
const fs = require('fs');
const path = require('path');
app.get('/', (req, res) => {
res.set({
'Content-Type': 'text/html'
});
res.write(fs.readFileSync(path.resolve(__dirname, 'index.html')));
res.end();
});
app.get('/stream', (req, res) => {
const { message = '' } = req.query
// 3个请求头重点,需要返回text/event-stream,告知浏览器以何种类型解析
res.set({
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
let step = 0;
// 定时器依次返回message
const time = setInterval(() => {
const data = { message: message[step++]};
// 每个消息以 \n\n分割
res.write(`data: ${JSON.stringify(data)}\n\n`);
if (step > message.length - 1) {
res.end()
clearInterval(time)
}
}, 100);
});
app.listen(port, () => console.log(`Server running at http://localhost:${port}`));
2)浏览器端:
要从浏览器连接到 SSE 服务器,您需要这样做:
// 创建一个EventSource
const eventSource = new EventSource(`/stream?message=${encodeURIComponent(`除了品牌标志设计、产品包装设计、广告宣传和产品研发,Vini Cosmetics在社会责任方面也非常有意义。例如,Vini Cosmetics通过其“Vini Foundation”慈善基金会,为社会做出了积极的贡献。该基金会致力于为儿童和妇女提供教育、医疗和社会福利等方面的支持,帮助他们获得更好的生活和未来。`)}`);
// 监听服务器返回的数据
eventSource.onmessage = function (event) {
const data = event.data;
const res = JSON.parse(data);
document.querySelector('#content').innerHTML += res.message;
};
eventSource.onerror = function () {
eventSource.close();
};
3.WebSocket 和 SSE 主要区别
综合上文中提到的优缺点,下表快速概括了 WebSocket 和 SSE 之间的主要区别:
WebSocket | Server-Sent Events |
---|---|
双向信息传输 | 单向信息传输(服务器到客户端) |
支持二进制和 UTF-8 数据传输 | 仅支持 UTF-8 数据传输 |
支持每个浏览器的大量连接 | 支持每个浏览器的有限连接数(六个) |
不能使用 JavaScript 进行多重填充;需要退回到基本 HTTP 消息 | 可以使用 JavaScript 进行多重填充 |
某些具有数据包检查功能的企业防火墙在处理 | 企业防火墙不会阻塞 |
4.我们应该如何选择?
使用哪种技术取决于您的使用场景,下面我们将介绍这两种技术适合的场景。
1)何时使用 WebSocket:
WebSocket 比 SSE 更复杂,要求更高,需要开发人员投入更多。但您只需投入少量资金,就能获得一个全双工 TCP 连接,该连接适用于多种应用场景。例如,WebSocket 更适用于多人协作和聊天应用程序等用例。虽然技术上可以使用 SSE + AJAX 来实现这些功能,但这种组合可能会导致通信不同步,并且比 WebSocket 所需的工作量更大。
2)为什么使用 SSE 而不是 WebSocket?
对于只需要单向通信(从服务器到客户端)的简单实时用例,服务器发送事件是 WebSocket 的理想替代品。这方面的例子包括股票行情或新闻更新等只读实时应用程序。不过要注意的是,通过 SSE 只能发送 UTF-8 数据(不支持二进制数据)。
3)SSE 与 WebSocket - 孰优孰劣?
SSE 是一种更简单的解决方案,但它不具有可扩展性:如果您的网络应用需求发生变化,最终可能需要使用 WebSocket 对其进行重构。虽然 WebSocket 技术的前期工作更多,但它是一个通用性和可扩展性更强的框架,因此对于可能会随着时间推移添加新功能的复杂应用程序来说,它是一个更好的选择。
5.ChatGPT 用的是 SSE 还是 WebSocket?
在 ChatGPT 最初版本中使用的是 SSE 技术,但是最近切到了 WebSocket,具体原因官方未给说明,猜测的可能原因如下:
改用 Websocket 技术与服务器端通信可以提升通信效率、降低延迟而且支持性更好。 从生命周期来看 Websocket 管理起来更麻烦一些,但其建立连接次数会下降。这也算是一个优化吧,工程上提升复杂度,性能上拿到收益。
关注FED实验室(前端开发实验室)获取更多前端热点资讯、技术实践、面试招聘等精彩内容。