SSE(Server-Sent Events)是一种服务端到客户端(浏览器)的单向消息推送方式。
SSE网络协议
SSE是基于HTTP协议的,客户端向服务端发起一个请求,建立长连接( keep-alive connection);服务端向客户端发送(应答)不是一次性的包,而是一个数据流。
客户端
要实现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) {
const source = new EventSource('http://localhost:2000/stream');
source.addEventListener('open', () => {
console.log('Connected');
}, false);
source.addEventListener('message', e => {
console.log(`data: ${e.data}`);
}, false);
source.addEventListener('slide', e => {
console.log(`data: ${e.data}`);
}, false);
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
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)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)