SSE概述传统的网页都是浏览器向服务器“查询”数据,可是许多场所,最有效的方式是服务器向浏览器“发送”数据。这要比浏览器定时向服务器查询(polling)更有效率。**服务器发送事件(Server-Sent Events,简称SSE)**就是为相识决这个问题,而提出的一种新API,部署在EventSource工具上。
现在,除了IE,其他主流浏览器都支持。简朴说,所谓SSE,就是浏览器向服务器发送一个HTTP请求,然后服务器不停单向地向浏览器推送“信息”(message)。这种信息在花样上很简朴,就是“信息”加上前缀“data: ”,然后以“nn”末端。
$ curl http://localhost/streamdata: 1394572346452data: 1394572347457data: 1394572348463^CSSE特点SSE与WebSocket有相似功效,都是用来建设浏览器与服务器之间的通信渠道。两者的区别在于:WebSocket是全双工通道,可以双向通信,功效更强;SSE是单向通道,只能服务器向浏览器端发送,因为流信息本质上就是下载。
WebSocket是一个新的协议,需要服务器端支持;SSE则是部署在HTTP协议之上的,现有的服务器软件都支持。SSE是一个轻量级协议,相对简朴;WebSocket是一种较重的协议,相对庞大。SSE 一般只用来传送文本,二进制数据需要编码后传送;WebSocket 默认支持传送二进制数据。SSE默认支持断线重连;WebSocket则需要自己实现。
SSE支持自界说发送的数据类型。客户端代码EventSourceSSE 的客户端 API 部署在EventSource工具上。
下面的代码可以检测浏览器是否支持 SSE。if (!!window.EventSource) { // ...}//或者if ('EventSource' in window) { // ...}使用 SSE 时,浏览器首先生成一个EventSource实例,向服务器提倡毗连。var source = new EventSource(url);上面的url可以与当前网址同域,也可以跨域。
跨域时,可以指定第二个参数,打开withCredentials属性,表现是否一起发送 Cookie。var source = new EventSource(url, { withCredentials: true });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();客户端实现<!DOCTYPE html><html><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>JS Bin</title></head><body><div id="example"></div><script> var source = new EventSource('http://127.0.0.1/stream'); var div = document.getElementById('example'); source.onopen = function (event) { div.innerHTML += '<p>Connection open ...</p>'; }; source.onerror = function (event) { div.innerHTML += '<p>Connection close.</p>'; }; source.addEventListener('connecttime', function (event) { div.innerHTML += ('<p>Start time: ' + event.data + '</p>'); }, false); source.addEventListener('myevent', function (event) { div.innerHTML += ('<p>MyEvent: ' + event.data + '</p>'); }, false); source.onmessage = function (event) { div.innerHTML += ('<p>Ping: ' + event.data + '</p>'); div.innerHTML +=('<p>Message: ' + event.mymessage + '</p>'); }; </script></body></html>服务端实现数据花样服务器端发送的数据的HTTP头信息如下:Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-alive后面的行都是如下花样:field: valuenfield可以取四个值:“data”, “event”, “id”, or “retry”,也就是说有四类头信息。每次HTTP通信可以包罗这四类头信息中的一类或多类。
n代表换行符。以冒号开头的行,表现注释。通常,服务器每隔一段时间就会向浏览器发送一个注释,保持毗连不中断。: This is a comment下面是一些例子。
: this is a test streamnndata: some textnndata: another messagendata: with two lines nndata:数据栏数据内容用data表现,可以占用一行或多行。如果数据只有一行,则像下面这样,以“nn”末端。
data: messagenn如果数据有多行,则最后一行用“nn”末端,前面行都用“n”末端。data: begin messagendata: continue messagenn总之,最后一行的data,末端要用两个换行符号,表现数据竣事。以发送JSON花样的数据为例。
data: {ndata: "foo": "bar",ndata: "baz", 555ndata: }nnid:数据标识符数据标识符用id表现,相当于每一条数据的编号。id: msg1ndata: messagenn浏览器用lastEventId属性读取这个值。
一旦毗连断线,浏览器会发送一个HTTP头,内里包罗一个特殊的“Last-Event-ID”头信息,将这个值发送回来,用来资助服务器端重建毗连。因此,这个头信息可以被视为一种同步机制。event栏:自界说信息类型event头信息表现自界说的数据类型,或者说数据的名字。
event: foondata: a foo eventnndata: an unnamed eventnnevent: barndata: a bar eventnn上面的代码缔造了三条信息。第一条是foo,触发浏览器端的foo事件;第二条未取名,表现默认类型,触发浏览器端的message事件;第三条是bar,触发浏览器端的bar事件。
下面是另一个例子:event: userconnectdata: {"username": "bobby", "time": "02:33:48"}event: usermessagedata: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}event: userdisconnectdata: {"username": "bobby", "time": "02:34:23"}event: usermessagedata: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}retry:最大距离时间浏览器默认的是,如果服务器端三秒内没有发送任何信息,则开始重连。服务器端可以用retry头信息,指定通信的最大距离时间。
retry: 10000nNodeJS实现SSE 要求服务器与浏览器保持毗连。对于差别的服务器软件来说,所消耗的资源是纷歧样的。Apache 服务器,每个毗连就是一个线程,如果要维持大量毗连,势须要消耗大量资源。
Node 则是所有毗连都使用同一个线程,因此消耗的资源会小得多,可是这要求每个毗连不能包罗很耗时的操作,好比磁盘的 IO 读写。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", "Access-Control-Allow-Origin": '*', }); 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); /* interval2 = setInterval(function() { res.write("event: myeventn"); res.write("data: " + (new Date()) + "nn"); }, 2000); */ req.connection.addListener("close", function () { clearInterval(interval); // clearInterval(interval2); }, false); }}).listen(80, "127.0.0.1");PHP代码实现<?phpheader('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()));?>Java代码实现实现由两部门组成:一部门是用来发生数据的 org.eclipse.jetty.servlets.EventSource 接口的实现,另一部门是作为浏览器会见端点的继续自 org.eclipse.jetty.servlets.EventSourceServlet 类的 servlet 实现。需要实现 EventSource 接口的 onOpen、onResume 和 onClose 方法,其中 onOpen 方法在浏览器端的毗连打开的时候被挪用,onResume 方法在浏览器端重新建设毗连时被挪用,onClose 方规则在浏览器关闭毗连的时候被挪用。
onOpen 和 onResume 方法都有一个 EventSource.Emitter 接口类型的参数,可以用来发送数据。EventSource.Emitter 接口中包罗的方法包罗 data、event、comment、id 和 close 等,划分对应于通讯协议中种种差别类型的事件。而 onResume 方法还分外包罗一个参数 lastEventId,表现通过 Last-Event-ID 头发送过来的最近一次事件的标识符。EventSource 类对应的 servlet 实现比力简朴,只需要继续自 EventSourceServlet 类并覆写 newEventSource 方法即可。
在 newEventSource 方法的实现中,需要返回一个 MovementEventSource 类的工具IE 支持使用浏览器原生的 EventSource 工具的一个比力大的问题是 IE 并不提供支持。为了在 IE 上提供同样的支持,一般有两种措施。
第一种措施是在其他浏览器上使用原生 EventSource 工具,而在 IE 上则使用浅易轮询或 COMET 技术来实现;另外一种做法是使用 polyfill 技术,纵然用第三方提供的 JavaScript 库来屏蔽浏览器的差别。本文使用的是 polyfill 技术,只需要在页面中加载第三方 JavaScript 库即可。
应用自己的浏览器端代码并不需要举行改动。一般推荐使用第二种做法,因为这样的话,在服务器端只需要使用一种实现技术即可。
在 IE 上提供类似原生 EventSource 工具的实现并不简朴。理论上来说,只需要通过 XMLHttpRequest 工具来获取服务器端的响应内容,并通过文本剖析,就可以提取出相应的事件,并触发对应的事件处置惩罚方法。不外问题在于 IE 上的 XMLHttpRequest 工具并不支持获取部门的响应内容。
只有在响应完成之后,才气获取其内容。由于服务器端推送事件使用的是一个长毗连。当毗连一直处于打开状态时,通过 XMLHttpRequest 工具并不能获取响应的内容,也就无法触发对应的事件。
更详细的来说,当 XMLHttpRequest 工具的 readyState 为 3(READYSTATE_INTERACTIVE)时,其 responseText 属性是无法获取的。为相识决 IE 上 XMLHttpRequest 工具的问题,就需要使用 IE 8 中引入的 XDomainRequest 工具。XDomainRequest 工具的作用是发出跨域的 AJAX 请求。
XDomainRequest 工具提供了 onprogress 事件。当 onprogress 事件发生时,可以通过 responseText 属性来获取到响应的部门内容。这是 XDomainRequest 工具和 XMLHttpRequest 工具的最大差别,也是使用 XDomainRequest 工具来实现类似原生 EventSource 工具的基础。
在使用 XDomainRequest 工具打开与服务器端的毗连之后,当服务器端有新的数据发生时,可以通过 XDomainRequest 工具的 onprogress 事件的处置惩罚方法来举行处置惩罚,对吸收到的数据举行剖析,凭据数据的内容触发相应的事件。不外由于 XDomainRequest 工具原来的目的是发出跨域 AJAX 请求,思量到跨域会见的宁静性问题,XDomainRequest 工具在使用时的限制也比力严格。这些限制会影响到其作为 EventSource 工具的实现方式。
详细的限制息争决措施如下所示:服务器端的响应需要包罗 Access-Control-Allow-Origin 头,用来声明允许从哪些域会见该 URL。“*”表现允许来自任何域的会见,不推荐使用该值。
一般使用与当前应用相同的域,限制只允许来自当前域的会见。XDomainRequest 工具发出的请求不能包罗自界说的 HTTP 头,这就限制了不能使用 Last-Event-ID 头来声明浏览器端最近一次吸收到的事件的标识符。只能通过 HTTP 请求的其他方式来通报该标识符,如 GET 请求的参数或 POST 请求的内容体。
XDomainRequest 工具的请求的内容类型(Content-Type)只能是“text/plain”。这就意味着,当使用 POST 请求时,服务器端使用的框架,如 servlet,不会对 POST 请求的内容举行自动剖析,无法使用 HttpServletRequest 类的 getParameter 方法来获取 POST 请求的内容。
只能在服务器端对原始的请求内容举行剖析,获取到其中的参数的值。XDomainRequest 工具发出的请求中不包罗任何与用户认证相关的信息,包罗 cookie 等。这就意味着,如果服务器端需要认证,则需要通过 HTTP 请求的其他方式来通报用户的认证信息,好比 session 的 ID 等。
由于 XDomainRequest 工具的这些限制,服务器端的实现也需要作出相应的改动。这些改动包罗返回 Access-Control-Allow-Origin 头;对于浏览器端发送的“text/plain”类型的参数举行剖析;处置惩罚请求中包罗的用户认证相关的信息。本文的示例使用的 polyfill 库是 GitHub 上的 Yaffle 开发的 EventSource 项目,详细的地址见参考资源。
在使用该 polyfill 库,并对服务器端的实现举行修改之后,就可以在 IE 8 及以上的浏览器中使用服务器推送事件。如果需要支持 IE 7,则只能使用浅易轮询或 COMET 技术。本文的示例代码见参考资源。
本文来源:ag真人官网平台-www.chengxingjm.com