> 文章列表 > [Net]SSE消息推送简介

[Net]SSE消息推送简介

[Net]SSE消息推送简介

文章目录

SSE(Server-Sent Events)是一种服务端到客户端(浏览器)的单向消息推送方式。

SSE网络协议

SSE是基于HTTP协议的,客户端向服务端发起一个请求,建立长连接( keep-alive connection);服务端向客户端发送(应答)不是一次性的包,而是一个数据流。

SSE

客户端

要实现SSE协议,客户端发起的请求头中需要携带:

  • Accept: text/event-stream: 表示可接收事件流类型
  • Cache-Control: no-cache: 禁用任何的事件缓存
  • Connection: keep-alive: 表示正在使用持久连接

如:

GET /sse HTTP/1.1 Accept: text/event-stream Cache-Control: no-cache Connection: keep-alive

SSE默认支持断线重连机制,在连接断开时会触发EventSource的error事件,同时自动重连。

服务端

服务端应答头中需要包含:

  • Content-Type: text/event-stream;charset=UTF-8: 表示标准要求的事件的媒体类型和编码
  • Transfer-Encoding: chunked: 表示服务器流式传输动态生成的内容,因此内容大小事先未知

如:

HTTP/1.1 200 Content-Type: text/event-stream;charset=UTF-8 Transfer-Encoding: chunked

事件

事件采用UTF-8编码的文本消息:

  • 事件之间由两个换行符\\n\\n分隔;
  • 每个事件由一个或多个{key}: {value}字段组成;字段间由单个换行符\\n分隔。
  • 若某行以冒号:开始,客户端应忽略:可用于防止中间代理因超时关闭连接;如:ping

规范中定义了事件的四种字段:

  • retry:表示超时重连间隔(毫秒);
  • data:表示包含的是数据,可多次出现;
  • event:表示事件的类型(若不写,默认为message),浏览器会生成对应类型的事件;
  • id:表示事件标识符;
id: 1
event: chat
retry: 3000
data: firstid: 2
event: chat
retry: 3000
data: second
data: second continue

注意:如果服务器端返回的数据中包含了事件的标识符id,浏览器会记录最近一次接收到的事件的标识符。当浏览器因断开重连时,会通过HTTP头Last-Event-ID来声明最后一次接收到的事件的标识符;服务器端可根据此标识符确定从哪个事件开始来继续连接。

SSE示例

客户端

客户端SSE是在EventSource中实现的,EventSource内置了3个EventHandler属性、2个只读属性和1个方法:

  • onopen属性:在连接打开时被调用。
  • onmessage属性:在收到一个没有event属性的消息时被调用。
  • onerror属性:在连接异常时被调用。
  • readyState只读属性:代表连接状态;可能值是CONNECTING(0),OPEN(1),CLOSED(2)
  • url只读属性:连接的URL。
  • close()方法:关闭连接
'use strict';if (window.EventSource) {// 创建 EventSource 对象连接服务器const source = new EventSource('http://localhost:2000/stream');// 连接成功后会触发 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');
}

服务端

服务端使用基于Flask的实现

from flask import Flask, request
from flask import Response
from flask import render_templateapp = Flask(__name__)def get_message():"""this could be any function that blocks until data is ready"""time.sleep(1)s = time.ctime(time.time())return json.dumps(['当前时间:' + s , 'a'], ensure_ascii=False)@app.route('/')
def hello_world():return render_template('index.html')@app.route('/stream')
def stream():user_id = request.args.get('user_id')print(user_id)def eventStream():id = 0for i in range(10):id +=1# wait for source data to be available, then push ityield 'id: {}\\nevent: add\\ndata: {}\\n\\n'.format(id,get_message())id +=1yield 'id: {}\\nevent: done\\n\\n'.format(id)    return Response(eventStream(), mimetype="text/event-stream")if __name__ == '__main__':app.run(port=2000)