介绍
服务器向浏览器推送信息,除了 WebSocket,还有一种方法:Server-Sent Events(以下简称 SSE)。
严格地说,HTTP 协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming)。也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时客户端不会关闭连接,会一直等着服务器发过来的新的数据流,文件下载就是基于这种原理。
SSE 就是利用这种机制,使用流信息向浏览器推送信息。它基于 HTTP 协议,目前除了 IE/Legacy Edge,其他浏览器都支持。
SSE 与 WebSocket
SSE 与 WebSocket 作用相似,都是建立浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息。
- WebSocket: 全双工通道,可以双向通信。
- SSE: 单向通道,只能服务器向浏览器发送,因为流信息本质上就是下载。如果浏览器向服务器发送信息,就变成另一次 HTTP 请求。
优点
- SSE 使用 HTTP 协议,现有的服务器软件都支持。WebSocket 是一个独立协议,可能存在无法升级的情况。
- SSE 属于轻量级,使用简单;WebSocket 协议相对复杂。
- SSE 默认支持断线重连,WebSocket 需要自己实现。
服务器 API
- 指定响应头
1 2 3
| Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive
|
- 每次发送一个数据包,每个数据包之间用
\n\n
分隔,一个数据包内部由若干行文本组成,每行的格式是:
field 的取值可以是:data
、event
、retry
、id
- data: 表示传输的数据
- event: 表示下一行 data 将触发的 client 的事件名,不指定则触发
message
事件
- retry: 意外断连后重连的时间间隔
- id: 数据标识符,用于在意外断连时,浏览器会自动在请求头中加上
Last-Event-ID
,帮助服务器重建连接
示例
1 2 3 4 5 6 7 8
| retry: 10000\n\n
event: connectTime\n data: Connection open…\n\n
data: Hello World!\n\n
: This is a comment\n\n
|
浏览器 API
EventSource 对象
- 实例化
1
| var source = new EventSource(url, { withCredentials: true });
|
- 获取连接的状态
- 监听事件
1 2 3 4 5
| source.onopen; source.onmessage; source.onerror;
source.close();
|
- demo
原生 EventSource 的缺点
- 只支持发 GET 请求
- MS 系浏览器(IE、legacy Edge)不支持
基于 fetch 改造
- 使用 fetch API 流式处理请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const reader = response.body.pipeThrough(new TextDecoderStream()).getReader(); await reader.read();
const reader = response.body.getReader(); const textDecoder = new TextDecoder(); const result = await reader.read();
let str = ""; str += textDecoder.decode(result.value);
var chunk = value; var str = "";
for (var i = 3; i < chunk.byteLength; i++) { str += String.fromCharCode(chunk[i]); }
|
- 参照原生,手动解析响应的 body(demo)