1. 程式人生 > >HTML5伺服器推送訊息的各種解決辦法

HTML5伺服器推送訊息的各種解決辦法

AJAX

正常的一個頁面在瀏覽器中是這樣工作的:

  1. 使用者向給予瀏覽器一個需要訪問的地址
  2. 瀏覽器根據這個地址訪問伺服器,並與伺服器之間建立一個TCP連線(HTTP請求)
  3. 伺服器根據這個地址和一些其它資料,組建一段HTML文字,將寫入TCP連線,然後關閉連線
  4. 瀏覽器得到了來自伺服器的HTML文字,解析並呈現了瀏覽器上給使用者瀏覽

此時,使用者點選了網站上任何一個<a>或觸發任何一個<form>提交時:

  1. 瀏覽器根據form的引數或者a的引數,作為訪問的地址
  2. 與伺服器建立TCP連線
  3. 伺服器組建HTML文字,然後關閉連線
  4. 瀏覽器將當前顯示的頁面摧毀,並按照新的HTML文字呈現一個新的頁面給使用者

我們不難發現的是整個過程中間,一旦建立了一個連線,頁面就無法再維護住了。整個過程看上去有點強買強賣,也許我只要一杯新的可樂,但你非要給我一整個套餐組合。

此時我們可以瞭解一下XmlHttpRequest元件,這個元件提供我們手動建立一個HTTP請求,傳送我們想要的資料,伺服器也可以只返回我們想要的結果,最大的好處是,當我們收到伺服器的響應時,原來的頁面沒有被摧毀。這就好比,我喊一句"我的咖啡喝完了,我要續杯",然後服務員就拿了一杯咖啡過來,而不是會把我沒吃完的套餐全部倒掉。

當我們利用AJAX實現伺服器推送時,其實質是客戶端不停地向伺服器詢問"有沒有給我的訊息呀?",然後伺服器回答"有"或"沒有"來達到的實現效果。它的實現方法也很簡單,利用jQuery框架封裝好的AJAX呼叫也很方便:

function getMessage(fn) {
    $.ajax({
        url: "Handler.ashx", //一個能夠提供訊息的頁面
        dataType: "text",    //響應型別,可以是JSON,XML等其它型別
        type: "get",         //HTTP請求型別,還可以是post
        success: function (d, s) {
            fn(d);           //得到了正常的響應時,利用回撥函式通知外部
        },
        complete: function
(x, s) { setTimeout(function () { getMessage(fn); }, 5000); //無論響應成功或失敗,在若干秒後再詢問一次伺服器 } }); }

 通過上面的程式碼,可以每隔5秒詢問一次伺服器是否有需要處理的訊息,通過這種方式可以達到推送的效果,但是會存在一個問題:

  1. 間隔時間越快,推送的及時性越好,伺服器的消費越大;
  2. 間隔時間越慢,推送的及時性越低,伺服器的消費越小。

而且嚴格地來說,這種實際方式,並不是真正意義上的伺服器主動推送訊息,但由於早期技術手段缺乏,所以AJAX輪循成為了一種很普遍的手段。

Comet

我們知道HTTP請求其實是基於TCP連線實現的,再看看之前說的HTTP請求處理過程:

  1. 客戶端與伺服器建立TCP連線
  2. 伺服器根據客戶端提交的報文處理並生成HTML文字
  3. 將HTML封閉成為HTTP協議報文並返回給客戶端
  4. 關閉連結。

看到這個處理過程,我們不難聯想到,如果把第4步——關閉連線給省掉,那不就相當於有一個長連線一直被維持住了麼。通過對服務端的一些操作,我們可以直接將資料從這個TCP連線傳送客戶端了。

通過這種技術,我們可以大大提高伺服器推送的實時性,還可以減去服務端不停地建立、施放連線所形成的開銷。

目前市面上有不少基於AJAX實現的Comet機制,但主要有兩種方式:

  1. 建立連線後依然使用"詢問"+"應答"的模式。雖然工作方式沒變,但是因為減去了每次建立與施放連線的工作,所以效能上提升了很多。而且伺服器對TCP連線可以有上下文的定義,而不像以前的AJAX完全是無狀態的。
  2. 通過對Stream的寫入實現伺服器將資料主動傳送到客戶端。因為是TCP連線,所以通過對伺服器的程式設計,我們可以主動的把資料從服務端傳送給客戶端,從模式上真正建立起了推送的概念。

Server-Sent

Server-Sent是HTML5提出一個標準,它延用了Comet的思路,並對其進行了一些規範。使得Comet這項技術由原來的分支衍生技術轉成了正統的官方標準。

它的原理與Comet相同,由客戶端發起與伺服器之間建立TCP連線,然後並維持這個連線,至到客戶端或伺服器中的做任何一放斷開,ServerSent使用的是"問"+"答"的機制,連線建立後瀏覽器會週期性地傳送訊息至伺服器詢問,是否有自己的訊息。

這項標準不僅要求了支援的瀏覽器能夠原生態的建立與伺服器的長連線,更要求了對JavaScript指令碼的統一性,使得兼程該功能的瀏覽器可以使用同一套程式碼完成Server-Sent的編碼工作。

建立程式碼非常簡單:

//定義一個ServerSent物件
var s = new EventSource("Handler.ashx");
//當收到一個非自定義事件時的回撥函式
s.onmessage = function (e) {
    alert(e.data);
};
//當收到一個被伺服器命名為MyEvent事件訊息時的回撥函式
s.addEventListener("MyEvent", function (e) {
    alert(e.data);
});

而伺服器的程式碼也很簡單:

public class Handler : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/event-stream";
        context.Response.Expires = -1;
        context.Response.Write("event: MyEvent\r\n");       //事件型別,使用\r\n結尾
        context.Response.Write("data: HelloWorld!\r\n");    //事件資料,換行時使用\r\n,並在新行再加上data:
        context.Response.Write("data: I'm server!\n\n");    //事件資料結束,使用\n\n
        context.Response.Flush();                           //這裡不能用End,否則是關閉連線的
    }

    public bool IsReusable
    {
        get
        {
            return true;
        }
    }

}

兩小段程式碼,就已經具備了伺服器訊息推送了。

總得來說SeverSent就是HTML5規範下的Comet,具有更好的統一性,而且簡單好用。

WebSocket

看名字就知道了,這是一個可以用在瀏覽器上的Socket連線。

這也是一個HTML5標準中的一項內容,他要求瀏覽器可以通過JavaScript指令碼手動建立一個TCP連線與服務端進行通訊。

WebSocket不包含太多的額外功能,僅僅就是TCP連線的幾項基本功能:建立,臨時以及傳送。

另外WebSocket使用了ws和wss協議,需要伺服器有與之握手的演算法才能將連線開啟。

所以WebSocket相對於之前幾種手段來說,其編碼量是最大的,但由於沒有其它的約束,因此它也可以自由地實現所有可能的功能。

即可以滿足"問"+"答"的響應機制,也可以實現主動推送的功能。

與ServerSent相同,HTML5也對WebSocket呼叫的JavaScript進行規範,我們可以弄過很簡單的一程式碼構建一個WebSocket連線

var ws = new WebSocket("ws://192.168.0.105:10080"); //連線伺服器        

ws.onopen = function (event) { alert("已經與伺服器建立了連線\r\n當前連線狀態:" + this.readyState); };
ws.onmessage = function (event) { alert("接收到伺服器傳送的資料:\r\n" + event.data); };
ws.onclose = function (event) { alert("已經與伺服器斷開連線\r\n當前連線狀態:" + this.readyState); };
ws.onerror = function (event) { alert("WebSocket異常!"); };

還可以通過send的方式傳送訊息

ws.send("Hello World");

WebSocket具有較為複雜的協議,需要在服務端做額外程式設計才能進行資料通訊。有關協議的詳細內容,我會在以後的文章中進行解釋。

WebSocket + MessageQueue

MessageQueue,簡稱MQ,也就是訊息列隊。是一種常常用於Tcp服務端的技術。通過生產和訪問各種訊息型別,MQ伺服器會將生產者所生成的訊息發給感興趣的客戶端。市面上有很多的MQ框架,比如:ActiveMQ。

ActiveMQ已經支援了WebSocket協議,也就意味著,WebSocket已經可以作為一個生產者或一個消費者,與MQ伺服器連線。

開發者可以通過MQTT的JS指令碼,連線上MQ伺服器,同時將Web伺服器也連上MQ伺服器,從此可以告別了Http通訊協議,完完全全使用Socket通訊來完成資料的交換。

總結:

總得來說,在HTML5規範下,最推薦使用ServerSent和WebSocket的方式進行伺服器訊息的推送。

對比這兩種方式。

ServerSent的方式,可以使服務端的開發依然依用以前的方式,但是其工作方式與Comet類似。

而WebSocket的方式,則對服務端的開發有著較高的要求,但其工作方式是完全的推送。

我本人其實挺偏向WebSocket + MQ的工作方式,但是對於老專案的翻新,還是用SeverSent比較好

結尾

本文為作者原創,轉載請註明出處:http://www.cnblogs.com/ShimizuShiori/p/5464063.html

文章中的相關程式碼可以在 http://j.zizhusoft.com/Develop/Explorer.aspx 中的ServerSent目錄中檢視