首页 web前端 H5教程 HTML5支持服务器发送事件

HTML5支持服务器发送事件

May 28, 2017 am 10:54 AM
服务器

传统的WEB应用程序通信时的简单时序图:

现在Web App中,大都有Ajax,是这样子:

 

HTML5有一个Server-Sent Events(SSE)功能,允许服务端推送数据到客户端。(通常叫数据推送)。基于数据推送是这样的,当数据源有新数据,它马上发送到客户端,不需要等待客户端请求。这些新数据可能是最新闻,最新股票行情,来自朋友的聊天信息,天气预报等。

数据拉与推的功能是一样的,用户拿到新数据。但数据推送有一些优势。 你可能听说过Comet, Ajax推送, 反向Ajax, HTTP流,WebSockets与SSE是不同的技术。可能最大的优势是低延迟。SSE用于web应用程序刷新数据,不需要用户做任何动作。
     你可能听说过HTML5的WebSockets,也能推送数据到客户端。WebSockets是实现服务端更加复杂的技术,但它是真的全双工socket, 服务端能推送数据到客户端,客户端也能推送数据回服务端。SSE工作于存在HTTP/HTTPS协议,支持代理服务器与认证技术。SSE是文本协议你能轻易的调试它。如果你需要发送大部二进制数据从服务端到客户端,WebSocket是更好的选择。关于SSE与WebSocket的区别,本文下面会讲到。

 

HTML5 服务器发送事件(server-sent event)允许网页获得来自服务器的更新
Server-Sent 事件 - 单向消息传递
Server-Sent 事件指的是网页自动获取来自服务器的更新。
以前也可能做到这一点,前提是网页不得不询问是否有可用的更新。通过服务器发送事件,更新能够自动到达。
例子:Facebook/Twitter 更新、估价更新、新的博文、赛事结果等。

浏览器支持(所有主流浏览器均支持服务器发送事件,除了 Internet Explorer。)

EventSource 推送(ajax普通轮询):

处理过程:

客户端建立EventSource对象,对服务器通过http协议不断进行请求。服务器对客户端的响应数据格式有四部分构成,event,data,id,空格行。客户端接收到服务器端的响应数据之后,根据event事件值,找到EventSource对象对应的事件监听器。

 

接收 Server-Sent 事件通知
EventSource 对象用于接收服务器发送事件通知:


    //创建一个新的 EventSource 对象,规定发送更新的页面的 URL
    var source = new EventSource("../api/MyAPI/ServerSentEvents");    //默认支持message事件
    source.onmessage = function (event) {
        console.log(source.readyState);
        console.log(event);
    };
登录后复制


实例解析:
  创建一个新的 EventSource 对象,然后规定发送更新的页面的 URL(本例中是 "demo_sse.php"),参数url就是服务器网址,必须与当前网页的网址在同一个网域(domain),而且协议和端口都必须相同
  每接收到一次更新,就会发生 onmessage 事件

 

检测 Server-Sent 事件支持
以下实例,我们编写了一段额外的代码来检测服务器发送事件的浏览器支持情况:


if(!!EventSource && typeof(EventSource)!=="undefined")
{    // 浏览器支持 Server-Sent
    // 一些代码.....}else{    // 浏览器不支持 Server-Sent..}
登录后复制


服务器端代码实例
为了让上面的例子可以运行,您还需要能够发送数据更新的服务器(比如 PHP、ASPASP.NET、Java)。
服务器端事件流的语法是非常简单的。你需要把 "Content-Type" 报头设置为 "text/event-stream"。现在,您可以开始发送事件流了。
我只会C#,所以用 ASP.NET的MVC 里面的ApiController写了个最简单的服务器端:


    public class MyAPIController : ApiController
    {        /// <summary>
        /// ...api/MyAPI/ServerSentEvents        /// </summary>
        /// <returns></returns>        [HttpGet, HttpPost]        public Task<HttpResponseMessage> ServerSentEvents()
        {            //Response.ContentType = "text/event-stream"            //Response.Expires = -1            //Response.Write("data: " & now())            //Response.Flush()
            
            string data = "id: 123456\nevent: message\ndata: 666\n\n";

            HttpResponseMessage response = new HttpResponseMessage
            {                //注意:ContentType = "text/event-stream"
                Content = new StringContent(data, Encoding.GetEncoding("UTF-8"), "text/event-stream")
            };            return Task.FromResult(response);
        }
    }
登录后复制


代码解释:
  把报头 "Content-Type" 设置为 "text/event-stream"
  规定不对页面进行缓存
  输出发送日期(始终以 "data: " 开头)
  向网页刷新输出数据

 


EventSource 对象

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

source.readyState
它可以取以下值:

  0,相当于常量EventSource.CONNECTING,表示连接还未建立,或者连接断线。

  1,相当于常量EventSource.OPEN,表示连接已经建立,可以接受数据。

  2,相当于常量EventSource.CLOSED,表示连接已断,且不会重连。

 


在上面的例子中,我们使用 onmessage 事件来获取消息。不过还可以使用其他事件:
事件    描述
onopen   当通往服务器的连接被打开
onmessage 当接收到消息
onerror   当发生错误

 

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

--------------------------------------------------------------------------------------

规范
Server-sent Events 规范是 HTML 5 规范的一个组成部分,具体的规范文档见参考资源。该规范比较简单,主要由两个部分组成:第一个部分是服务器端与浏览器端之间的通讯协议,第二部分则是在浏览器端可供 JavaScript 使用的 EventSource 对象。通讯协议是基于纯文本的简单协议服务器端的响应的内容类型是“text/event-stream”。响应文本的内容可以看成是一个事件流,由不同的事件所组成。每个事件由类型和数据两部分组成,同时每个事件可以有一个可选的标识符。不同事件的内容之间通过仅包含回车符和换行符的空行(“\r\n”)来分隔。每个事件的数据可能由多行组成。代码清单 1 给出了服务器端响应的示例:


retry: 10000\n
event: message\n
id: 636307190866448426\n
data: 2017/05/18 15:44:46\n\n
登录后复制


Chrome浏览器监视视图

响应报文头部:

响应报文内容:


每个事件之间通过空行来分隔。对于每一行来说,冒号(“:”)前面表示的是该行的类型,冒号后面则是对应的值。可能的类型包括:
类型为空白,表示该行是注释,会在处理时被忽略。
类型为 data,表示该行包含的是数据。以 data 开头的行可以出现多次。所有这些行都是该事件的数据。
类型为 event,表示该行用来声明事件的类型。浏览器在收到数据时,会产生对应类型的事件。
类型为 id,表示该行用来声明事件的标识符。
类型为 retry,表示该行用来声明浏览器在连接断开之后进行再次连接之前的等待时间。

当有多行数据时,实际的数据由每行数据以换行符连接而成。
如果服务器端返回的数据中包含了事件的标识符,浏览器会记录最近一次接收到的事件的标识符。如果与服务器端的连接中断,当浏览器端再次进行连接时,会通过 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);
};//自定义事件 myeventes.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页面:


<!DOCTYPE html><html><head>
    <meta charset="utf-8">
    <title>HTML5 服务器发送事件(Server-Sent Events)-单向消息传递</title>
    <meta name="author" content="熊仔其人" />
    <meta name="generator" content="2017-05-18" /></head><body>
    <h1>获取服务端更新数据</h1>
    <p id="result"></p><script>if(typeof(EventSource)!=="undefined")
{    //创建一个新的 EventSource 对象,规定发送更新的页面的 URL
    var source = new EventSource("../api/MyAPI/ServerSentEvents");    //默认支持open事件    source.onopen = function (event) {
        console.log(source.readyState);
        console.log(event);
    };    //默认支持error事件    source.onerror = function (event) {
        console.log(source.readyState);
        console.log(event);
    };    //默认支持message事件    source.onmessage = function (event) {
        console.log(source.readyState);
        console.log(event);
        document.getElementById("result").innerHTML += event.data + "<br>";
    };    //处理服务器响应报文中的自定义事件    source.addEventListener("CustomEvent", function (e) {
        console.log("唤醒自定义事件");
        console.log(e);
        document.getElementById("result").innerHTML += e.data + "<br>";
    });
}else{
    document.getElementById("result").innerHTML="抱歉,你的浏览器不支持 server-sent 事件...";
}</script></body></html>
登录后复制


C#写的服务器端:


using System;using System.Net.Http;using System.Text;using System.Threading.Tasks;using System.Web.Http;namespace WebTest.Controllers
{    /// <summary>
    /// api/{controller}/{id}    /// </summary>
    public class MyAPIController : ApiController
    {        static readonly Random random = new Random();        /// <summary>
        /// ...api/MyAPI/ServerSentEvents        /// </summary>
        /// <returns></returns>        [HttpGet, HttpPost]        public Task<HttpResponseMessage> ServerSentEvents()
        {            //Response.ContentType = "text/event-stream"            //Response.Expires = -1            //Response.Write("data: " & now())            //Response.Flush()
            
            string data = "";            if (random.Next(0, 10) % 3 == 0)
            {                //唤醒自定义的CustomEvent
                data = ServerSentEventData("这是自定义通知", DateTime.Now.Ticks.ToString(), "CustomEvent");
            }            else
            {                //唤醒默认的message
                data = ServerSentEventData(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), DateTime.Now.Ticks.ToString());
            }

            HttpResponseMessage response = new HttpResponseMessage
            {                //注意:ContentType = "text/event-stream"
                Content = new StringContent(data, Encoding.GetEncoding("UTF-8"), "text/event-stream")
            };            return Task.FromResult(response);
        }        public string ServerSentEventData(string data, string id, string _event = "message", long retry = 10000)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("retry:{0}\n", retry);
            sb.AppendFormat("event:{0}\n", _event);
            sb.AppendFormat("id:{0}\n", id);
            sb.AppendFormat("data:{0}\n\n", data);            return sb.ToString();
        }
        
    }
}
登录后复制


通信在页面上的显示结果:

通过Chrome监控网络交互时序:

通过Chrome浏览器控制台输出,下面是一轮ope、message、error事件的详情:

 

至此,大功告成。

 

以上是HTML5支持服务器发送事件的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1659
14
CakePHP 教程
1416
52
Laravel 教程
1310
25
PHP教程
1259
29
C# 教程
1233
24
电驴搜索连不上服务器如何解决 电驴搜索连不上服务器如何解决 Jan 25, 2024 pm 02:45 PM

解决办法:1、检查电驴设置,确保已输入正确的服务器地址和端口号;2、检查网络连接,确保计算机已连接到互联网,并重置路由器;3、检查服务器是否在线,如果您的设置和网络连接都没有问题,则需要检查服务器是否在线;4、更新电驴版本,访问电驴官方网站,下载最新版本的电驴软件;5、寻求帮助。

无法连接到RPC服务器导致无法进入桌面的解决方法 无法连接到RPC服务器导致无法进入桌面的解决方法 Feb 18, 2024 am 10:34 AM

RPC服务器不可用进不了桌面怎么办近年来,计算机和互联网已经深入到我们的生活中的各个角落。作为一种集中计算和资源共享的技术,远程过程调用(RPC)在网络通信中起着至关重要的作用。然而,有时我们可能会遇到RPC服务器不可用的情况,导致无法进入桌面。本文将介绍一些可能导致此问题的原因,并提供解决方案。首先,我们需要了解RPC服务器不可用的原因。RPC服务器是一种

CentOS安装fuse及CentOS安装服务器详解 CentOS安装fuse及CentOS安装服务器详解 Feb 13, 2024 pm 08:40 PM

作为一名LINUX用户,我们经常需要在CentOS上安装各种软件和服务器,本文将详细介绍如何在CentOS上安装fuse和搭建服务器的过程,帮助您顺利完成相关操作。CentOS安装fuseFuse是一个用户空间文件系统框架,允许非特权用户通过自定义文件系统实现对文件系统的访问和操作,在CentOS上安装fuse非常简单,只需按照以下步骤操作:1.打开终端,以root用户登录。2.使用以下命令安装fuse软件包:```yuminstallfuse3.确认安装过程中的提示,输入`y`继续。4.安装完

如何将Dnsmasq配置为DHCP中继服务器 如何将Dnsmasq配置为DHCP中继服务器 Mar 21, 2024 am 08:50 AM

DHCP中继的作用是将接收到的DHCP数据包转发到网络上的另一个DHCP服务器,即使这两个服务器位于不同的子网中。通过使用DHCP中继,您可以实现在网络中心部署一个集中式的DHCP服务器,并利用它为所有网络子网/VLAN动态分配IP地址。Dnsmasq是一种常用的DNS和DHCP协议服务器,可以配置为DHCP中继服务器,以帮助管理网络中的动态主机配置。在本文中,我们将向您展示如何将dnsmasq配置为DHCP中继服务器。内容主题:网络拓扑在DHCP中继上配置静态IP地址集中式DHCP服务器上的D

用PHP构建IP代理服务器的最佳实践指南 用PHP构建IP代理服务器的最佳实践指南 Mar 11, 2024 am 08:36 AM

在网络数据传输中,IP代理服务器扮演着重要的角色,能够帮助用户隐藏真实IP地址,保护隐私、提升访问速度等。在本篇文章中,将介绍如何用PHP构建IP代理服务器的最佳实践指南,并提供具体的代码示例。什么是IP代理服务器?IP代理服务器是一种位于用户与目标服务器之间的中间服务器,它充当用户与目标服务器之间的中转站,将用户的请求和响应进行转发。通过使用IP代理服务器

如何查看服务器状态 如何查看服务器状态 Oct 09, 2023 am 10:10 AM

查看服务器状态的方法有使用命令行工具、图形界面工具、监控工具、日志文件和远程管理工具等。详细介绍:1、使用命令行工具,在Linux或Unix服务器上,可以使用命令行工具来查看服务器的状态;2、使用图形界面工具,对于具有图形界面的服务器操作系统,可以使用系统提供的图形界面工具来查看服务器状态;3、使用监控工具,可以使用专门的监控工具来实时监视服务器的状态等等。

如何开启TFTP服务器 如何开启TFTP服务器 Oct 18, 2023 am 10:18 AM

开启TFTP服务器的步骤包括选择TFTP服务器软件、下载和安装软件、配置TFTP服务器以及启动和测试服务器等。详细介绍:1、选择TFTP服务器软件,首先需要选择适合自己需求的TFTP服务器软件,目前有很多可供选择的TFTP服务器软件,例如Tftpd32、PumpKIN、tftp-hpa等,这些软件都提供了简单易用的界面和配置选项;2、下载和安装TFTP服务器软件等等。

epic服务器离线进不了游戏怎么办?epic离线进不了游戏解决方法 epic服务器离线进不了游戏怎么办?epic离线进不了游戏解决方法 Mar 13, 2024 pm 04:40 PM

  epic服务器离线进不了游戏怎么办?这个问题想必很多小伙伴都有遇到过,出现了此提示就是导致正版的游戏无法启动,那么出现这个问题一般是网络和安全软件干扰导致的,那么应该怎么解决呢,本期小编就来和大伙分享解决方法,希望今日的软件教程可以帮助各位解决问题。  epic服务器离线进不了游戏怎么办:  1、很可能是被安全软件干扰了,将游戏平台和安全软件关闭在重启。  2、其次就是网络波动过大,尝试重启一次路由器,看看是否有效,如果条件可以的话,可以尝试使用5g移动网络来进行操作。  3、然后有可能是更

See all articles