1 简介
html5 服务器发送事件 (server-sent event) 允许网页获得来自服务器主动推送的更新.
EventSource 接口用来管理服务器发送事件. 你可以通过将 EventSource 对象的 onmessage 属性指向一个自定义方法来处理那些从服务器接受到的无类型的消息 (也就是, 没有 event 字段的消息). 你还可以使用 addEventListener() 方法来监听其他指定了事件类型的消息.
1.1 Server-Sent 事件 - 单向消息传递
Server-Sent 事件指的是网页自动获取来自服务器的更新. 以前也可能做到这一点, 前提是网页不得不询问是否有可用的更新. 通过服务器发送事件, 更新能够自动到达.
例子: Facebook/Twitter 更新, 估价更新, 新的博文, 赛事结果等.
1.2 浏览器兼容性
所有主流浏览器均支持服务器发送事件, 除了 InternetExplorer.
1.2.1 Desktop
1.2.2 Mobile
1.3 接收 Server-Sent 事件通知
EventSource 对象用于接收服务器发送事件通知:
1.3.1 客户端实例
- var source = newEventSource("demo_sse.php");
- source.onmessage = function(event)
- {
- document.getElementById("result").innerHTML+=event.data+ "<br />";
- };
例子解释:
创建一个新的 EventSource 对象, 然后规定发送更新的页面的 URL(本例中是 "demo_sse.php")
每接收到一次更新, 就会发生 onmessage 事件
当 onmessage 事件发生时, 把已接收的数据推入 id 为 "result" 的元素中
1.3.2 服务器端代码实例
服务器端事件流的语法是非常简单的. 把 "Content-Type" 报头设置为 "text/event-stream". 现在, 您可以开始发送事件流了.
PHP 代码 (demo_sse.php):
ASP 代码(VB) (demo_sse.asp):
代码解释:
把报头 "Content-Type" 设置为 "text/event-stream"
规定不对页面进行缓存
输出发送日期(始终以 "data:" 开头)
向网页刷新输出数据
2 EventSource 对象
2.1 方法
- void close();
- void init(in nsIPrincipal principal, in nsIScriptContext scriptContext, in nsPIDOMWindow ownerWindow, in DOMString url);
- 2.1.1 close()
如果连接处于打开状态, 则关闭连接, 并把 readyState 属性的值设置为 CLOSED. 如果连接已经关闭, 则该方法不会做任何事情.
2.2 init
在 C++ 代码中新建一个 EventSource 对象.
void init(in nsIPrincipal principal, in nsIScriptContext scriptContext, in nsPIDOMWindow ownerWindow, in DOMString url );
2.2.1 参数
principal
用来进行该请求的 principal, 不能为 null.
scriptContext
进行该请求的脚本上下文, 可以为 null.
ownerWindow
与该请求关联的 window 对象, 可以为 null.
url
EventSource 对象的目标 URL, 不能为空.
2.3 属性
除了这些普通属性, 下面还有两个无法通过 JavaScript 代码直接访问的内部属性(当然, 没有常规的属性名):
重新连接时间
一个时间值, 单位为毫秒, 用来决定在连接失败后需要等待多久再次尝试连接.
最后一次的事件 ID 字符串
初始值为空字符串, 如果服务器发送的消息中包含有 id 字段, 则对应的字段值会赋值给该属性. 如果该属性已经有值, 则会覆盖旧的那个值.
2.4 常量
3 使用服务器发送事件
在 web 应用程序中使用服务器发送事件很简单. 在服务器端, 只需要按照一定的格式返回事件流, 在客户端中, 只需要为一些事件类型绑定监听函数, 和处理其他普通的事件没多大区别.
3.1 从服务器接受事件
服务器发送事件 API 也就是 https://developer.mozilla.org/zh-CN/Server-sent_events/EventSource 接口, 在你创建一个新的 https://developer.mozilla.org/zh-CN/Server-sent_events/EventSource 对象的同时, 你可以指定一个接受事件的 URI. 例如:
var evtSource = new EventSource("ssedemo.php");
注: 从 Firefox 11 开始, EventSource 开始支持 CORS https://developer.mozilla.org/zh-CN/HTTP_access_control . 虽然该特性目前并不是标准, 但很快会 成为标准.
一旦你成功初始化了一个事件源, 就可以开始监听它的消息了:
3.1.1 利用 onmessage 监听消息
- evtSource.onmessage = function(e) {
- var newElement = document.createElement("li");
- newElement.innerHTML = "message:" + e.data;
- eventList.appendChild(newElement);
- }
上面的代码监听了那些从服务器发送来的所有没有指定事件类型的消息(没有 event 字段的消息), 然后把消息内容显示在页面文档中.
3.1.2 使用 addEventListener()方法来监听
你也可以使用 addEventListener()方法来监听其他类型的事件:
- evtSource.addEventListener("ping", function(e) {
- var newElement = document.createElement("li");
- var obj = JSON.parse(e.data);
- newElement.innerHTML = "ping at" + obj.time;
- eventList.appendChild(newElement); },
- false);
这段代码也类似, 只是只有在服务器发送的消息中包含一个值为 "ping" 的 event 字段的时候才会触发对应的处理函数, 也就是将 data 字段的字段值解析为 JSON 数据, 然后在页面上显示出所需要的内容.
3.2 服务器端如何发送事件流
服务器端发送的响应内容应该使用值为 "text/event-stream" 的 MIME 类型. 这里有一个事件流文件的例子: Event stream https://developer.mozilla.org/zh-CN/docs/Server-sent_events/Using_server-sent_events#Event_stream_format format.
演示的 PHP 代码如下:
- date_default_timezone_set("America/New_York");
- header("Content-Type: text/event-stream\n\n");
- $counter =rand(1, 10);
- while (1){
- // Every second, sent a "ping" event.
- echo "event: ping\n";
- $curDate =date(DATE_ISO8601);
- echo 'data: {"time":"' . $curDate . '"}';
- echo "\n\n";
- // Send a simple message at random intervals.
- $counter--;
- if(!$counter) {
- echo 'data: This is a message at time' .
- $curDate . "\n\n";
- $counter = rand(1, 10);
- }
- ob_flush();
- flush();
- sleep(1);
- }
上面的代码会让服务器每隔一秒生成一个事件流并返回, 其中每条消息的事件类型为 "ping", 数据字段都使用了 JSON 格式, 数组字段中包含了每个事件流生成时的时间字符串. 而且会随机返回一些无事件类型的消息.
3.3 事件流格式
事件流仅仅是一个简单的文本数据流, 文本应该使用 UTF-8 格式的编码. 每条消息后面都由一个空行作为分隔符. 以冒号开头的行为注释行, 会被忽略.
注: 注释行可以用来防止连接超时, 服务器可以定期发送消息一条注释行, 以保持连接不断.
每条消息是由多个字段组成的, 每个字段由字段名, 一个冒号, 以及字段值组成.
3.3.1 字段
规范中规定了下面这些字段:
event
事件类型. 如果指定了该字段, 则在客户端接收到该条消息时, 会在当前的 EventSource 对象上触发一个事件, 事件类型就是该字段的字段值, 你可以使用 addEventListener()方法在当前 EventSource 对象上监听任意类型的命名事件, 如果该条消息 没有 event 字段, 则会触发 onmessage 属性上的事件处理函数.
data
消息的数据字段. 如果该条消息包含多个 data 字段, 则客户端会用换行符把它们连接成一个字符串来作为字段值.
id
事件 ID, 会成为当前 EventSource 对象的内部属性 "最后一个事件 ID" 的属性值.
retry
一个整数值, 指定了重新连接的时间(单位为毫秒), 如果该字段值不是整数, 则会被忽略.
除了上面规定的字段名, 其他所有的字段名都会被忽略.
注: 如果一行文本中不包含冒号, 则整行文本会被解析成为字段名, 其字段值为空.
3.3.2 例子
3.3.2.1 未命名事件
下面的例子中发送了三条消息, 第一条仅仅是个注释, 因为它以冒号开头. 第二条消息只包含了一个 data 字段, 值为 "some text". 第三条消息包含的两个 data 字段会被解析成为一个字段, 值为 "another message\nwith two lines". 其中每两条消息之间是以一个空行为分割符的.
- : this is a test stream
- data: some text
- data: another message
data: with two lines
3.3.2.2 命名事件
下面的事件流中包含了一些命名事件. 每个事件的类型都是由 event 字段指定的, 另外每个 data 字段的值可以使用 JSON 格式, 当然也可以不是.
- event: userconnect
- data: {"username": "bobby", "time": "02:33:48"}
- event: usermessage
- data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
- event: userdisconnect
- data: {"username": "bobby", "time": "02:34:23"}
- event: usermessage
- data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}
3.3.2.3 混合两种事件
你可以在一个事件流中同时使用命名事件和未命名事件.
- event: userconnect
- data: {"username": "bobby", "time": "02:33:48"}
data: Here's a system message of some kind that will get used
data: to accomplish some task.
- event: usermessage
- data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
3.4 浏览器兼容性
3.4.1 Desktop
3.4.2 Mobile
4 参考链接
HTML 5 服务器发送事件
- http://www.w3school.com.cn/html5/html_5_serversentevents.asp
- Server-Sent Events
- http://www.w3.org/TR/eventsource/#the-eventsource-interface
使用服务器发送事件
https://developer.mozilla.org/zh-CN/docs/Server-sent_events/Using_server-sent_events
来源: http://www.jianshu.com/p/7d8cc5d8fd76