3 天学习 HTML 5 - 第 3 天 - 第 1 部分






4.98/5 (17投票s)
在本文中,我们将详细介绍服务器发送事件(Server Sent Events)。
引言
终于到了最后一天,第三天。我相信过去的两天你们都过得很愉快。这是最后的阶段——第三天。第三天写得有些不同。在第三天,我们将详细介绍服务器发送事件。 | ![]() |
完整列表
为什么需要服务器发送事件?
服务器发送事件(SSE)是一种技术,通过该技术,浏览器可以通过 HTTP 连接从服务器接收自动更新。
让我们通过一个场景来简化这个定义。假设我们有一个 HTML 页面。需求很简单。该 HTML 页面将不断地从服务器获取更新的数据并在 UI 中显示。
旧方法
完成此需求的一种传统方法是“轮询”。在轮询中,我们会向服务器发送一个 Ajax 请求并获取响应。一旦收到响应,负责此通信的 HTTP 连接就会关闭。一段时间后,它将使用新的 HTTP 连接再次重复,并不断重复。这就是轮询的概念。
1 – 获取 HTML 页面
2 – 从 HTML 页面向服务器请求数据
流式响应的概念
持续的轮询会影响整体性能。一个解决方案是服务器进行流式响应。一旦发出请求,服务器将返回响应,但作为数据流。
流式传输
我敢肯定你见过 YouTube。它是流媒体的最佳范例。YouTube 不是一次性返回整个视频,而是只从服务器返回一小部分视频,YouTube 播放器就开始播放这一小部分视频。但服务器不会止步于此,它会定期不断地发送更多片段。这个概念被称为流媒体。更精确地说,对于视频,它被称为流媒体。
同样,其他数据也可以被流式传输。简单来说,客户端会向服务器请求一些数据,服务器返回一些数据,而不是结束执行和关闭连接,服务器会持续发送数据。一旦没有要发送的内容,服务器将完成执行并关闭连接。
注意:我们很快将使用 Asp.Net MVC 创建一个示例服务器端代码来演示数据流。
普通的 Ajax 调用无法实现流媒体
使用 XmlHttpRequest 进行的传统 Ajax 调用无法让我们获取数据流。使用 Ajax,我们将从 JavaScript 向服务器发送异步请求。一旦在 JavaScript 中收到响应,客户端就会自动关闭连接。它不会等待下一个流返回。
新方法 – 服务器发送事件
在 SSE 的情况下,情况会有点不同。这里我们将有两个东西:
- 服务器 – 它将不断发送数据流。
- 一个 HTML 页面,它将使用 EventSource 对象向服务器发出请求。EventSource 是一个用于处理 SSE 的新的 HTML5 概念。
EventSource 对象提供了一个回调函数,该函数将在服务器每次发送响应时执行。
1 – 获取 HTML 页面
2 – 从 HTML 页面向服务器请求数据
让我们通过演示来更好地理解它。
实验 21 – SSE 演示
步骤 1 - 创建服务器代码
在我们的示例中,我们将选择 Asp.Net MVC 作为服务器端技术。
注意:本文更侧重于 HTML 5 SSE,而不是 Asp.net MVC。如果您对 Asp.Net MVC 有先前的了解会更好,否则只需按照以下步骤准备服务器端部分。甚至不要犹豫从附带的示例中下载 MVC 项目并直接使用它。
步骤 1.1 – 创建 Asp.Net MVC 项目。
使用 Visual Studio 创建一个名为 ServerSentDemo 的新 Asp.Net MVC 项目。您可以从这里下载 Visual Studio。
在下一个对话框中,在模板部分选择“Empty”,在引用部分选择“MVC”,然后单击“OK”。
步骤 1.2- 创建控制器
右键单击 Controllers 文件夹,选择 AddNew>>Controller。
在下一个对话框中选择“MVC 5 Controller - Empty”,然后单击“OK”。
将名称设置为 SSEController,然后单击“OK”。
步骤 1.3- 创建 Action 方法
在 SSEController 中创建名为 Index 的 Action 方法,如下所示。
publicvoid Index()
{
int i = 0;
do
{
Response.Write("Current Date:"+DateTime.Now+"
");
Response.Flush();
System.Threading.Thread.Sleep(2000);
i++;
} while (i<5);
}
正如您所见,Action 方法的返回类型设置为 void,并且通过 Response.Write 和 Response.Flush 显式生成并返回响应。
步骤 1.4 - 执行和测试
按 F5 并执行应用程序。
注意:为了测试返回数据流的服务器代码,Chrome 是最佳选择。因此,请在 Chrome 中执行应用程序。
导航到 SSEController 的 Index Action 并检查输出。
正如您所见,服务器正在以流的方式发送响应。每次数据流之间有 2 秒的间隔。
步骤 2 – 创建客户端代码
在实际场景中,我们不会将上述 URL 直接暴露给最终用户。相反,我们将创建一个用户友好的 URL,并使用 JavaScript 通过 HTML 5 概念 SSE 向上述 URL 发送请求。
步骤 2.1 – 创建 HTML 客户端文件。
只需创建一个 HTML 页面并将其命名为“SSEClient.html”。
将以下 HTML 放入新创建的 html 文件中。
<!DOCTYPEhtml> <html> <head> <metaname="viewport"content="width=device-width"/> <title>Index</title> </head> <body> <ulid="messages"></ul> </body> </html>
这是一个简单的 HTML 内容,带有一个 div 标签。我们将在该 div 中显示服务器发送的响应。
步骤 2.2 – 实现 EventSource
在 HTML 文件中,在 Head 部分创建一个 script 标签,并像这样处理窗口的 onload 函数:
<scripttype="text/javascript">
window.onload = function () {
var o = new EventSource("https://:13971/SSE/Index");
};
</script>
正如您所见,在 onload 函数中创建了 EventSource 对象。这一行将向之前编写的服务器代码发出请求。
步骤 2.3 – 处理响应数据
EventSource 对象公开一个名为“onmessage”的属性。此属性需要一个函数作为值,该函数将在服务器每次发送内容时执行。编写以下代码。
var o = new EventSource("https://:13971/SSE/Index");
o.onmessage = function (e) {
var x = e.data;
var MyDiv = document.getElementById("messages");
MyDiv.innerHTML = MyDiv.innerHTML + "<li>" + x+ "</li>";
}
步骤 2.4 – 执行和测试
只需双击 SSEClient.html 文件并执行它。(确保服务器代码已启动并正在运行。)
奇怪的是,您在输出中看不到任何内容。
出现这种预期输出的原因是运行时错误。
只需在浏览器中打开开发者工具。在 Chrome 中按 F12。
默认情况下,浏览器不允许向与当前页面不在同一域的服务器发出请求。简单来说,我们的 HTML 文件和服务器代码不在同一域。一个域名是“localhost:13971”,而另一个是本地(C://MyRND.....)。为了实现这一点,我们必须在服务器上启用 CORS。
步骤 3 – 在服务器上启用 CORS
打开之前创建的服务器项目。打开 Web.Config 文件。在 configuration 部分创建一个名为 system.webServer 的子标签,如下所示:
......
<system.webServer>
<httpProtocol>
<customHeaders>
<addname="Access-Control-Allow-Origin"value="*"/>
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
注意:如果 configuration 部分已经包含 system.webServer 标签,则不要再次创建它。只需将 httpProtocol 标签放在其中,如上所示。
步骤 4 – 重新执行客户端
现在再次运行 SSECllient.html 文件。这次最后一个错误将得到解决,但不要期望有输出,因为新的运行时错误正在等待。使用开发者工具找出错误原因。
为了处理 SSE,我们需要一个发送数据流且 Content-Type 为“text/event-stream”的服务器。默认情况下它是“text/html”。
步骤 5 – 更改服务器响应 Content Type
打开服务器代码并将 Index Action 方法更改如下:
publicvoid Index()
{
Response.ContentType = "text/event-stream";
int i = 0;
do
{
Response.Write("data:" + DateTime.Now + "\n\n");
Response.Flush();
System.Threading.Thread.Sleep(2000);
i++;
} while (i<5);
}
正如您所见,不仅 Content-Type 设置为“text/event-stream”,而且输出格式也发生了变化。对于 SSE,响应格式必须是“data:ActualDataInStringType\n\n”。这是一个固定格式。“data”是一个关键字,实际响应文本后面必须跟两个“\n”。
步骤 6 – 重新执行客户端
再次运行 SSEClient.html 文件。
所以,终于我们得到了输出。
自动重新连接?
您可能会在上述输出中看到一种奇怪的行为。如果您长时间保持客户端打开,您会注意到输出在 5 次输出后不会结束。在 5 次输出后,服务器将结束响应,因此连接将关闭。3 秒后,将立即向服务器发送一个新请求,并通过流式传输 5 个更多输出。此过程将永远重复。
出现这种行为的原因是 Event Source 对象的自动重连能力。SSE 在 HTTP 协议之上工作。负责将客户端连接到服务器的 EventSource 对象确保连接始终可用。工作原理如下:
- 它向服务器发出第一个请求。
- 服务器开始处理请求并不断发送数据,而不结束响应。在客户端,每次收到响应时,EventSource 对象的 onmessage 回调都会被触发。
- EventSource 对象会在每次连接关闭后 3 秒自动尝试重新连接到服务器。
此重试时间将由服务器决定。服务器可以在响应中返回重试时间。在这种情况下,响应字符串必须包含“retry”关键字。示例如下:
“retry:5000\ndata:SukeshMarla\n\n”
如果连接在收到上述数据后关闭,将在 5 秒后尝试重新连接。
关闭连接
这只能在客户端完成。在客户端,我们必须调用 EventSource 对象的 close 方法。示例:
var o = new EventSource("https://:13971/SSE/Index2");
.
.
.
o.close()
指定事件名称
单个服务器能够在不同情况下向客户端发送不同的事件。这可以通过在响应字符串中包含事件名称来完成。在这种情况下,在客户端,可以设置一个事件监听器来监听该特定事件。让我们举一个简单的例子。
步骤 1 创建一个新的 Action 方法 Index2,如下所示:
publicvoid Index2()
{
Response.ContentType = "text/event-stream";
int i = 0;
do
{
if (i == 0)
Response.Write("event:first\ndata:" + DateTime.Now + "\n\n");
elseif (i == 4)
Response.Write("event:last\ndata:" + DateTime.Now + "\n\n");
else
Response.Write("data:" + DateTime.Now + "\n\n");
Response.Flush();
System.Threading.Thread.Sleep(2000);
i++;
} while (i < 5);
}
步骤 2 – 创建一个新的 HTML 文件,如下所示:
<!DOCTYPEhtml>
<html>
<head>
<metaname="viewport"content="width=device-width"/>
<title>Index</title>
<scripttype="text/javascript">
window.onload = function () {
var o = new EventSource("https://:13971/SSE/Index2");
o.onmessage = function (e) {
var x = e.data;
var MyDiv = document.getElementById("messages");
MyDiv.innerHTML = MyDiv.innerHTML + "<li>" + x+"</li>";
}
o.addEventListener("first", function (e) {
document.getElementById('FirstMessage').innerHTML = e.data;
});
o.addEventListener("last", function (e) {
document.getElementById('LastMessage').innerHTML = e.data;
});
};
</script>
</head>
<body>
<h1id="FirstMessage"></h1>
<ulid="messages"></ul>
<h1id="LastMessage"></h1>
</body>
</html>
步骤 3 – 执行并测试应用程序
执行服务器,然后执行客户端。
就这样。
希望大家都能愉快地阅读这篇文章。请务必分享您对本文的评论和想法。通过twitter或Facebook保持联系,以获取有关我帖子的定期更新。