SSE:使用HTTP做服务端数据推送的技术及其他通信技术

文章目录

  • 一、SSE
    • 使用场景
    • 服务端响应示例
    • 浏览器处理服务器返回数据
    • SSE使用注意事项
    • 使用示例
  • 二、轮询
  • 三、WebSocket
    • 什么是Socket?什么是WebSocket?
    • 那么他是如何建立连接?如何进行数据交换?如何维持连接呢?

SSE(Server-Sent Events): 通俗说就是一种基于HTTP的,以流的形式由服务端持续向客户端发送数据的技术。

一、SSE

服务器发送事件,是基于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 //表示该行用来声明浏览器在连接断开之后进行再次连接之前的等待时间 

如果服务器返回的数据中包含了事件标识符浏览器会记录最后一次接收的事件的标识符。如果与服务器的连接中断,当浏览器再次进行连接时,会通过httpLast-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);
})

SSE使用注意事项

  1. 如何保证数据的完整性
    客户端在每次接收到消息时,会把消息的id 字段作为内部属性 Last-Event-ID储存起来。SSE默认支持断线重连机制,在连接断开时会 触发EventSourceerror事件,同时自动重连。再次连接成功时 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');
        } 
    }

为了减少服务器的开销,我们也可以有目的的断开和重连简单的办法是服务器发送一个 关闭消息并指定一个重连的时间戳,客户端在触发关闭事件时关闭当前连接并创建 一个计时器,在重连时把计时器销毁。

二、轮询

客户端定时向服务器发送请求,如果服务端有数据就返回,没有则返回空数据

优点:实现简单
缺点:轮询时间确定,间隔太长时效性差,间隔太短,会造成大量无效的请求

三、WebSocket

什么是Socket?什么是WebSocket?

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);

你可能感兴趣的:(前端,SSE)