網上有關「服務器推送」的介紹很是多,其中一種實現方式就是採用comet技術,在瀏覽器與服務端之間創建一個http協議的「長鏈接」,所謂「長鏈接」,就是指瀏覽器到服務端的http請求不會立刻獲得服務端的應答,而是當知足必定條件的時候,服務器端才「主動」將數據返回給瀏覽器,這時候一次http請求才完成,普通http鏈接與http長鏈接見下圖:jquery
圖1web
如上圖,左邊爲通常http鏈接,服務端收到瀏覽器的http請求後會當即作出應答,右邊爲http長鏈接,服務端收到瀏覽器的http請求後,若是有數據須要返回,則當即返回,不然,服務端會「維持住」這個http請求,也就是說,服務端凍結了該次http請求,直到服務端有數據須要返回給瀏覽器或者超時,服務端才「解凍」該次http請求,這時候,一次「瀏覽器->服務端」的http請求才完整結束。ajax
「長鏈接」的做用很明顯,它能讓服務端「主動」(注意這裏的主動加了雙引號)將數據發送給瀏覽器,是的,你沒聽錯,傳統的Web程序只能是瀏覽器主動請求服務端,服務端再做出應答,而如今,服務端竟然能夠「主動推送」數據給瀏覽器了。既然服務端如今能夠「主動推送」數據給瀏覽器,那麼咱們能夠完成許多以前不能作到的事情,好比「即時通信」、「實時監控」等相似客戶端須要實時更新數據的應用程序了。這裏插一句,若是不採用服務端「主動推送」的方式,咱們確實能夠按照傳統瀏覽器端使用ajax主動循環請求服務端,來實現所謂的「實時」更新數據的效果,好比每秒ajax請求服務端,來刷新界面數據,可是這種方式的缺點可想而知,無論服務端有沒有需更新的數據,瀏覽器都必須不斷地循環去請求(有關ajax輪詢的缺點請參考網上其餘文章)。chrome
具體實現comet的關鍵有如下幾點:編程
1)服務端不會當即響應瀏覽器的http請求(除非當時有數據須要返回給瀏覽器);json
2)瀏覽器處理完服務端返回的數據後,要當即從新創建一個「http長鏈接」,供下次使用;數組
3)瀏覽器在處理服務端返回的數據時(下一次長鏈接創建以前),若是服務端有新的數據須要發送給瀏覽器,那麼服務端必須將這些新數據保存起來,等下次「長鏈接」創建後,再一塊兒發送給瀏覽器;瀏覽器
4)要想服務端可以實時地、不斷地「主動推送」數據到瀏覽器,瀏覽器與服務端之間必須無時無刻保持一條「http通道」(也就是http長鏈接),服務端可以藉助該通道將數據返回給瀏覽器;緩存
5)瀏覽器一接收到服務端返回的數據,一次「http長鏈接」就結束了,須要從新創建下一個。服務器
若是咱們以socket編程的角度看「http長鏈接」,它會是這樣的:
圖2
如上圖,每一個browser必須時刻存在一個http下行通道(服務端->瀏覽器,圖中(1)處),這樣任什麼時候候服務端都能「主動推送」數據給browser,同時,每一個瀏覽器都可以發起其餘正常http請求(圖中(2)處),這樣一來,1號瀏覽器經過普通http請求發送數據(get/post方式)給服務端,服務端就能夠立馬將數據「推送」給2號瀏覽器(使用http下行通道),沒錯,這不就是socket編程嗎?
asp.net中可使用「異步編程模型」(APM)簡單地實現comet,具體涉及到IHttpAsyncHandler接口以及它的BeginProcessRequest和EndProcessRequest兩個方法,當咱們接收到來自瀏覽器發起的「http長鏈接」請求時,咱們只調用IHttpAsyncHandler.BeginProcessRequest方法去異步處理,因爲咱們不當即調用IHttpAsyncHandler.EndProcessRequest方法,因此這個http請求不會立馬結束(也就是說,該請求被服務端凍結住了),等服務端有數據時,咱們再經過相似Response.Write()方法將數據發送給瀏覽器,同時調用IHttpAsyncHandler.EndProcessRequest方法結束異步處理http請求,這時候,瀏覽器端會接收到服務端「主動推送」的數據,瀏覽器端一次完整的http請求到此時才結束,緊接着,瀏覽器端經過腳本再次發起一個「http長鏈接」的請求,依次循環。
文章最後上傳一個即時通信的demo,每一個登陸的用戶均可以發送消息,在線用戶均能及時收到服務端「推送」來的數據(包括上線、下線以及消息內容等),因爲服務端發送的數據種類比較多,我簡單的指定了一個協議(protocol),相似以下:
1 協議相似socket通信編程中的協議(相似): 2 [4bytes]+[16bytes]+[some]+[1byte] 3 消息類型+消息長度+消息內容+驗證位 4 5 (Web Server->Browser方向)json格式數據包 每一個包相似C++中的結構體: 6 7 有用戶上線: 8 { 9 "type":"login", 10 "login_name":"xiaozhi_5638", 11 "login_time":"2014-04-15 12:34:19" 12 } 13 14 有用戶發送消息: 15 { 16 "type":"sendmsg", 17 "msg":"hello world![emo:23]", 18 "send_name":"xiaozhi_5638", 19 "send_time":"2014-04-15 12:34:19" 20 } 21 22 有用戶下線: 23 { 24 "type":"logout", 25 "logout_name":"xiaozhi_5638", 26 "logout_time":"2014-04-15 12:34:19" 27 } 28 29 心跳包: 30 { 31 "type":"heartbeat", 32 "time":"2014-04-15 12:34:19" 33 } 34 35 本身登陸結果: 36 { 37 "type":"login_result", 38 "result":"true", //or false 39 "online_users":["xiaozhi_5638","somebody","zhangsan"], //登陸成功 返回在線用戶 40 "time":"2014-04-15 12:34:19" 41 } 42 43 本身發送消息結果 44 { 45 "type":"sendmsg_result", 46 "result":"true", //or false 47 "msg":"hello world![emo:23]", 48 "time":"2014-04-15 12:34:19" 49 } 50 51 數據包集合(上面的都是單個數據包,data_list中包含多個數據包集合) 用於一次性將服務端緩存的數據包傳遞到browser 52 { 53 "type":"data_list", 54 "list":[{"type":"login","login_name":"xiaozhi_5638","login_time":"2014-04-15 12:34:19"},{"type":"sendmsg","msg":"hello world!","send_name":"xiaozhi_5638","send_time":"2014-04-15 12:34:19"}] //數組類型 包含多個數據包 55 } 56 Browser->Web Server方向的數據 以普通get/post方式傳遞
服務端到瀏覽器端的數據均以json格式傳遞,瀏覽器到服務端採用jquery寫好的ajax庫方式。瀏覽器端的腳本只須要解析服務端傳遞回來的json數據,而後更新界面,緊接着發起下一個「http長鏈接」請求。js腳本發起http長鏈接代碼以下:
1 function Open_Http_Channel() //開啓一個http通道(http長鏈接) 2 { 3 $.ajax( 4 { 5 url:"chat_aspx.ashx", //action處理頁面 6 type:"post", //數據傳遞方式 7 data:{"requestType":"a_long_connection","id":$("#input_user_name").val()}, //上傳參數 8 dataType:"json", //返回數據類型 9 success:function(data) //解析返回的json包 10 { 11 //接收web server端返回的數據 開始解析數據 參見protocol.txt 12 if(data.type == "login") //有人上線 13 { 14 ShowLogin(data); //顯示登陸信息 15 } 16 if(data.type == "sendmsg") //有人發送消息 17 { 18 ShowMsg(data.msg,data.send_time,data.send_name); //顯示消息 19 } 20 if(data.type == "logout") //有人下線 21 { 22 Logout(data); 23 } 24 if(data.type == "data_list") //數據包集合 25 { 26 for(i in data.list) //遍歷數據包集合 27 { 28 if(data.list[i].type == "login") 29 { 30 ShowLogin(data.list[i]); //顯示登陸信息 31 } 32 if(data.list[i].type == "sendmsg") 33 { 34 ShowMsg(data.list[i].msg,data.list[i].send_time,data.list[i].send_name); //顯示消息 35 } 36 if(data.list[i].type == "logout") //下線 37 { 38 Logout(data.list[i]); 39 } 40 //... 41 } 42 } 43 //... 44 //... 45 //...定義的其餘協議 在此處解析 46 Open_Http_Channel(); //立刻開啓第二次http通道 47 }, 48 error:function(xhr,info,obj) 49 { 50 Open_Http_Channel(); //立刻開啓第二次http通道 51 } 52 }); 53 }
其餘具體詳細的說明參見源代碼。效果圖一張圖3(gif表情沒有解析替換,直接顯示的文本)
圖3
源碼地址:http://files.cnblogs.com/xiaozhi_5638/comet_in_aspnet.rar vs2008
用到了jquery以及跟它相關的幾個界面庫。注意不要在同一個瀏覽器上登陸太多用戶,由於瀏覽器會爲每一個用戶維持一個http鏈接,而瀏覽器對http請求數量有限制(筆者用的chrome上限爲6個),超過上限的話,以後全部http請求都會被阻塞。