利用SSE服务器主动向浏览器端发送消息
1 SSE (Server-Sent Events)
我们知道我们可以利用WebSocket实现服务端给客户端主动发送消息,SEE跟WebScoket也一样,可以实现服务端给客户端主动发送消息,但各有不同,如果你只要单向传输的时候,其实SSE更方便使用。
服务器发送事件(Server-Sent Events,简称SSE)提出的一种新API,部署在EventSource对象上,目前,除了IE,其他主流浏览器都支持。
简单说,所谓SSE,就是浏览器向服务器发送一个HTTP请求,然后服务器不断单向地向浏览器推送“信息”(message)。这种信息在格式上很简单,就是“信息”加上前缀“data: ”,然后以“nn”结尾。
比如我们看这个东方财富网就使用了这个技术:http://data.eastmoney.com/bbsj/yjbb/601288.html
打开这个网站,我们打开Chrome的开发工具,查看Network -> XHR就可以看到一个sse的链接。
SSE与WebSocket有相似功能,都是用来建立浏览器与服务器之间的通信渠道。两者的区别在于:
- WebSocket是全双工通道,可以双向通信,功能更强;SSE是单向通道,只能服务器向浏览器端发送。
- WebSocket是一个新的协议,需要服务器端支持;SSE则是部署在HTTP协议之上的,现有的服务器软件都支持。
- SSE是一个轻量级协议,相对简单;WebSocket是一种较重的协议,相对复杂。
- SSE默认支持断线重连,WebSocket则需要额外部署。
- SSE支持自定义发送的数据类型。
从上面的比较可以看出,两者各有特点,适合不同的场合。
2 客户端代码
2.1 概述
首先,使用下面的代码,检测浏览器是否支持SSE。
if (!!window.EventSource) { // ...}
然后,部署SSE大概如下。
if (!!window.EventSource) {
var source = new EventSource('http://127.0.0.1/sse/');
}
2.2 建立连接
首先,浏览器向服务器发起连接,生成一个EventSource的实例对象。
var source = new EventSource(url);
参数url就是服务器网址,必须与当前网页的网址在同一个网域(domain),而且协议和端口都必须相同。
下面是一个建立连接的实例。
if (!!window.EventSource) {
var source = new EventSource('http://127.0.0.1/sse/');
}
新生成的EventSource实例对象,有一个readyState属性,表明连接所处的状态。
source.readyState
它可以取以下值:
- 0,相当于常量EventSource.CONNECTING,表示连接还未建立,或者连接断线。
- 1,相当于常量EventSource.OPEN,表示连接已经建立,可以接受数据。
- 2,相当于常量EventSource.CLOSED,表示连接已断,且不会重连。
2.3 open事件
连接一旦建立,就会触发open事件,可以定义相应的回调函数。
source.onopen = function(event) {
// handle open event
};
// 或者
source.addEventListener("open", function(event) {
// handle open event
}, false);
2.4 message事件
收到数据就会触发message事件。
source.onmessage = function(event) {
var data = event.data;
var origin = event.origin;
var lastEventId = event.lastEventId;
// handle message
};
// 或者
source.addEventListener("message", function(event) {
var data = event.data;
var origin = event.origin;
var lastEventId = event.lastEventId;
// handle message
}, false);
参数对象event有如下属性:
- data:服务器端传回的数据(文本格式)。
- origin: 服务器端URL的域名部分,即协议、域名和端口。
- lastEventId:数据的编号,由服务器端发送。如果没有编号,这个属性为空。
2.5 error事件
如果发生通信错误(比如连接中断),就会触发error事件。
source.onerror = function(event) {
// handle error event
};
// 或者
source.addEventListener("error", function(event) {
// handle error event
}, false);
2.6 自定义事件
服务器可以与浏览器约定自定义事件。这种情况下,发送回来的数据不会触发message事件。
source.addEventListener("foo", function(event) {
var data = event.data;
var origin = event.origin;
var lastEventId = event.lastEventId;
// handle message
}, false);
上面代码表示,浏览器对foo事件进行监听。
2.7 close方法
close方法用于关闭连接。
source.close();
3 数据格式
3.1 概述
服务器端发送的数据的HTTP头信息如下:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
后面的行都是如下格式:
field: valuen
field可以取四个值:“data”, “event”, “id”, or “retry”,也就是说有四类头信息。每次HTTP通信可以包含这四类头信息中的一类或多类。n代表换行符。
以冒号开头的行,表示注释。通常,服务器每隔一段时间就会向浏览器发送一个注释,保持连接不中断。
: This is a comment
下面是一些例子。
: this is a test streamnn
data: some textnn
data: another messagen
data: with two lines nn
3.2 data:数据栏
数据内容用data表示,可以占用一行或多行。如果数据只有一行,则像下面这样,以“nn”结尾。
data: messagenn
如果数据有多行,则最后一行用“nn”结尾,前面行都用“n”结尾。
data: begin messagen
data: continue messagenn
总之,最后一行的data,结尾要用两个换行符号,表示数据结束。
以发送JSON格式的数据为例。
data: {n
data: "foo": "bar",n
data: "baz", 555n
data: }nn
3.3 id:数据标识符
数据标识符用id表示,相当于每一条数据的编号。
id: msg1n
data: messagenn
浏览器用lastEventId属性读取这个值。一旦连接断线,浏览器会发送一个HTTP头,里面包含一个特殊的“Last-Event-ID”头信息,将这个值发送回来,用来帮助服务器端重建连接。因此,这个头信息可以被视为一种同步机制。
3.4 event栏:自定义信息类型
event头信息表示自定义的数据类型,或者说数据的名字。
event: foon
data: a foo eventnn
data: an unnamed eventnn
event: barn
data: a bar eventnn
上面的代码创造了三条信息。第一条是foo,触发浏览器端的foo事件;第二条未取名,表示默认类型,触发浏览器端的message事件;第三条是bar,触发浏览器端的bar事件。
3.5 retry:最大间隔时间
浏览器默认的是,如果服务器端三秒内没有发送任何信息,则开始重连。服务器端可以用retry头信息,指定通信的最大间隔时间。
retry: 10000n
4 服务器代码
服务器端发送事件,要求服务器与浏览器保持连接。对于不同的服务器软件来说,所消耗的资源是不一样的。Apache服务器,每个连接就是一个线程,如果要维持大量连接,势必要消耗大量资源。Node.js则是所有连接都使用同一个线程,因此消耗的资源会小得多,但是这要求每个连接不能包含很耗时的操作,比如磁盘的IO读写。
下面是Node.js的服务器发送事件的代码。
v
var http = require("http");
http.createServer(function (req, res) {
var fileName = "." + req.url;
if (fileName === "./stream") {
res.writeHead(200, {"Content-Type":"text/event-stream",
"Cache-Control":"no-cache",
"Connection":"keep-alive"});
res.write("retry: 10000n");
res.write("event: connecttimen");
res.write("data: " + (new Date()) + "nn");
res.write("data: " + (new Date()) + "nn");
interval = setInterval(function() {
res.write("data: " + (new Date()) + "nn");
}, 1000);
req.connection.addListener("close", function () {
clearInterval(interval);
}, false);
}
}).listen(80, "127.0.0.1")
- Enterprise Library深入解析与灵活应用(5):创建一个简易版的批处理执行器,认识Enterprise Library典型的配置方式和对象创建方式
- BTC.com时讯-IBM等老牌大企业因区块链技术获得新活力
- Kit 3D 更新
- Enterprise Library深入解析与灵活应用(5):创建一个简易版的批处理执行器,认识Enterprise Library典型的配置方式和对象创建方式
- 晚上好啊!这是今天人工智能精选要闻
- 构建Flex应用的10大误区
- Flex的起步推动新语言学习
- 简单科普云计算相关内容
- Silverlight初级教程-开发工具
- WCF的Binding模型之五:绑定元素(Binding Element)
- 2018年物联网发展趋势
- Silverlight初级教程-概述
- WCF的Binding模型之四:信道工厂(Channel Factory)
- 如何做一个好的前端重构工程师
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 「Android」通过注解自动生成类文件:APT实战(AbstractProcessor)
- 五、开始Github和码云之旅,新手如何上路
- 用 Shader 写个完美的波浪~
- K8s上的Go服务怎么扩容、发版更新、回滚、平滑重启?教你用Deployment全搞定!
- 图解Go内存管理器的内存分配策略
- why哥这里有一道Dubbo高频面试题,请查收。
- 「容器平台」Kubernetes网络策略101
- 架构师之路 - 服务器硬件扫盲
- 零基础Python教程045期 元组的增删改查测试实验
- 零基础Python教程044期 列表的函数方法,很耐用!
- 零基础Python教程047期 GUI窗体界面编程,迈出软件开发的第一步
- 零基础Python教程046期 矩阵行列互换算法,二维数组的典型应用
- 有一次小明传数据给我,把我弄哭了
- Java删除空字符:Java8 & Java11
- 6k+点赞的SpringBoot+Netty分布式即时通讯系统!爱了爱了!