转:SSE:服务器发送事件

原文来自于:http://javascript.ruanyifeng.com/htmlapi/eventsource.htmljavascript

目录

概述

传统的网页都是浏览器向服务器“查询”数据,可是不少场合,最有效的方式是服务器向浏览器“发送”数据。好比,每当收到新的电子邮件,服务器就向浏览器发送一个“通知”,这要比浏览器按时向服务器查询(polling)更有效率。php

服务器发送事件(Server-Sent Events,简称SSE)就是为了解决这个问题,而提出的一种新API,部署在EventSource对象上。目前,除了IE,其余主流浏览器都支持。html

简单说,所谓SSE,就是浏览器向服务器发送一个HTTP请求,而后服务器不断单向地向浏览器推送“信息”(message)。这种信息在格式上很简单,就是“信息”加上前缀“data: ”,而后以“\n\n”结尾。html5

$ curl http://example.com/dates data: 1394572346452 data: 1394572347457 data: 1394572348463 ^C

SSE与WebSocket有类似功能,都是用来创建浏览器与服务器之间的通讯渠道。二者的区别在于:java

  • WebSocket是全双工通道,能够双向通讯,功能更强;SSE是单向通道,只能服务器向浏览器端发送。node

  • WebSocket是一个新的协议,须要服务器端支持;SSE则是部署在HTTP协议之上的,现有的服务器软件都支持。api

  • SSE是一个轻量级协议,相对简单;WebSocket是一种较重的协议,相对复杂。浏览器

  • SSE默认支持断线重连,WebSocket则须要额外部署。缓存

  • SSE支持自定义发送的数据类型。bash

从上面的比较能够看出,二者各有特色,适合不一样的场合。

客户端代码

概述

首先,使用下面的代码,检测浏览器是否支持SSE。

if (!!window.EventSource) { // ... }

而后,部署SSE大概以下。

var source = new EventSource('/dates'); source.onmessage = function(e){ console.log(e.data); }; // 或者 source.addEventListener('message', function(e){})

创建链接

首先,浏览器向服务器发起链接,生成一个EventSource的实例对象。

var source = new EventSource(url);

参数url就是服务器网址,必须与当前网页的网址在同一个网域(domain),并且协议和端口都必须相同。

下面是一个创建链接的实例。

if (!!window.EventSource) { var source = new EventSource('http://127.0.0.1/sses/'); }

新生成的EventSource实例对象,有一个readyState属性,代表链接所处的状态。

source.readyState

它能够取如下值:

  • 0,至关于常量EventSource.CONNECTING,表示链接还未创建,或者链接断线。

  • 1,至关于常量EventSource.OPEN,表示链接已经创建,能够接受数据。

  • 2,至关于常量EventSource.CLOSED,表示链接已断,且不会重连。

open事件

链接一旦创建,就会触发open事件,能够定义相应的回调函数。

source.onopen = function(event) { // handle open event }; // 或者 source.addEventListener("open", function(event) { // handle open event }, false);

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:数据的编号,由服务器端发送。若是没有编号,这个属性为空。

error事件

若是发生通讯错误(好比链接中断),就会触发error事件。

source.onerror = function(event) { // handle error event }; // 或者 source.addEventListener("error", function(event) { // handle error event }, false);

自定义事件

服务器能够与浏览器约定自定义事件。这种状况下,发送回来的数据不会触发message事件。

source.addEventListener("foo", function(event) { var data = event.data; var origin = event.origin; var lastEventId = event.lastEventId; // handle message }, false);

上面代码表示,浏览器对foo事件进行监听。

close方法

close方法用于关闭链接。

source.close();

数据格式

概述

服务器端发送的数据的HTTP头信息以下:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

后面的行都是以下格式:

field: value\n

field能够取四个值:“data”, “event”, “id”, or “retry”,也就是说有四类头信息。每次HTTP通讯能够包含这四类头信息中的一类或多类。\n表明换行符。

以冒号开头的行,表示注释。一般,服务器每隔一段时间就会向浏览器发送一个注释,保持链接不中断。

: This is a comment

下面是一些例子。

: this is a test stream\n\n

data: some text\n\n

data: another message\n
data: with two lines \n\n

data:数据栏

数据内容用data表示,能够占用一行或多行。若是数据只有一行,则像下面这样,以“\n\n”结尾。

data:  message\n\n

若是数据有多行,则最后一行用“\n\n”结尾,前面行都用“\n”结尾。

data: begin message\n
data: continue message\n\n

总之,最后一行的data,结尾要用两个换行符号,表示数据结束。

以发送JSON格式的数据为例。

data: {\n
data: "foo": "bar",\n
data: "baz", 555\n
data: }\n\n

id:数据标识符

数据标识符用id表示,至关于每一条数据的编号。

id: msg1\n
data: message\n\n

浏览器用lastEventId属性读取这个值。一旦链接断线,浏览器会发送一个HTTP头,里面包含一个特殊的“Last-Event-ID”头信息,将这个值发送回来,用来帮助服务器端重建链接。所以,这个头信息能够被视为一种同步机制。

event栏:自定义信息类型

event头信息表示自定义的数据类型,或者说数据的名字。

event: foo\n
data: a foo event\n\n

data: an unnamed event\n\n

event: bar\n
data: a bar event\n\n

上面的代码创造了三条信息。第一条是foo,触发浏览器端的foo事件;第二条未取名,表示默认类型,触发浏览器端的message事件;第三条是bar,触发浏览器端的bar事件。

retry:最大间隔时间

浏览器默认的是,若是服务器端三秒内没有发送任何信息,则开始重连。服务器端能够用retry头信息,指定通讯的最大间隔时间。

retry: 10000\n

服务器代码

服务器端发送事件,要求服务器与浏览器保持链接。对于不一样的服务器软件来讲,所消耗的资源是不同的。Apache服务器,每一个链接就是一个线程,若是要维持大量链接,势必要消耗大量资源。Node.js则是全部链接都使用同一个线程,所以消耗的资源会小得多,可是这要求每一个链接不能包含很耗时的操做,好比磁盘的IO读写。

下面是Node.js的服务器发送事件的代码实例

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: 10000\n"); res.write("event: connecttime\n"); res.write("data: " + (new Date()) + "\n\n"); res.write("data: " + (new Date()) + "\n\n"); interval = setInterval(function() { res.write("data: " + (new Date()) + "\n\n"); }, 1000); req.connection.addListener("close", function () { clearInterval(interval); }, false); } }).listen(80, "127.0.0.1");

PHP代码实例。

<?php header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); // 建议不要缓存SSE数据 /**  * Constructs the SSE data format and flushes that data to the client.  *  * @param string $id Timestamp/id of this connection.  * @param string $msg Line of text that should be transmitted.  */ function sendMsg($id, $msg) { echo "id: $id" . PHP_EOL; echo "data: $msg" . PHP_EOL; echo PHP_EOL; ob_flush(); flush(); } $serverTime = time(); sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));

参考连接

相关文章
相关标签/搜索