[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: first

id: 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_template

app = 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 = 0
        for i in range(10):
            id +=1
            # wait for source data to be available, then push it

            yield 'id: {}\nevent: add\ndata: {}\n\n'.format(id,get_message())

        id +=1
        yield 'id: {}\nevent: done\n\n'.format(id)    


    return Response(eventStream(), mimetype="text/event-stream")


if __name__ == '__main__':
    app.run(port=2000)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值