最新更文通知.png
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~


仅接收服务器数据的长链接方案

在项目中或多或少有一些场景会使用到长链接,除去一些聊天的项目(双向数据交换)外,更多见的如:排行榜定时刷新,大屏数据动态刷新等,往往我们只是从服务器来获取数据进行展示即可,原来除了使用定时器来发送请求获取数据外还能想到的就是WebSocket了,因为WebSocket从0集成的成本相对较大,还需要处理一些状态,所以了解到了EventSource类。仅支持从服务器发送文本数据到客户端,用的也是常规的HTTP协议,最最关键是简单。

EventSource 是服务器推送的一个网络事件接口。一个EventSource实例会对HTTP服务开启一个持久化的连接,以text/event-stream 格式发送事件, 会一直保持开启直到被要求关闭。(摘自MDN)

Server-Sent Events 规范描述了一个内建的类 EventSource,它能保持与服务器的连接,并允许从中接收事件。与 WebSocket 类似,其连接是持久的。

EventSource介绍:


响应类型 Content-Type: text/event-stream 状态码200
消息格式 data: 消息文本
1. 冒号和消息文本间的空格可选
1. 实际开发消息文本大多沿用Json格式
支持跨域 EventSource(“http://localhost:8080/digits”); 服务器设置Access-Control-Allow-Origin
重新连接 自动连接
1. Content-Type不正常不重连
1. 状态码非301,307,200 和 204不重连
1. 关闭连接后需重新实例化ES对象打开
关闭连接 客户端关闭:eventSource.close()
1. 浏览器主动关闭
1. 服务器返回204状态码关闭
消息ID 客户端接收:eventSource.lastEventId
服务端接收:header的Last-Event-ID
连接状态 EventSource.CONNECTING = 0;
EventSource.OPEN = 1;
EventSource.CLOSED = 2;

1. 连接中或者重连中
1. 已连接
1. 连接已关闭

补充:
image.png

客户端代码:


封装EventSourceClient:

  1. export default class EventSourceClient {
  2. constructor(url) {
  3. this.url = url;
  4. this.eventSource = null;
  5. }
  6. // 建立连接
  7. connection(openCallback, messageCallback, errorCallback) {
  8. this.eventSource = new EventSource(this.url);
  9. this.eventSource.onopen = openCallback;
  10. this.eventSource.onmessage = messageCallback;
  11. this.eventSource.onerror = function (e) {
  12. if (this.readyState == EventSource.CONNECTING) {
  13. console.log(`Reconnecting (readyState=${this.readyState})...`);
  14. } else {
  15. errorCallback && errorCallback(e)
  16. }
  17. };
  18. }
  19. // 断开连接
  20. disconnect() {
  21. this.eventSource && this.eventSource.close();
  22. }
  23. addAction(action, fn) {
  24. this.eventSource && this.eventSource.addEventListener(action, fn);
  25. }
  26. }

使用EventSourceClient:

  1. <script type="module">
  2. import EventSourceClient from './event-source-client.js'
  3. const url = 'http://localhost:8080/digits'
  4. window.esc = new EventSourceClient(url);
  5. </script>
  6. <script>
  7. function start() {
  8. window.esc.connection((e) => {
  9. console.log('建立连接', e);
  10. }, (e) => {
  11. console.log('接收数据', e.data);
  12. }, (e) => {
  13. console.log('发生错误', e);
  14. })
  15. window.esc.addAction('bye', (e) => {
  16. console.log('自定义动作', e.data);
  17. })
  18. }
  19. function stop() {
  20. window.esc.disconnect();
  21. console.log('关闭连接');
  22. }
  23. </script>

服务端代码实现:


  1. let http = require('http');
  2. function onDigits(req, res) {
  3. // 设置请求头
  4. res.writeHead(200, {
  5. 'Cache-Control': 'no-cache',
  6. // 支持跨域请求
  7. "Access-Control-Allow-Origin": "*",
  8. // 返回类型为text/event-stream
  9. 'Content-Type': 'text/event-stream; charset=utf-8',
  10. });
  11. let i = 0;
  12. let timer = setInterval(write, 1000);
  13. write();
  14. function write() {
  15. i++;
  16. if (i == 4) {
  17. res.write('event: bye\ndata: bye-bye\n\n');
  18. clearInterval(timer);
  19. res.end();
  20. return;
  21. }
  22. res.write('data: ' + i + '\n\n');
  23. }
  24. }
  25. function accept(req, res) {
  26. if (req.url == '/digits') {
  27. onDigits(req, res);
  28. }
  29. }
  30. http.createServer(accept).listen(8080);

执行演示:


image.png


欢迎关注我的公众号“前端小鑫同学”,原创技术文章第一时间推送。
699fd99d49e52dde146cc32f28bf9c1.bmp