來自維基百科:Comet是一種用于web的技術,能使服務器能實時地將更新的信息傳送到客戶端,而無須客戶端發出請求,目前有兩種實現方式,長輪詢和iframe流。
簡單的說是一種基于現有Http協議基礎上的長輪詢技術,之所有會產生這種技術的主要原因是Http協議是無狀態的所以客戶端和服務端之間沒辦法建立起一套長時間的連接。比如我們要做一個聊天室,在Web環境下我們通常不能從服務端推送消息到瀏覽器里,而只能通過每個客戶端不斷的輪詢服務器,以獲取最新的消息,這樣一來效率非常低,而且不斷的向服務器發送請求對于訪問量大的應用來說也會造成很大的資源占用。
于是人們就發現了這種技術,向服務器發起一個請求,然后服務器一直不響應這個請求,這樣客戶端和服務端之間就形成了一個長連接,直到服務端響應這個請求后結束本次連接。借用一下IBM里的圖片:
通過Ajax技術可以實現長輪詢的服務器推模型,客戶端和服務端之間通過不斷的發起長輪詢即可以實現數據的交互,這個過程由于是Ajax實現的異步操作所以體驗上會比較好,效率也很高。哎呀呀,說不清楚,找個網上的資料:
Comet方式通俗的說就是一種長連接機制(longlivedhttp)。同樣是由Browser端主動發起請求,但是Server端以一種似乎非常慢的響應方式給出回答。這樣在這個期間內,服務器端可以使用同一個connection把要更新的數據主動發送給Browser。因此請求可能等待較長的時間,期間沒有任何數據返回,但是一旦有了新的數據,它將立即被發送到客戶機。Comet又有很多種實現方式,但是總的來說對Server端的負載都會有增加.雖然對于單位操作來說,每次只需要建議一次connection,但是由于connection是保持較長時間的,對于server端的資源的占用要有所增加。
優點:實時性好(消息延時小);性能好(能支持大量用戶)
缺點:長期占用連接,喪失了無狀態高并發的特點。
應用:股票系統、實時通訊。
參考資料:
Comet:基于HTTP長連接的“服務器推”技術
Asp.Net本身就是為web而生的技術,所以先天是滿足滴?;贏jax技術與Asp.net的異步請求處理可以為Comet提供更加強大的能力。在此隆重推出:IHttpAsyncHandler接口。
IhttpAsyncHandler是繼承于IhttpHandler,但是不同的是IHttpAsyncHandler具有天生的異步能力。他比IHttpHandler多2個方法:
IAsyncResultBeginPRocessRequest(HttpContextcontext,AsyncCallbackcb,objectextraData);voidEndProcessRequest(IAsyncResultresult);
BeginProcessRequest方法返回的是IAsyncResult接口,通常在BeginProcessRequest中處理一些比較繁重費時的任務,比如IO操作,讀取Web服務等。一旦異步操作完成之后,則可以通過EndProcessRequest方法獲得異步的結果。
IHttpAsyncHandler的好處在于,在它處理異步方法的時候,處理請求的線程可以暫時得到釋放,而有空閑去處理其他請求,等異步方法運行完畢之后,在由線程去處理接下來的請求。
有了技術基礎那么來看看如何實現這項技術:
在客戶端我們需要實現發送請求,這方面可以通過Ajax技術來實現,可以通過javascript比較簡單方便的實現異步請求操作。
在服務端監聽專門的請求類型,通過實現IhttpAsyncHandler處理請求,BeginProcessRequest方法中有個AsyncCallback類型的參數cb,這是個回調函數,在asp.net中如果不調用這個回調函數cb則不會響應請求,即不會向客戶端返回內容,這就實現長連接。直到服務端有數據需要返回給客戶端,服務端再調用cb函數以觸發執行EndProcessRequest方法,此時客戶端才會接收到響應包。
在客戶端接收完數據后可以繼續向服務端發起請求,重復這個過程就可以模擬出一個長連接的狀態。
在asp.net里有個開源的組件AspComet比較好的實現了Comet,此組件的開源站點:https://github.com/nmosafi/aspcomet。
在AspComet中的核心主要是通過Ajax發起請求,在服務端基于IhttpAsyncHandler來處理請求,通過一個消息總線處理了一整套的Web推技術。組件分為服務端和客戶端兩部分,都具備良好的擴展性,服務端有比較靈活的委托處理,也可以通過自己繼承實現改寫自己需要的業務處理,非常方便的二次開發。而客戶端也提供了良好的封裝性,支持多種主流js腳本庫,如Jquery,dojo等,在官方的demo中就提供了這兩種腳本庫的實現。
在閱讀了Aspcomet的源代碼后還是比較感嘆的,雖然看起來很費勁,但也著實感覺到了這套代碼對二次開發提供了很好的支持。基本都是面向對象來實現了整個組件,即使是JS也應用了很多的設計模式。下面就這個組件的主要實現做一些介紹:
1、首先必須實現IhttpAsyncHandler接口
CometHttpHandler:IhttpAsyncHandler,此類就是用于異步請求的處理單元,簡單的說就是服務端的入口。在這里通過BeginProcessRequest方法將請求的內容hold住,同時也將callback也Hold住。當然這里有個重點要注意就是MessageBus,所有消息如何hold住就得看它的了,因為有些消息是要即刻返回給客戶端的,而有些是要經過消息總線處理后再轉發的,也有的是要留下來作為長連接的。具體的會在講消息總線時再說明。
最終CometHttpHandler會在請求需要結束時調用EndProcessRequest方法,從而將消息返回給一直等待的連接,客戶端會接收并處理此請求的響應包。
CometHttpHandler就是實現了一個入口和出口,通過IhttpAsyncHandler的異步處理能力從而實現了長連接狀態。
2、消息封包
對于客戶端和服務端之間交互必須有一個消息封包,否則雙方無法做一些約定,畢竟Http協議是松散的無狀態性。在AspComet中實現了一個類:Message
這個類AspComet的開發者叫其:bauyeuxmessage(介紹),貌似是Dojo提出的一套協議。
在這個消息封包中主要介紹幾個:
Channel:消息頻道,用于消息廣播所在的頻道 |
clientId:客戶端的id |
data:數據封包(就是一個object類型,很容易用于擴展的數據包) |
version:版本號,這塊對消息的向下兼容很有作用 |
advice:返回后的處理方式,叫通知也可 |
timestamp:時間戳 |
ext:貌似是擴展用的 |
封包的內容很豐富,有時候協議就是種約定,其實對于我們來說就是一個類嘛,甚至于你可以理解就是一個字符串,客戶端和服務端通過某種約定可以相互解析識別就可。
3、消息總線設計
在說到IhttpAnyscHandler時就提到了消息總線,在AspComet中抽象為一個接口:IMessageBus。
public interface IMessageBus{void HandleMessages(Message[] messages, ICometAsyncResult cometAsyncResult);}
就一個方法,這也就是AspComet用于處理消息的核心方法了,方法的意思就是處理消息,在這個方法里主要是將接收的消息分配給不同的消息處理者進行處理,比如:發起握手協議時要將消息給MetaHandshakeHandler來處理,這就是一個消息中轉中心。
參數messages是消息封包,因為可能是多個消息所以用了數組。
參數cometAsyncResult是對異步請求回調函數的一個二次封裝,主要目的是將callback給接住,不讓其響應,這樣就可以控制什么時候返回響應包了。ICometAsyncResult接口就兩個方法
SendAwaitingMessages是用于將發送等待的消息,主要是用于將要發送的消息寫入到發送管道中
CompleteRequestWithMessages是用于完成請求的過程,主要是調一下callback以告訴IhttpAsyncHandler請求可以返回啦
通過這兩個方法的配合就可以實現將消息向客戶端發送消息啦。
這里提一點:其實向客戶端發送數據的方法很簡單,Http分請求包和響應包,客戶端發給服務端的叫請求(Request),服務端發給客戶端的叫響應(Response),這下應該明白了吧。SendAwaitingMessages就是把數據寫入到Response里,這樣客戶端不就有接收的數據了嗎?
4、各類型消息的處理
在消息總線里提到了消息處理者,為什么會有這個東東存在呢?其實這跟整個的通信過程有關,有握手過程、連接建立過程、斷開過程等等,這就要有一整套處理的方法,也就是要對每種不同的過程做一個類型分開處理。在AspComet中有一個接口:ImessageHandler,它定義了一個消息處理的統一方法:
通過繼承這個接口實現特定的消息處理類就可以完成一些特定的業務了,下面列舉一下各種消息處理類:
MetaHandshakeHandler | 握手協議處理 |
MetaConnectHandler | 連接協議處理 |
MetaDisconnectHandler | 斷開連接處理 |
MetaSubscribeHandler | 訂閱處理 |
MetaUnsubscribeHandler | 停止訂閱處理 |
ForwardingHandler | 消息轉發處理 |
ExceptionHandler | 異常消息處理 |
SwallowHandler | 吞掉消息處理,不給客戶端返回 |
從字面意思應該就可以理解大體了,發什么消息做什么處理,就這個意思。
說到消息的分類處理有個東西必須說明,在MessageBus中如何區分消息類型并找到對應的處理者呢?這就是和ImessagesProcessor的功勞了。
在這個接口中Process方法就是用于處理每條消息的轉發,這個設計也很好,我們甚至可以實現一個自己的MessagesProcessor完全按自己的要求進行消息轉發和處理。在此我還是看一下官方的默認實現吧,在AspComet組件中有個默認的實現MessagesProcessor,代碼如下:
在代碼中可以看到,MessageProcessor是通過一個HandlerFactory來獲取實際的ImessageHandler實例,進而處理消息的,這個過程也不復雜,官方提供的實現就是MessageHandlerFactory類:
在這里處理的方法是根據channel的不同調用相應的handler。
回到ImessageHandler,就得說明一下AspComet對單獨消息處理時釋放出來的委托設計,在Handler執行Handlemessage方法時會調用相應的委托,外部程序可以訂閱委托實現進行一些處理。比如我在握手過程中驗證客戶端合法性,但這個客戶端的合法性需要外部應用程序才能檢驗,怎么辦呢?就可以通過MetaHandshakeHandler中HandleMessage方法釋放出來的兩個委托進行處理,代碼如下:
在這段代碼里有兩個EventHub.Publish(…)的調用,這就是兩個委托調用,我們要實現客戶端合法性驗證就要在第一個委托時做處理,比如上面代碼中有兩行這樣的代碼:
這就是調用一個委托,參數是handshakingEvent。外部訂閱此委托的程序會處理相應的邏輯,如果不符合要求則將其Cancel屬性設置為true,就說明本次消息發送過程要取消掉,并且可以寫入相應的原因。下面是一個實現的例子:
CheckHandshake方法就是訂閱了委托的方法,其中的參數就是從EventHub.Publish(handshakingEvent);中傳過來的。在CheckHandshake里可以取得相應的Client對象并做一些檢查等,如果不符合要求可以將ev.Cancel設置為true,并將原因寫入CancellationReason屬性發回給客戶端。
5、客戶端對象管理
在服務端要管理客戶端的信息,這樣才能在消息廣播時向特定的客戶端發送,為了保持客戶端的應用無關性,AspComet定義了Iclient接口:
Iclient說明
這里定義了對Client的一些基礎定義,繼承此接口實現一個客戶端類就行了。
這里所說有客戶端并非指的實際的瀏覽器端
新聞熱點
疑難解答