当有多行数据时,实际的数据由每行数据以换行符连接而成。
如果服务器端返回的数据中包含了事件的标识符,浏览器会记录最近一次接收到的事件的标识符。如果与服务器端的连接中断,当浏览器端再次进行连接时,会通过 HTTP 头“Last-Event-ID”来声明最后一次接收到的事件的标识符。服务器端可以通过浏览器端发送的事件标识符来确定从哪个事件开始来继续连接。
对于服务器端返回的响应,浏览器端需要在 JavaScript 中使用 EventSource 对象来进行处理。EventSource 使用的是标准的事件监听器方式,只需要在对象上添加相应的事件处理方法即可。EventSource 提供了三个标准事件:
EventSource 对象提供的标准事件
名称 说明 事件处理方法
open 当成功与服务器建立连接时产生 onopen
message 当收到服务器发送的事件时产生 onmessage
error 当出现错误时产生 onerror
而且,服务器端可以返回自定义类型的事件。对于这些事件,可以使用 addEventListener 方法来添加相应的事件处理方法:
var es = new EventSource('events'); es.onmessage = function(e) { console.log(e.data); }; //自定义事件 myevent es.addEventListener('myevent', function(e) { console.log(e.data); });
在指定 URL 创建出 EventSource 对象之后,可以通过 onmessage 和 addEventListener 方法来添加事件处理方法。当服务器端有新的事件产生,相应的事件处理方法会被调用。EventSource 对象的 onmessage 属性的作用类似于 addEventListener( ‘ message ’ ),不过 onmessage 属性只支持一个事件处理方法。
传统的网页都是浏览器向服务器“查询”数据,但是很多场合,最有效的方式是服务器向浏览器“发送”数据。比如,每当收到新的电子邮件,服务器就向浏览器发送一个“通知”,这要比浏览器按时向服务器查询(polling)更有效率。服务器发送事件(Server-Sent Events,简称SSE)就是为了解决这个问题,而提出的一种新API,部署在EventSource对象上。目前,除了IE,其他主流浏览器都支持。
简单说,所谓SSE,就是浏览器向服务器发送一个HTTP请求,然后服务器不断单向地向浏览器推送“信息”(message)。这种信息在格式上很简单,就是“信息”加上前缀“data: ”,然后以“\n\n”结尾。
SSE与WebSocket有相似功能,都是用来建立浏览器与服务器之间的通信渠道。两者的区别在于:
WebSocket是全双工通道,可以双向通信,功能更强;SSE是单向通道,只能服务器向浏览器端发送。
WebSocket是一个新的协议,需要服务器端支持;SSE则是部署在HTTP协议之上的,现有的服务器软件都支持。
SSE是一个轻量级协议,相对简单;WebSocket是一种较重的协议,相对复杂。
SSE默认支持断线重连,WebSocket则需要额外部署。
SSE支持自定义发送的数据类型。
从上面的比较可以看出,两者各有特点,适合不同的场合。
个人完整的HTML5页面和C#(MVC实现服务端代码)如下:
前端HTML5页面:
HTML5 服务器发送事件(Server-Sent Events)-单向消息传递获取服务端更新数据) { ); (event) { console.log(source.readyState); console.log(event); }; (event) { console.log(source.readyState); console.log(event); }; (event) { console.log(source.readyState); console.log(event); document.getElementById(; }; (e) { console.log(); console.log(e); document.getElementById(; }); } else { document.getElementById(; }
C#写的服务器端:
using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Web.Http; namespace WebTest.Controllers { api/{controller}/{id} MyAPIController : ApiController { static readonly Random random = new Random(); ...api/MyAPI/ServerSentEvents [HttpGet, HttpPost] public Task<HttpResponseMessage> ServerSentEvents() { //Response.ContentType = "text/event-stream" //Response.Expires = -1 //Response.Write("data: " & now()) data = ""; if (random.Next(0, 10) % 3 == 0) { //唤醒自定义的CustomEvent data = ServerSentEventData(, DateTime.Now.Ticks.ToString(), ); } else { //唤醒默认的message data = ServerSentEventData(DateTime.Now.ToString(), DateTime.Now.Ticks.ToString()); } HttpResponseMessage response = new HttpResponseMessage { //注意:ContentType = "text/event-stream" Content = ), ) }; return Task.FromResult(response); } , long retry = 10000) { StringBuilder sb = new StringBuilder(); sb.AppendFormat(, retry); sb.AppendFormat(, _event); sb.AppendFormat(, id); sb.AppendFormat(, data); return sb.ToString(); } } }
通信在页面上的显示结果:
通过Chrome监控网络交互时序:
通过Chrome浏览器控制台输出,下面是一轮ope、message、error事件的详情:
至此,大功告成。