Server-sent events(SSE)& EventSource 客户端使用与服务器基础实现(基于Node.js)
Posted Naisu Xu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Server-sent events(SSE)& EventSource 客户端使用与服务器基础实现(基于Node.js)相关的知识,希望对你有一定的参考价值。
目的
传统的Web前后台通讯主要是使用HTTP方式进行的,通常是前台需要什么就主动向后台请求,后台无法直接向前台发送数据。通常后台数据如果频繁变动的话前台主要靠 轮询 或是 长连接 方式来处理,两种方式相对来说都不是很优雅。
而到了现在有两种方案来处理这个问题: WebSocket 和 Server-sent events(SSE) 。 WebSocket 可以实现实时的双向通讯,功能上来说是非常强的,不过相对于HTTP而言是一种另起炉灶的技术。Server-sent events 则是在HTTP之上扩展出来的功能,有点像前面提到的 长连接 ,但是这是原生的标准,功能上更加完善,使用起来也更加方便。这篇文章将对 Server-sent events 相关内容做个说明。
基础说明
Server-sent events 是H5中加入的功能,它是在HTTP上扩展出来的功能,使得服务器可以主动发送数据给客户端。在客户端使用 EventSource 接口来处理 Server-sent events 。
Server-sent events 最核心的其实就是新增了一个 MIME type : text/event-stream
。看名字就是知道这是一个流,只要不结束的的话就一直可以传数据。
实际使用中只要客户端主动发起访问接口,建立连接后就不用管了,服务器会在需要的时候主动推送消息。
Server-sent events 这种原生的功能有一个好处是浏览器端默认会自动重连。
客户端使用
客户端使用 EventSource 接口来处理 Server-sent events 。使用方法主要如下:
var es = new EventSource("/sse"); // 声明EventSource对象并连接url
es.onmessage = (e) => {} // 收到服务器消息时触发
es.onopen = (e) => {} // 连接建立时触发
es.onerror = (e) => {} // 发生错误时触发
// es.close(); // 关闭EventSource连接
// console.log(es.url);
// console.log(es.readyState); // 连接状态: 0 - connecting; 1 - open; 2 - closed;
客户端基本的使用是比较简单的,演示需要结合下面服务器进行。
对于前端来说直接使用JS的EventSource接口就可以使用SSE了,对于其它语言作为客户端来说可能没有现成的方法可用,但其实要用上也挺简单,使用 GET
方法访问链接,并在头文件中包含下面属性即可(只是建议,并不是必须):
accept: text/event-stream
cache-control: no-store
connection: keep-alive
这样就可以建立起连接了,之后等待并处理来自服务器的数据就行。
服务器实现
Server-sent events 是在HTTP上扩展出来的功能,所以服务器实现只需要在HTTP服务器的基础上稍作处理即可,最主要的就是设置响应头中 MIME type 为 text/event-stream
。下面是个最简单的例子:
const http = require('http')
const server = http.createServer((req, res) => {
// 访问链接 /sse
if (req.url == '/sse') {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
});
res.write("data: " + 'connected' + "\\n\\n");
// 服务器定期向客户端主动发消息
interval = setInterval(function () {
res.write("data: " + 'hello world' + "\\n\\n");
}, 5000);
// req.on('close', () => {}) // 客户端断开连接时触发
return
}
// 其它任何链接都返回网页
res.statusCode = 200
res.end(`
<script>
var es = new EventSource("/sse");
es.onmessage = (e) => { console.log(e); }
es.onopen = (e) => { console.log(e); }
es.onerror = (e) => { console.log(e); }
</script>
`)
})
server.listen(80, '127.0.0.1', () => {
console.log(`Server running at http://127.0.0.1/`)
})
上面例子中服务器在收到EventSource的连接请求后返回了 200
状态码,并在响应头中加入了 text/event-stream
,这些内容发送给客户端后连接就算建立完成了。之后只要有需要时服务器再向客户端发送消息即可,上面例子中使用定时器来模拟发送消息。
Server-sent events中服务器发送的数据必须是 UTF-8 编码的文本,还有一定的格式要求:
- 数据必须是一段一段发送,每端之间必须有空行
比如上面例子中的\\n\\n
,后一个换行符就提供 了空行 - 数据前有特定的标识字段
比如上面例子中发送消息使用data: 数据文本
上面提到的标识字段可选值如下:
data:
event:
定义事件类型,未设置此项的情况下发送数据会触发 message 事件,即默认的 onmessage 方法;id:
标识当前这段信息用的编号,客户端可以通过 lastEventId 属性来获取,如果连接断开,客户端在建立重连的时候会在请求头中的 Last-Event-ID 字段中填写最近的 id ;retry:
指定浏览器重新发起连接的时间间隔;:
会被忽略的信息,网络中的服务器可能会关闭长时间(比如15秒)未传输数据的连接,可以使用该方式发送消息来保持连接;
针对上面的一些内容可以使用下面方式进行测试:
const http = require('http')
const server = http.createServer((req, res) => {
// 访问链接 /sse
if (req.url == '/sse') {
console.log(req.headers);
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
});
res.write("retry: 10000\\n");
res.write("event: connected\\n");
res.write("data: connected\\n\\n");
// 服务器定期向客户端主动发消息
interval = setInterval(function () {
res.write("data: " + "hello world" + "\\n");
res.write("id: " + "aaa" + "\\n\\n");
res.write("event: " + "naisu" + "\\n");
res.write("data: " + "233~~~" + "\\n\\n");
}, 5000);
return
}
// 其它任何链接都返回网页
res.statusCode = 200
res.end(`
<script>
var es = new EventSource("/sse");
es.onmessage = (e) => { console.log(e); } // 使用data取数据
es.addEventListener("connected", (e) => { console.log(e) }, false); // 监听自定义的事件
es.addEventListener("naisu", (e) => { console.log(e) }, false); // 监听自定义的事件
</script>
`)
})
server.listen(80, '127.0.0.1', () => {
console.log(`Server running at http://127.0.0.1/`)
})
注意事项
Server-sent events 虽然简单,但使用时还有一些事项需要注意:
- 浏览器连接数量限制
大多数浏览器对于同一个IP或域名都有连接数量限制(比如chrome上限是6个),对于普通的HTTP请求,连接一下又断开了这没什么问题,但 Server-sent events 是长连接,超过6个就无法再连接了; - 长时间未传输数据连接被关闭
真实环境中数据传输过程中可能会经过代理服务器等网络节点,这些节点可能会关闭长时间未传输数据的连接; - 数据不实时传输
真实环境中数据传输过程中可能会经过代理服务器等网络节点,这些节点可能会缓存数据,达到一定数量后才转发,可以关闭相关缓存以提高实时性;
总结
Server-sent events的使用总的来说挺简单的,更多内容可以参考下面链接:
https://html.spec.whatwg.org/multipage/server-sent-events.html
http://www.ruanyifeng.com/blog/2017/05/server-sent_events.html
以上是关于Server-sent events(SSE)& EventSource 客户端使用与服务器基础实现(基于Node.js)的主要内容,如果未能解决你的问题,请参考以下文章
Long-Polling, Websockets, SSE(Server-Sent Event) 之间有什么区别?
Server-sent events(SSE)& EventSource 客户端使用与服务器基础实现(基于Node.js)