1. 9.2 服务器发送事件
      1. 9.2.1 简介
      2. 9.2.2 EventSource 接口
      3. 9.2.3 Last-Event-ID 头部
      4. 9.2.4 事件流格式
      5. 9.2.5 创作说明

9.2 服务器发送事件

服务器发送事件

所有当前引擎都支持。

Firefox6+Safari5+Chrome6+
Opera11+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android45+Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+

9.2.1 简介

为了使服务器能够通过 HTTP 或使用专用的服务器推送协议将数据推送到网页,本规范引入了 EventSource 接口。

使用此 API 包括创建 EventSource 对象和注册事件监听器。

var source = new EventSource('updates.cgi');
source.onmessage = function (event) {
  alert(event.data);
};

在服务器端,脚本(在本例中为“updates.cgi”)以以下形式发送消息,并使用 text/event-stream MIME 类型

data: This is the first message.

data: This is the second message, it
data: has two lines.

data: This is the third message.

作者可以使用不同的事件类型来分隔事件。这是一个有两个事件类型“add”和“remove”的流

event: add
data: 73857293

event: remove
data: 2153

event: add
data: 113411

处理此类流的脚本如下所示(其中 addHandlerremoveHandler 是接受一个参数(事件)的函数)

var source = new EventSource('updates.cgi');
source.addEventListener('add', addHandler, false);
source.addEventListener('remove', removeHandler, false);

默认事件类型为“message”。

事件流始终解码为 UTF-8。无法指定其他字符编码。


事件流请求可以使用 HTTP 301 和 307 重定向,就像普通的 HTTP 请求一样。如果连接关闭,客户端将重新连接;可以使用 HTTP 204 无内容响应代码告诉客户端停止重新连接。

使用此 API 而不是使用 XMLHttpRequestiframe 模拟它,允许用户代理在用户代理实现者和网络运营商能够提前协调的情况下更好地利用网络资源。除其他好处外,这还可以节省便携式设备的电池电量。这将在下面关于 无连接推送 的部分中进一步讨论。

9.2.2 EventSource 接口

EventSource

所有当前引擎都支持。

Firefox6+Safari5+Chrome6+
Opera11+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android45+Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+
source = new EventSource( url [, { withCredentials: true } ])

创建一个新的 EventSource 对象。

url 是一个字符串,用于提供事件流的 URL

withCredentials 设置为 true 将为连接请求到 url凭据模式 设置为“include”。

source.close()

中止为此 EventSource 对象启动的 fetch 算法的任何实例,并将 readyState 属性设置为 CLOSED

source.url

返回 提供事件流的 URL

source.withCredentials

如果连接请求到 提供事件流的 URL凭据模式 设置为“include”,则返回 true,否则返回 false。

source.readyState

返回此 EventSource 对象的连接状态。它可以具有以下描述的值。

CONNECTING(数值 0)
连接尚未建立,或者它已关闭并且用户代理正在重新连接。
OPEN(数值 1)
用户代理具有打开的连接,并且在收到事件时会分派事件。
CLOSED(数值 2)
连接未打开,并且用户代理未尝试重新连接。要么发生致命错误,要么调用了 close() 方法。

以下是所有实现 EventSource 接口的对象支持的 事件处理程序(及其对应的 事件处理程序事件类型),作为 事件处理程序 IDL 属性

事件处理程序 事件处理程序事件类型
onopen

EventSource/open_event

所有当前引擎都支持。

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android45+Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android12+
open
onmessage

EventSource/message_event

所有当前引擎都支持。

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android45+Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android12+
message
onerror

EventSource/error_event

所有当前引擎都支持。

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android45+Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android12+
error

9.2.3 Last-Event-ID 头部

Last-Event-ID HTTP 请求头部在用户代理要 重新建立连接 时向服务器报告 EventSource 对象的 最后一个事件 ID 字符串

请参阅 whatwg/html issue #7363 以更好地定义值空间。它本质上是任何 UTF-8 编码的字符串,不包含 U+0000 NULL、U+000A LF 或 U+000D CR。

9.2.4 事件流格式

此事件流格式的 MIME 类型text/event-stream

事件流格式如以下 ABNF 的 stream 产生式所述,其字符集为 Unicode。 [ABNF]

stream        = [ bom ] *event
event         = *( comment / field ) end-of-line
comment       = colon *any-char end-of-line
field         = 1*name-char [ colon [ space ] *any-char ] end-of-line
end-of-line   = ( cr lf / cr / lf )

; characters
lf            = %x000A ; U+000A LINE FEED (LF)
cr            = %x000D ; U+000D CARRIAGE RETURN (CR)
space         = %x0020 ; U+0020 SPACE
colon         = %x003A ; U+003A COLON (:)
bom           = %xFEFF ; U+FEFF BYTE ORDER MARK
name-char     = %x0000-0009 / %x000B-000C / %x000E-0039 / %x003B-10FFFF
                ; a scalar value other than U+000A LINE FEED (LF), U+000D CARRIAGE RETURN (CR), or U+003A COLON (:)
any-char      = %x0000-0009 / %x000B-000C / %x000E-10FFFF
                ; a scalar value other than U+000A LINE FEED (LF) or U+000D CARRIAGE RETURN (CR)

此格式的事件流必须始终编码为 UTF-8。 [编码]

行必须以 U+000D 回车 U+000A 换行(CRLF)字符对、单个 U+000A 换行(LF)字符或单个 U+000D 回车(CR)字符分隔。

以下事件流,后跟一个空行

data: YHOO
data: +2
data: 10

...将导致在 EventSource 对象上分派一个事件 message,其接口为 MessageEvent。该事件的 data 属性将包含字符串“YHOO\n+2\n10”(其中“\n”表示换行符)。

这可以按如下方式使用

var stocks = new EventSource("https://stocks.example.com/ticker.php");
stocks.onmessage = function (event) {
  var data = event.data.split('\n');
  updateStocks(data[0], data[1], data[2]);
};

...其中 updateStocks() 是一个定义为

function updateStocks(symbol, delta, value) { ... }

...或类似的函数。

以下流包含四个块。第一个块只有一个注释,不会触发任何操作。第二个块分别有两个名为“data”和“id”的字段;将为此块触发一个事件,数据为“first event”,然后将最后一个事件 ID 设置为“1”,以便如果在此块与下一个块之间连接断开,服务器将收到一个带有值“1”的 `Last-Event-ID` 头部。第三个块触发一个数据为“second event”的事件,并且还有一个“id”字段,这次没有值,这会将最后一个事件 ID 重置为空字符串(意味着现在不会在尝试重新连接的情况下发送 `Last-Event-ID` 头部)。最后,最后一个块只触发一个数据为“ third event”(带有一个前导空格字符)的事件。请注意,最后一个仍然必须以空行结尾,流的结尾不足以触发最后一个事件的分派。

: test stream

data: first event
id: 1

data:second event
id

data:  third event

以下流触发两个事件

data

data
data

data:

第一个块触发数据设置为空字符串的事件,如果最后一个块后跟一个空行,则最后一个块也会触发。中间块触发一个数据设置为单个换行符的事件。最后一个块被丢弃,因为它后跟一个空行。

以下流触发两个相同的事件

data:test

data: test

这是因为如果存在冒号后的空格将被忽略。

9.2.5 创作说明

已知旧版代理服务器在某些情况下会在短时间后超时后断开 HTTP 连接。为了防止此类代理服务器,作者可以每 15 秒左右包含一个注释行(以“:”字符开头)。

希望将事件源连接相互关联或关联到之前提供的特定文档的作者可能会发现依赖 IP 地址不起作用,因为单个客户端可以具有多个 IP 地址(由于具有多个代理服务器)并且单个 IP 地址可以具有多个客户端(由于共享一个代理服务器)。最好在提供文档时在文档中包含一个唯一标识符,然后在建立连接时将该标识符作为 URL 的一部分传递。

还提醒作者,HTTP 分块可能会对该协议的可靠性产生意想不到的负面影响,特别是如果分块由不了解时间要求的不同层完成时。如果这是一个问题,可以禁用分块以提供事件流。

支持 HTTP 的每个服务器连接限制的客户端在从站点打开多个页面时可能会遇到麻烦,如果每个页面都对同一域有一个 EventSource。作者可以使用相对复杂的机制来避免这种情况,例如为每个连接使用唯一的域名,或者允许用户在每个页面上启用或禁用 EventSource 功能,或者使用 共享工作线程 共享单个 EventSource 对象。