Html5实践之EventSource
最近嘗試了一下服務(wù)器端的推送,之前的做法都是客戶端輪詢,定時向服務(wù)器發(fā)送請求。但這造成了我的一些困擾:
1:輪詢是由客戶端發(fā)起的,那么在服務(wù)端就不能判別我要推送的內(nèi)容是否已經(jīng)過期,因為我很難判斷某個信息是否已經(jīng)推送給全部的客戶端,那么服務(wù)端就需要緩存大量的數(shù)據(jù)。如果數(shù)據(jù)保存在數(shù)據(jù)庫,那么還要每次請求都需要查詢數(shù)據(jù)庫,這對數(shù)據(jù)庫和系統(tǒng)設(shè)計都是一個很大的挑戰(zhàn)。
2:請求的頻率太高,每次的請求包中含有同樣的數(shù)據(jù),這對pc來說也許算不得什么,但是對于移動客戶端來講,這應(yīng)該不是最佳的方案。尤其是遇到還要做權(quán)限判斷的時候,那么服務(wù)端的邏輯和效率也會造成用戶體驗的降低。
好在Html5為我們提供了一種方式:?Server-Sent Events包含新的HTML元素EventSource和新的MIME類型 text/event-stream?來完成我的需要。
因為是第一次接觸Html5,?w3school中也有對EventSource的說明和使用?。于是馬上開始著手實踐。
頁面腳本就不用說了,按照w3school的方式即可。
var source=new EventSource("demo_sse.php"); source.onmessage=function(event) { document.getElementById("result").innerHTML+=event.data + "<br />"; };服務(wù)端的代碼也是如初一折,w3school提供了php和asp的代碼:
//php方式 <?php header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); $time = date('r'); echo "data: The server time is: {$time}\n\n"; flush(); ?> //asp方式 <% Response.ContentType="text/event-stream" Response.Expires=-1 Response.Write("data: " & now()) Response.Flush() %>- 把報頭 "Content-Type" 設(shè)置為 "text/event-stream"
- 規(guī)定不對頁面進(jìn)行緩存
- 輸出發(fā)送日期(始終以 "data: " 開頭)
- 向網(wǎng)頁刷新輸出數(shù)據(jù)
也許大家應(yīng)該注意到,php和asp的案例有一點(diǎn)不一樣,就是php推送的信息一個使用了"\n\n"作為結(jié)束標(biāo)志,而asp卻沒有。而本人實踐則是用asp.net+mvc,經(jīng)過測試,如果不以"\n\n"作為結(jié)束標(biāo)志,那么客戶端將不能接收到推送的值。還有需要特別聲明一下:推送的信息格式必須為”data:內(nèi)容\n\n“,否則。。。
public void Subscribe(){HttpContext.Response.ContentType = "text/event-stream";HttpContext.Response.CacheControl = "no-cache"; HttpContext.Response.Write("data:" + DateTime.Now.ToString()+ "\n\n"); HttpContext.Response.Flush(); }至此,客戶端應(yīng)該可以收到服務(wù)端推送的值。而如此簡單的結(jié)構(gòu)真的可以完成我們需要的功能設(shè)計嗎??
此例我們只是推送了一個當(dāng)前時間,而我們實際要推送的值是不斷變化的,不然也就沒有推送的必要了。
于是我想到了將訂閱的請求保存起來,當(dāng)需要推送的時候,在對每個請求進(jìn)行循環(huán)推送,于是有了下面的代碼:
public class PublishService{private static IDictionary<string, HttpResponseBase> contexts = new Dictionary<string, HttpResponseBase>(); public static void AddHttpContext(HttpContextBase context) { var token = context.GetToken(”CookieName“); if (!contexts.Keys.Contains(token)) contexts.Add(token, context.Response); } private static void Publish() { foreach (var context in contexts.Values) { context.ContentType = "text/event-stream"; context.CacheControl = "no-cache"; msg = GetData(context.GetToken("CookieName")); context.Write("data:" + msg + "\n\n"); context.Flush(); } } ??????? public void Subscribe() ??????? { ??????????? PublishService.AddHttpContext(HttpContext); ??????????? PublishService.Publish(); ??????? } }可是在進(jìn)行測試的時候Chrome告訴我:?EventSource's response has a MIME type ("text/plain") that is not "text/event-stream". Aborting the connection.
而FF告訴我:?Firefox 無法建立到 http://localhost:8000/Location/Notification/Subscribe 服務(wù)器的連接。
經(jīng)過調(diào)試發(fā)現(xiàn),在每次flush的時候發(fā)生異常:Server cannot flush a completed response.這究竟是為啥呢?不論是google,還是baidu,我都沒能找到合適的答案,所以此案至今未結(jié),如哪位知道請細(xì)說一二。
于是乎,我放棄了這種方式,轉(zhuǎn)而就推送一個時間看看是什么效果。結(jié)果發(fā)現(xiàn)Chrome每隔3秒向客戶端推送一次,而FF是每5秒推送一次。有了這樣一個發(fā)現(xiàn),那么服務(wù)端的設(shè)計就應(yīng)該是另一個樣子:
public void Subscribe(){var data = GetData();HttpContext.Response.ContentType = "text/event-stream"; HttpContext.Response.CacheControl = "no-cache"; HttpContext.Response.Write("data:" + data + "\n\n"); HttpContext.Response.Flush(); }服務(wù)端只需要提供一個服務(wù)GetData(),這個服務(wù)用來獲取我們需要推送的信息,而根據(jù)?Server-Sent Events規(guī)范推薦如果沒有其他的數(shù)據(jù)要發(fā)送,那么定期的發(fā)送keep-alive注釋。?其他的事情就不用我們操心了。
這只是一個簡單的使用,因為本人在使用EventSource的時候走了一些彎路,所以寫出來,希望能對大家有些幫助。
求教:EventSource.onopen和EventSource.onerror每次都會觸發(fā)這兩個事件,而且每次得到的結(jié)果都一樣,為何?
總結(jié)
以上是生活随笔為你收集整理的Html5实践之EventSource的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Microsoft Visual Stu
- 下一篇: 模拟alert和confirm