SSE(Server-Sent Events): 通俗说就是一种基于HTTP的,以流的形式由服务端持续向客户端发送数据的技术。
服务器发送事件,是基于http
协议,和WebSocket
全双工通道(web
端和服务端相互通信)相比,SSE
是单通道(服务端推送数据到客户端)。是HTML 5
规范的一个组成部分。
在web
端消息推送功能中,由于传统的HTTP
协议是由客户端主动发起请求,服务端才会响应。基本的ajax
轮询技术便是如此。而在SSE
中,浏览发送一个请求给服务端,通过响应头中的Content-Type:text/event-stream
等向客户端声名这是一个长连接,发送的是流数据,这样客户端就不会关闭连接,一直等待服务端发送数据。
//如下,每个事件通过空行来分隔,对于每一行来说, 冒号前表示的是该行的类型,冒号后面是对应的值
data: first event //类型为data, 表示改行表示的是数据,以data开头的行可以多次出现,都是该事件中的数据
data: second event
id: 001
event: myevent //类型为event,表示用来声名事件类型,浏览器收到该事件时,会产生对应类型的事件.如果没有该字段,会产生默认类型
data: third event
id: 002 //id,用来声名事件标识符
: this is a comment //类型为空白,表示该行是注释,回在处理时被忽略
data: fourth event
data: fourth event continue
retry: 1000 //表示该行用来声明浏览器在连接断开之后进行再次连接之前的等待时间
如果服务器返回的数据中包含了事件标识符,浏览器会记录最后一次接收的事件的标识符。如果与服务器的连接中断,当浏览器再次进行连接时,会通过http
头Last-Event-ID
来声明最后一次接收的事件的标识符。服务器端可以通过浏览器发送的事件标识符来确定从哪个事件来继续连接。
对于服务端返回的响应,浏览器端需要在js
中使用EventSource
对象来进行处理。EventSource
使用的是标准的事件监听方式:只需要在对象上添加对应的处理方法即可。EventSource
提供的三个标准事件:
名称 | 说明 | 事件处理方法 |
---|---|---|
open | 成功与服务器建立连接时产生 | onopen |
message | 收到服务器发送的事件时产生 | onmessage |
error | 出现错误时产生 | onerroe |
如果服务器端返回自定义类型的事件,对于这些事件可以使用addEventListener
方法来添加相应的事件处理方法,例如:
let es = new EnventSource('events');
//标准事件
es.onmessage = function (e) {
console.log(e.data);
};
//自定义事件
es.addEventListener('myevent', function(e){
console.log(e.data);
})
id
字段作为内部属性 Last-Event-ID
储存起来。SSE
默认支持断线重连机制,在连接断开时会 触发EventSource
的error
事件,同时自动重连。再次连接成功时 EventSource
会把Last-Event-ID
属性作为请求头发送给服务器,这样服务器就可以根据这个Last-Event-ID
作出相应的处理。这里需要注意⚠️的是,id
字段不是必须的,服务器有可能不会在消息中带上id
字段,这样子客户端就不会存在Last-Event-ID
这个属性。所以为了保证数据可靠,我们需要在每条消息上带上id
字段。SSE
不支持IE
浏览器服务端:
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
let data = fs.readFileSync('./1.json');
// 服务器声明接下来发送的是事件流
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
});
// 发送消息
setInterval(() => {
res.write('event: slide1\n'); // 事件类型
res.write(`id: ${+new Date()}\n`); // 消息 ID
res.write(`data:${data} \n`); // 消息数据
res.write('retry: 10000\n'); // 重连时间
res.write('\n\n'); // 消息结束
}, 5000);
}).listen(2000);
客户端:
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(`message: ${e.data}`);
}, false);
// 自定义 EventHandler,在收到 event 字段为 slide 的消息时触发
source.addEventListener('slide1', e => {
console.log(`slide1: ${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');
}
}
为了减少服务器的开销,我们也可以有目的的断开和重连简单的办法是服务器发送一个 关闭消息并指定一个重连的时间戳,客户端在触发关闭事件时关闭当前连接并创建 一个计时器,在重连时把计时器销毁。
客户端定时向服务器发送请求,如果服务端有数据就返回,没有则返回空数据
优点:实现简单
缺点:轮询时间确定,间隔太长时效性差,间隔太短,会造成大量无效的请求
socket
并不是一个新的协议,它只是为了便于程序员进行网络编程而对tcp/ip
协议族通信机制的一种封装。
websocket
协议是html5
规范中的一部分。为客户端和服务端提供了一种全双工通信机制。他是一种全新的应用层协议。通常表示为ws://echo.websocket.org/?encoding=text
,可以看出除了协议名和http
不同之外,他的表示和传传统的url
一致。
Websocket
使用服用了HTTP
的握手通道。具体指的是,客户端通过http
请求与WebSocket
服务端协商升级协议。协议升级完成后,和后续的数据交换遵照Websocket
协议。那么客户端时如何进行协议升级呢?
WebSocket建立连接的过程
websocket
客户端与服务端一旦建立连接,后续的操作都是基于数据帧传递。
WebSocket
为了保持客户端、服务端的实时双向通信,需要确保客户端、服务端之间的TCP
道保持连接没有断开。然而,对于长时间没有数据往来的连接,如果依旧长时间保持着,可能会浪费包括的连接资源。但不排除有些场景,客户端、服务端虽然长时间没有数据往来,但仍需要保持连接。
这个时候,可以采用心跳来实现:
发送方->接收方:ping
接收方->发送方:pong
ping、pong
的操作,对应的是WebSocket
的两个控制帧,opcode
分别是0x9、0xA
。
例如:WebSocket
服务端向客户端发送ping
,只需要如下代码(采用ws
模块)ws.ping('', false, true);