简介
Server-sent Events (SSE) 是一个独立于 WebSocket 的 HTML5 规范,它使用 HTTP 协议来实现服务器到客户端的事件推送,可以让服务器主动向客户端发送数据,而不是需要客户端定时读取。SSE 最常见的使用场景就是实现实时更新消息,比如聊天室、股票行情等。
SSE 基于常规的 HTTP 连接,通过浏览器与服务器维持一个单向的持久连接,浏览器向服务器发送一个 HTTP 请求,服务器的响应中设置了正确的 MIME 类型,然后保持连接打开,这样服务器可以发送任意数量的消息,直到客户端关闭连接或者网络连接中断。
本文将详细介绍 SSE 的实现方法和最佳实践,并提供一个完整的示例代码。
实现 SSE 的服务器端代码
在服务器端,需要设置正确的响应头 Content-Type 为 text/event-stream,其它的 HTTP 头都与标准的 HTTP 请求相同。下面是一个 Node.js 的 SSE 服务器实现:
const http = require('http'); const fs = require('fs'); const server = http.createServer((req, res) => { if (req.url === '/') { res.writeHead(200, { 'Content-Type': 'text/html' }); fs.createReadStream('./index.html').pipe(res); } else if (req.url === '/events') { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); const id = (new Date()).toLocaleTimeString(); setInterval(() => { res.write(`id: ${id}\nevent: ping\ndata: ${new Date()}\n\n`); }, 5000); req.socket.on('close', () => { clearInterval(intervalId); console.log(`Connection closed: ${id}`); }); } else { res.writeHead(404); res.end(); } }); server.listen(3000, () => { console.log('Server started on http://localhost:3000'); });
代码中,当客户端通过浏览器访问 http://localhost:3000/ 时,服务器会返回 index.html 页面;当客户端访问 http://localhost:3000/events 时,服务器会发送一条事件数据,该事件类型为 ping,发送间隔为 5 秒。
客户端代码示例
在客户端,需要使用 JavaScript 来建立 SSE 连接,并监听服务器推送过来的消息。以下代码演示 SSE 的基本用法:
const eventSource = new EventSource('/events'); eventSource.onopen = e => { console.log('Connection opened'); }; eventSource.onmessage = e => { console.log(e.data); }; eventSource.onerror = e => { console.error('Connection error'); };
当 SSE 连接建立后,客户端通过 onmessage 方法监听服务器发送的事件数据。需要注意的是,SSE 连接在默认情况下是不会自动重连的,如果需要自动重连,可以添加如下代码:
eventSource.onerror = e => { if (eventSource.readyState == EventSource.CLOSED) { console.error('Connection closed'); } else { console.error('Connection error'); } setTimeout(() => { eventSource = new EventSource('/events'); }, 1000); };
SSE 最佳实践
根据事件类型发送消息
在服务端发送事件时,可以通过指定事件类型来让客户端更加清晰地处理不同类型的事件:
res.write(`event: myEvent\ndata: ${JSON.stringify({ message: 'Hello world' })}\n\n`);
客户端在接收到事件时可以根据类型做出不同的处理:
eventSource.addEventListener('myEvent', e => { console.log(e.data.message); });
使用 JSON 序列化数据
为了提高 SSE 的兼容性,建议在发送数据时使用 JSON 格式来序列化数据:
res.write(`data: ${JSON.stringify({ message: 'Hello world' })}\n\n`);
在客户端接收到数据后,可以通过 JSON.parse 方法进行反序列化:
eventSource.onmessage = e => { const data = JSON.parse(e.data); console.log(data.message); };
启用 gzip 压缩
由于 SSE 的数据传输是基于文本的,可以通过启用 gzip 压缩来减少传输的数据量,从而提升 SSE 的性能。服务器端需要设置响应头 Content-Encoding 为 gzip,并将响应数据进行压缩:
const zlib = require('zlib'); // Write the response. res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Content-Encoding': 'gzip' }); const stream = fs.createReadStream('myData.txt', 'utf8'); stream.pipe(zlib.createGzip()).pipe(res);
使用跨域资源共享 (CORS)
如果服务端和客户端不在同一个域下,需要使用跨域资源共享 (CORS) 来解决跨域问题:
服务端代码:
res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*' });
客户端代码:
const eventSource = new EventSource('http://example.com/events');
总结
Server-sent Events 是一种轻量级的实时消息推送技术,与 WebSocket 相比,它的开销更小、易于实现和维护。通过本文的介绍和示例代码,你应该能够了解如何使用 SSE 实现自定义消息推送,并掌握 SSE 的最佳实践。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/659119faeb4cecbf2d656d8e