通過在HTML頁面裏陷入一個隱藏的iframe,然後將這個iframe的src屬性設爲對一個長連接的請求(利用chunked傳輸response),服務器端就能源源不斷地往客戶推送數據。
基於流方式的”服務器推”模型:
服務端在接到客戶端的請求時,通過Response的Flush()方法發送數據,可以使用定時器的方式發送數據,沒有數據也發送”無數據”,讓客戶端保持長連接,直至客戶端斷開連接,請求結束。每次數據傳送不會關閉連接,連接只會在通信出現錯誤時,或是連接重建時關閉(一些防火牆常被設置爲丟棄過長的連接, 服務器端可以設置一個超時時間, 超時後通知客戶端重新建立連接,並關閉原來的連接)。
實現代碼:
頁面Default.aspx,用來展示數據:
1: 數據列表:o<br />
2: <div id="con" style=" width:400; height:200px; border:1px solid #FF0">
3: </div>
4: <iframe id="flush" src="Flush.aspx" style=" display:none" />
5:
ifame的src對應的Flash.aspx後臺代碼,模擬後臺發送數據:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: string startHTML = "<!DOCTYPE HTML PUBLIC /"-//W3C//DTD XHTML 1.0 Transitional//EN/" /"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd/">" + Environment.NewLine
4: + "<html xmlns=/"http://www.w3.org/1999/xhtml/" >" + Environment.NewLine
5: + "<head>" + Environment.NewLine
6: + "</head>" + Environment.NewLine
7: + "<body>" + Environment.NewLine;
8:
9: startHTML += new String(' ', 1024) + Environment.NewLine;
10:
11: Response.Write(startHTML);
12: Response.Flush();
13:
14: string data = "<script type=/"text/javascript/">parent.$('#con').append(/"{0}/");</script>";
15: Response.Write(string.Format(data, "開始發送數據:<br/>"));
16: Response.Flush();
17:
18: int index = 0;
19: while (true)
20: {
21: System.Threading.Thread.Sleep(2000);
22: if (index % 2 == 0)
23: {
24: Response.Write(string.Format(data, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 服務端發送數據<br/>"));
25: }
26: else
27: {
28: Response.Write(string.Format(data, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 無數據發送<br/>"));
29: }
30: Response.Flush();
31:
32: index++;
33: }
34: }
35:
運行Default.aspx的結果:
使用 iframe 請求一個長連接有一個很明顯的不足之處:IE、Morzilla Firefox 下端的進度欄都會顯示加載沒有完成,而且 IE 上方的圖標會不停的轉動,表示加載正在進行;刷新當前頁面反應也是會很慢。
解決IE的進度欄顯示加載沒有完成,可以使用一個稱爲“htmlfile”的 ActiveX,是Google 的天才們使用的方法,該控件也被用到gmail+gtalk 產品中。
修改Default.aspx的頁面代碼:
1: 數y據Y列D表í:o<br />
2: <div id="con" style=" width:400; height:200px; border:1px solid #FF0">
3: </div>
4: <script type="text/javascript">
5: function getData(d)
6: {
7: $("#con").append(d);
8: }
9:
10: function rpc_iframe() {
11: var transferDoc = new ActiveXObject("htmlfile");
12: transferDoc.open();
13: transferDoc.write("<html>")
14: transferDoc.write("<div><iframe src=/"Flush.aspx/"></iframe></div>");
15: transferDoc.close("</html>");
16: transferDoc.parentWindow.getData = getData;
17: setInterval(function () { }, 10000); //不加這句會使連接斷開
18: }
19:
20: rpc_iframe();
21: </script>
22:
修改Flush.aspx.cs代碼:
1: //string data = "<script type=/"text/javascript/">parent.$('#con').append(/"{0}/");</script>";
2: string data = "<script type=/"text/javascript/">parent.getData(/"{0}/");</script>";
3: