目前服务端推送的方式有:

  • HTTP长连接模拟
  • websocket
  • Server-Sent Events(SSE基于HTML5)
  • HTTP2.0 服务端推送

HTTP长连接模拟

通常有两种实现:

  • 服务端收到请求后,一直hold,直到有数据更新时才给出相应,并关闭连接,由客户端发起第二次长连接
  • 服务端有更新后一直不关闭连接,用流的方式,不断发送后续更新的数据

websocket

最为“正规的”全双工实时通信,这里不多介绍;

Server-Sent Events(SSE基于HTML5)

本质上依然是单项通信,只能由服务端发起,服务端响应一个数据流,不断的发送数据,如视频播放,文件流下载;
基于客户端建立EventSource对象,服务端以text/event-stream 格式发送事件;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
if (window.EventSource) {
// 建立 EventSource 对象链接服务器
const source = new EventSource('http://localhost:2000');

// 链接成功后会触发 open 事件
source.addEventListener('open', () => {
console.log('Connected');
}, false);

// 服务器发送信息到客户端时,若是没有 event 字段,默认会触发 message 事件
source.addEventListener('message', e => {
console.log(`data: ${e.data}`);
}, false);

// 自定义 EventHandler,在收到 event 字段为 slide 的消息时触发
source.addEventListener('slide', e => {
console.log(`data: ${e.data}`); // => data: 7
}, false);

// 链接异常时会触发 error 事件并自动重连
source.addEventListener('error', e => {
if (e.target.readyState === EventSource.CLOSED) {
console.log('Disconnected');
} else if (e.target.readyState === EventSource.CONNECTING) {
console.log('Connecting...');
}
}, false);
} else {
console.error('Your browser doesn\'t support SSE');
}

服务端text/event-stream:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const http = require('http');

http.createServer((req, res) => {

// 服务器声明接下来发送的是事件流
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
});

// 发送消息
setInterval(() => {
res.write('event: slide\n'); // 事件类型
res.write(`id: ${+new Date()}\n`); // 消息 ID
res.write('data: 7\n'); // 消息数据
res.write('retry: 10000\n'); // 重连时间
res.write('\n\n'); // 消息结束
}, 3000);

// 发送注释保持长链接
setInterval(() => {
res.write(': \n\n');
}, 12000);
}).listen(2000)

为什么有了websocket还要有SSE?

  • websocket全双工,SSE是以客户端发起的单向通信,如:股票、新闻推送全依赖服务端更新的场景;
  • SSE使用HTTP协议,无需而外的升级websocket复杂协议

HTTP2.0 服务端推送

这个属于HTTP2.0的细节,最容易被忽视,其能够检测html文档中的css、js等静态文件,而不需要等浏览器解析到,再而外发起请求,属于服务端推送在“性能优化”场景的应用。