开发一个使用服务器发送的事件的Web应用程序是很容易的。你需要在服务器上的一些代码将事件流传输到Web应用程序,但Web应用程序端的事情几乎完全相同,处理任何其他类型的事件。

在Web应用程序中使用服务器发送事件很简单.在服务器端,只需要按照一定的格式返回事件流,在客户端中,只需要为一些事件类型绑定监听函数,和处理其他普通的事件没多大区别.

 

从服务器接受事件

服务器发送事件API也就是EventSource接口,在你创建一个新的EventSource对象的同时,你可以指定一个接受事件的URI.例如:

var evtSource = new EventSource("ssedemo.php");
注:从Firefox 11开始,EventSource开始支持CORS.虽然该特性目前并不是标准,但很快会成为标准.

一旦你成功初始化了一个事件源,就可以开始监听它的消息了:

evtSource.onmessage = function(e) {
  var newElement = document.createElement("li");
  
  newElement.innerHTML = "message: " + e.data;
  eventList.appendChild(newElement);
}

上面的代码监听了那些从服务器发送来的所有没有指定事件类型的消息(没有event字段的消息),然后把消息内容显示在页面文档中.

你也可以使用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数据,然后在页面上显示出所需要的内容.

服务器端如何发送事件流

服务器端发送的响应内容应该使用值为"text/event-stream"的MIME类型.这里有一个事件流文件的例子: Event stream 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格式,数组字段中包含了每个事件流生成时的时间字符串.而且会随机返回一些无事件类型的消息.

事件流格式

事件流仅仅是一个简单的文本数据流,文本应该使用UTF- 8格式的编码.每条消息后面都由一个空行作为分隔符.以冒号开头的行为注释行,会被忽略.

注:注释行可以用来防止连接超时,服务器可以定期发送一条消息注释行,以保持连接不断.

每条消息是由多个字段组成的,每个字段由字段名,一个冒号,以及字段值组成.

字段

规范中规定了下面这些字段:

event
事件类型.如果指定了该字段,则在客户端接收到该条消息时,会在当前的EventSource对象上触发一个事件,事件类型就是该字段的字段值,你可以使用addEventListener()方法在当前EventSource对象上监听任意类型的命名事件,如果该条消息没有event字段,则会触发onmessage属性上的事件处理函数.
data
消息的数据字段.如果该条消息包含多个data字段,则客户端会用换行符把它们连接成一个字符串来作为字段值.
id
事件ID,会成为当前EventSource对象的内部属性"最后一个事件ID"的属性值.
retry
一个整数值,指定了重新连接的时间(单位为毫秒),如果该字段值不是整数,则会被忽略.

除了上面规定的字段名,其他所有的字段名都会被忽略.

注: 如果一行文本中不包含冒号,则整行文本会被解析成为字段名,其字段值为空.

例子

未命名事件

下面的例子中发送了三条消息,第一条仅仅是个注释,因为它以冒号开头.第二条消息只包含了一个data字段,值为"some text".第三条消息包含的两个data字段会被解析成为一个字段,值为"another message\nwith two lines".其中每两条消息之间是以一个空行为分割符的.

: this is a test stream

data: some text

data: another message
data: with two lines 

命名事件

下面的事件流中包含了一些命名事件.每个事件的类型都是由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."}

混合两种事件

你可以在一个事件流中同时使用命名事件和未命名事件.

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."}

浏览器兼容性

We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
EventSource support 9 6.0 (6.0) 未实现 11 5
Feature Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
EventSource support 未实现 ? ? 11.1 4

 

文档标签和贡献者

此页面的贡献者: xgqfrms-GitHub, jamemark, ziyunfei
最后编辑者: xgqfrms-GitHub,