有时候进行网站开发会遇到消息推送、即时信息等功能需要,以往使用循环的异步网络请求实现“轮询”操作,但现在更推荐使用 HTML5 的 Server-Sent Events SSE 特性,此方法比多次循环的网络请求更加有效率,除了旧版的 Internet Explorer 外,现在主流的浏览器都支持此特性,这在浏览器调试器中是这样呈现的:

看起来只有一次请求,实则数据都集成在了 EventStream 里,下面用个 C# 和 ASP.NET 的一般处理程序配合前端实现简单的示例并讲解基本用法:
首先是前端与脚本代码,用来接收服务端推送的消息:
<p id="p1"></p>
<script>
var source = new EventSource("Handler1.ashx", {
withCredentials: true
});
source.onopen = function (event) {
console.log("onopen", event)
};
source.onerror = function (event) {
console.log("onerror", event)
source.close()
};
//source.onmessage = function (event) {
// console.log("onmessage", event)
// document.getElementById("p1").innerHTML += event.data + "<br>";
//};
source.addEventListener("gett", function (event) {
console.log("gett", event)
document.getElementById("p1").innerHTML += event.data + "<br>";
})
</script>
上面的代码中监听了一个名叫 gett 的事件并且注释了一段事件代码,这里的事件名称由服务器端定义,如果服务器端没有定义事件名称,那么将会执行到被注释的 onmessage 事件代码那里,定义了事件名称的话就会跳过 onmessage 事件;如果遇到了错误将会执行 onerror 事件里的代码,默认情况下遇到错误后会重新执行连接,所以我们在遇到错误时就关闭掉这个 EventSource;每第一次连接时就会执行 onopen 事件里的代码,包括发生错误后的重连。
前端的代码已结束,下面我们看看 ASP.NET 服务器端的一般处理程序 Handler1.ashx 中的代码:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/event-stream";
context.Response.Expires = -1;
while (true)
{
context.Response.Write("id:" + DateTime.Now.ToString("yyyyMMddHHmmss") + "\n" + "event:gett" + "\n" + "data:" + DateTime.Now.ToString() + "\n" + "retry:" + 2000 + "\n\n");
//context.Response.Write("data:" + DateTime.Now.ToString() + "\n" + "retry:" + 2000 + "\n\n");
context.Response.Flush();
Thread.Sleep(2000);
}
}
服务器端约定了 4 个字段:
event 定义事件的名称,如上面的 gett ,可选。
id 定义事件的 ID 字段值,可选。
data 用于传输数据的字段。
retry 重新连接的时间,以毫秒为单位,如果发生错误会等待此时间然后重试,可选。
这些字段的名称后紧跟一个英文冒号 :,然后是其值,字段之间需以一个换行符 \n 分隔,并以两个换行符 \n\n 结尾。
注:如果在线程中不使用循环而直接一次性输出后中断,再配合使用 retry 字段似乎也可以实现相应的功能,但这样每一次都会经过 onopen 和 onerror 事件,并且会重复执行前后端的交互请求,这几乎等同于轮询的方式,所以不推荐这样做。