本文將介紹 HTML5 Server-Sent Events 機制如何運作?
【致謝】
感謝點部落團隊舉辦 HTML 5 & JavaScript 程式開發實戰贈書活動,讓筆者有幸可以獲得這本好書。
【摘要】
以往在設計網頁應用程式時,若想要從 Client 端定期從 Server 端取得資料以更新給使用者時,可能會用到定期以程式方式重新整理網頁,或進階一點使用 XMLHttpRequest 物件來與 Server 端做資料交換。在 SignalR Framework 問世後要做到上述的效果便顯得更加簡便且有效率了,許多前輩已經寫了不少跟 SignalR 相關的好文,有興趣的朋友可以瀏覽參考資料中的網址。而 HTML5 也提供實作單向 Server-Sent Events 以及雙向的 WebSocket 資料傳輸的物件,本文將針對 Server-Sent Events 來做介紹。
【實作 Server 端】
所謂 Server-Sent Event 中文解釋為伺服器推送或伺服器推播,主要是用來將伺服器上的資料自動傳輸至 Client 端,使用的是 HTTP 通訊協定。首先需要 Server 端的應用程式,筆者以 ASP.NET 泛型處理常式來實作本文 Server 端的應用程式,您也可以利用 PHP、JSP 等其他 Server 端的程式語言來實作。
Server 端的程式推送給 Client 端的封包是由 event(事件或識別名稱,預設值為 message)、data(要傳送給 Client 端的資料內容)、id(Server 端推送資料到 Client 端時的事件ID) 和 retry(以毫秒為單位,用來定義兩次要求之間的間隔時間)等四個欄位所組成,其格式為【欄位名稱:欄位內容】。其中又以 data 欄位最為重要,若 Server 端沒有傳送該欄位給 Client 端,即便您定義了資料接收的事件,該事件仍不會被觸發。
下列程式碼用來示範將回傳目前 Server 端的系統時間給 Client 端的瀏覽器,其中 ContentType 必須要設定為【text/event-stream】整個 Server-Sent Events 機制才能正常運作。
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace ServerSendEvents { /// <summary> /// ServerSide 的摘要描述 /// </summary> public class ServerSide : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/event-stream"; context.Response.Write(string.Format("data:{0}\n", DateTime.Now.ToString())); } public bool IsReusable { get { return false; } } } }
【實作 Client 端】
Client 端要能接收 Server 端的推送資料,必須先建立 EventSource 物件,例如下列的程式碼,透過建構式傳入的 URL 來設定要從哪裡接收資料,接著 EventSource 物件便會以非同步方式來向 URL 參數所對應的 Server 端提出要求,以接收回傳的資料。
var es = new EventSource(url);
處理 Server 端推送下來的資料時,有三個事件屬性可以使用,分別是 onopen(連線建立時觸發)、onmessage(接收到 Server 端推送的訊息時觸發) 與 onerror(連線失敗時觸發)。
var es = new EventSource("ServerSide.ashx"); var stat; es.onerror = function (e) { switch (e.target.readyState) { case EventSource.CONNECTING: stat = "等待重新連線"; break; case EventSource.CLOSED: stat = "連線失敗,停止連線"; break; } document.getElementById("divMsg").innerHTML += stat + "<br/>"; } es.onmessage = function (e) { document.getElementById("divMsg").innerHTML += "現在時刻:" + e.data.toString() + "<br/>"; } es.onopen = function (e) { switch (e.target.readyState) { case EventSource.CONNECTING: stat = "Connecting"; break; case EventSource.OPEN: stat = "Open"; break; case EventSource.CLOSED: stat = "Closed"; break; default: stat = "n/a"; break; } document.getElementById("divMsg").innerHTML += "連線狀態:" + stat + "<br/>" }
執行結果如下:
若您希望與 Server 端停止連線,可以呼叫 EventSource 的 Close 方法,例如下列的程式碼(其中 es 為上面所宣告的 EventSource 物件):
function Stop() { es.close(); }
【變更重新連線的間隔時間】
由上一節的執行結果可知,預設 Server 端每 5000 毫秒推送一次資料到 Client 端的瀏覽器上,若您要調整這個間隔時間,可以在 Server 端設定 retry 欄位的值,例如下列的程式碼:
context.Response.Write(string.Format("retry:{0}\n", "1000"));
修改過後重新執行 Client 端網頁,您將看到如下圖的結果,已經由原本的每 5000 毫秒推送一次,變成每 1000 毫秒推送一次。
【參考資料】
- HTML5 & JavaScript 程式開發實戰 - 導讀