使用SuperWebSocket 構建實時 Web 應用

Web 應用的信息交互過程一般是客戶端經過瀏覽器發出一個請求,服務器端接收和審覈完請求後進行處理並返回結果給客戶端,而後客戶端瀏覽器將信息呈現出來,這種機制對於信息變化不是特別頻繁的應用尚能相安無事,可是對於那些實時要求比較高的應用來講,好比說在線遊戲、在線證券、設備監控、新聞在線播報、RSS 訂閱推送等等,當客戶端瀏覽器準備呈現這些信息的時候,這些信息在服務器端可能已通過時了。因此保持客戶端和服務器端的信息同步是實時 Web 應用的關鍵要素,對 Web 開發人員來講也是一個難題。在 WebSocket 規範出來以前,開發人員想實現這些實時的 Web 應用,不得不採用一些折衷的方案,其中最經常使用的就是輪詢 (Polling) 和 Comet 技術,而 Comet 技術其實是輪詢技術的改進,又可細分爲兩種實現方式,一種是長輪詢機制,一種稱爲流技術。下面咱們簡單介紹一下這幾種技術:javascript

輪詢css

這是最先的一種實現實時 Web 應用的方案。客戶端以必定的時間間隔向服務端發出請求,以頻繁請求的方式來保持客戶端和服務器端的同步。這種同步方案的最大問題是,在一些數據更新比較頻繁的應用裏,頁面的數據要想獲得最新的結果須要從新刷新頁面,但這樣會產生大量的冗餘數據在服務器和客戶端傳輸,另外因爲頁面是同步處理的,因此在頁面加載完畢以前是不能繼續操做的。當客戶端以固定頻率向服務器發起請求的時候,服務器端的數據可能並無更新,這樣會帶來不少無謂的網絡傳輸,因此這是一種很是低效的實時方案。html

長輪詢:java

長輪詢是對定時輪詢的改進和提升,目地是爲了下降無效的網絡傳輸。當服務器端沒有數據更新的時候,鏈接會保持一段時間週期直到數據或狀態改變或者時間過時,經過這種機制來減小無效的客戶端和服務器間的交互。固然,若是服務端的數據變動很是頻繁的話,這種機制和定時輪詢比較起來沒有本質上的性能的提升。node

Comet

流:web

流技術方案一般就是在客戶端的頁面使用一個隱藏的窗口向服務端發出一個長鏈接的請求。服務器端接到這個請求後做出迴應並不斷更新鏈接狀態以保證客戶端和服務器端的鏈接不過時。經過這種機制能夠將服務器端的信息源源不斷地推向客戶端。這種機制在用戶體驗上有一點問題,須要針對不一樣的瀏覽器設計不一樣的方案來改進用戶體驗,同時這種機制在併發比較大的狀況下,對服務器端的資源是一個極大的考驗。編程

綜合這幾種方案,您會發現這些目前咱們所使用的所謂的實時技術並非真正的實時技術,它們只是在用 Ajax 方式來模擬實時的效果,在每次客戶端和服務器端交互的時候都是一次 HTTP 的請求和應答的過程,而每一次的 HTTP 請求和應答都帶有完整的 HTTP 頭信息,這就增長了每次傳輸的數據量,並且這些方案中客戶端和服務器端的編程實現都比較複雜,在實際的應用中,爲了模擬比較真實的實時效果,開發人員每每須要構造兩個 HTTP 鏈接來模擬客戶端和服務器之間的雙向通信,一個鏈接用來處理客戶端到服務器端的數據傳輸,一個鏈接用來處理服務器端到客戶端的數據傳輸,這不可避免地增長了編程實現的複雜度,也增長了服務器端的負載,制約了應用系統的擴展性。windows

HTML5 WebSocket 設計出來的目的就是要取代輪詢和 Comet 技術,使客戶端瀏覽器具有像 C/S 架構下桌面系統的實時通信能力。 瀏覽器經過 JavaScript 向服務器發出創建 WebSocket 鏈接的請求,鏈接創建之後,客戶端和服務器端就能夠經過 TCP 鏈接直接交換數據。由於 WebSocket 鏈接本質上就是一個 TCP 鏈接,因此在數據傳輸的穩定性和數據傳輸量的大小方面,和輪詢以及 Comet 技術比較,具備很大的性能優點。Websocket.org 網站對傳統的輪詢方式和 WebSocket 調用方式做了一個詳細的測試和比較,將一個簡單的 Web 應用分別用輪詢方式和 WebSocket 方式來實現,在這裏引用一下他們的測試結果圖(http://www.websocket.org/quantum.html ):瀏覽器

websocket

輪詢和 WebSocket 實現方式的網絡負載對比圖服務器

經過這張圖能夠清楚的看出,在流量和負載增大的狀況下,WebSocket 方案相比傳統的 Ajax 輪詢方案有極大的性能優點。這也是爲何咱們認爲 WebSocket 是將來實時 Web 應用的首選方案的緣由。

WebSocket協議設計用來取代使用HTTP做爲傳輸層的雙向通訊技術,並從現有的基礎設施(代理、過濾器、認證)受益。這些技術做爲效率與可靠性的平衡而實現,由於HTTP最初並非用於雙向通訊的。WebSocket嘗試解決在現有HTTP基礎設施的環境下現有HTTP雙向通訊技術的目標;像這樣,它設計來工做於HTTP 80、443端口上,並支持HTTP代理和中間設施,即便這意味着增長現有環境的一些複雜性。

而後,設計並無將WebSocket侷限於HTTP,將來的實現能夠在特定的端口上使用更簡單的握手,而不須要從新發明整個協議。最後點是重要的,由於交互式消息的傳輸模式並不緊密符合標準的HTTP傳輸,會在一些部件上引發異常的負載。

SuperWebSocket是基於.NET開源Socket框架SuperSocket開發的, SuperSocket所支持的大部分功能在SuperWebSocket中獲得了繼承。用戶可經過SuperWebSocket來快速的構建可靠的,高性能的websocket服務器端應用程序。

和SuperSocket同樣,SuperWebSocket能夠控制檯和windows服務的形式運行,同時它還支持直接運行在Website以內,這樣更簡化了用戶的部署。

WebSocket 協議本質上是一個基於 TCP 的協議。爲了創建一個 WebSocket 鏈接,客戶端瀏覽器首先要向服務器發起一個 HTTP 請求,這個請求和一般的 HTTP 請求不一樣,包含了一些附加頭信息,其中附加頭信息」Upgrade: WebSocket」代表這是一個申請協議升級的 HTTP 請求,服務器端解析這些附加的頭信息而後產生應答信息返回給客戶端,客戶端和服務器端的 WebSocket 鏈接就創建起來了,雙方就能夠經過這個鏈接通道自由的傳遞信息,而且這個鏈接會持續存在直到客戶端或者服務器端的某一方主動的關閉鏈接。

下面咱們來詳細介紹一下 WebSocket 規範,WebSocket 協議有兩部分:握手和數據傳輸。

客戶端發出的握手信息:

GET /chat HTTP/1.1

Host: server.example.com

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

Origin: http://example.com

Sec-WebSocket-Protocol: chat, superchat

Sec-WebSocket-Version: 13

服務器端返回的握手信息:

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Sec-WebSocket-Protocol: chat

客戶端握手的引導行聽從(HTTP)Request-Line格式,服務器發出的引導行聽從(HTTP)Status-Line格式。在兩種狀況下,引導行後面跟着一組未排序的頭域。額外的頭域也可能出現,如cookie。頭的格式和解析在RFC2616定義。

一旦客戶端和服務器都發送了他們的握手,若是握手成功,傳輸數據部分開始。

在實際的開發過程當中,爲了使用 WebSocket 接口構建 Web 應用,咱們首先須要構建一個實現了 WebSocket 規範的服務器,服務器端的實現不受平臺和開發語言的限制,只須要聽從 WebSocket 規範便可,目前已經出現了一些比較成熟的 WebSocket 服務器端實現,好比:

  • Kaazing WebSocket Gateway — 一個 Java 實現的 WebSocket Server
  • mod_pywebsocket — 一個 Python 實現的 WebSocket Server
  • Netty —一個 Java 實現的網絡框架其中包括了對 WebSocket 的支持
  • node.js —一個 Server 端的 JavaScript 框架提供了對 WebSocket 的支持
  • SuperWebSocket --一個.NET/Mono 實現的WebSocket Server(本文的主角)

瀏覽器支持

下面是主流瀏覽器對 HTML5 WebSocket 的支持狀況:

瀏覽器

支持狀況

Chrome

Supported in version 4+

Firefox

Supported in version 4+

Internet Explorer

Supported in version 10+

Opera

Supported in version 10+

Safari

Supported in version 5+

 

SuperWebSocket是基於.NET開源Socket框架SuperSocket開發的, SuperSocket所支持的大部分功能在SuperWebSocket中獲得了繼承。用戶可經過SuperWebSocket來快速的構建可靠的,高性能的websocket服務器端應用程序。和SuperSocket同樣,SuperWebSocket能夠控制檯和windows服務的形式運行,同時它還支持直接運行在Website以內,這樣更簡化了用戶的部署。

 

從SuperWebSocket 下載最新的0.6版本的代碼 http://superwebsocket.codeplex.com/releases/view/86249。代碼中包含了一個簡單的聊天示例:

web.config中配置文件說明參考 SuperSocket系列文檔(2) SuperSocket的基本配置

在Global.asax文件裏看StartSuperWebSocketByConfig:

void StartSuperWebSocketByConfig() 
      { 
          var serverConfig = ConfigurationManager.GetSection("socketServer") as SocketServiceConfig; 
          if (!SocketServerManager.Initialize(serverConfig)) 
              return;

          var socketServer = SocketServerManager.GetServerByName("SuperWebSocket") as WebSocketServer; 
          var secureSocketServer = SocketServerManager.GetServerByName("SecureSuperWebSocket") as WebSocketServer;

          Application["WebSocketPort"] = socketServer.Config.Port; 
          Application["SecureWebSocketPort"] = secureSocketServer.Config.Port;

          socketServer.NewMessageReceived += new SessionEventHandler<WebSocketSession, string>(socketServer_NewMessageReceived); 
          socketServer.NewSessionConnected += new SessionEventHandler<WebSocketSession>(socketServer_NewSessionConnected); 
          socketServer.SessionClosed += new SessionEventHandler<WebSocketSession, CloseReason>(socketServer_SessionClosed);

          secureSocketServer.NewSessionConnected += new SessionEventHandler<WebSocketSession>(secureSocketServer_NewSessionConnected); 
          secureSocketServer.SessionClosed += new SessionEventHandler<WebSocketSession, CloseReason>(secureSocketServer_SessionClosed);

          if (!SocketServerManager.Start()) 
              SocketServerManager.Stop(); 
      }

有三個事件(CommandHandler, NewSessionConnected, SessionClosed),在每一個會話到達的時候,將建立新的處理程序來處理。

客戶端的實現相對於服務器端的實現來講要簡單得多了,咱們只須要發揮想象去設計 HTML 用戶界面,而後調用 WebSocket JavaScript 接口來和 WebSocket 服務器端來交互就能夠了。固然別忘了使用一個支持 HTML5 和 WebSocket 的瀏覽器。

當頁面初次加載的時候,首先會檢測當前的瀏覽器是否支持 WebSocket 並給出相應的提示信息。頁面會初始化一個到聊天服務器的 WebSocekt 鏈接,初始化成功之後,頁面會加載對應的 WebSocket 事件處理函數,客戶端 JavaScript 代碼以下所示:

<script type="text/javascript"> 
     var noSupportMessage = "Your browser cannot support WebSocket!"; 
     var ws;

     function resizeFrame() { 
         var h = $(window).height(); 
         var w = $(window).width(); 
         //Adapt screen height 
         $('#messageBoard').css("height", (h - 80 - 50 - 100) + "px"); 
         $('#messageBoxCell').css("width", (w - 100) + "px"); 
         $('#messageBox').css("width", (w - 110) + "px"); 
     }

     $(document).keypress(function (e) { 
         if (e.ctrlKey && e.which == 13 || e.which == 10) { 
             $("#btnSend").click(); 
             document.body.focus(); 
         } else if (e.shiftKey && e.which == 13 || e.which == 10) { 
             $("#btnSend").click(); 
             document.body.focus(); 
         } 
     })

     function scrollToBottom(target) { 
         target.animate({ scrollTop: target[0].scrollHeight }); 
     }

     function connectSocketServer() { 
         var messageBoard = $('#messageBoard');

         var support = "MozWebSocket" in window ? 'MozWebSocket' : ("WebSocket" in window ? 'WebSocket' : null);

         if (support == null) { 
             alert(noSupportMessage); 
             messageBoard.append("* " + noSupportMessage + "<br/>"); 
             return; 
         }

         messageBoard.append("* Connecting to server ..<br/>"); 
         // create a new websocket and connect 
         ws = new window[support]('ws://<%= Request.Url.Host %>:<%= WebSocketPort %>/sample');

         // when data is comming from the server, this metod is called 
         ws.onmessage = function (evt) { 
             messageBoard.append("# " + evt.data + "<br />"); 
             scrollToBottom(messageBoard); 
         };

         // when the connection is established, this method is called 
         ws.onopen = function () { 
             messageBoard.append('* Connection open<br/>'); 
         };

         // when the connection is closed, this method is called 
         ws.onclose = function () { 
             messageBoard.append('* Connection closed<br/>'); 
         }

         //setup secure websocket 
         var wss = new window[support]('wss://<%= Request.Url.Host %>:<%= SecureWebSocketPort %>/sample');

         // when data is comming from the server, this metod is called 
         wss.onmessage = function (evt) { 
             messageBoard.append("# " + evt.data + "<br />"); 
             scrollToBottom(messageBoard); 
         };

         // when the connection is established, this method is called 
         wss.onopen = function () { 
             messageBoard.append('* Secure Connection open<br/>'); 
         };

         // when the connection is closed, this method is called 
         wss.onclose = function () { 
             messageBoard.append('* Secure Connection closed<br/>'); 
         } 
     }

     function sendMessage() { 
         if (ws) { 
             var messageBox = document.getElementById('messageBox'); 
             ws.send(messageBox.value); 
             messageBox.value = ""; 
         } else { 
             alert(noSupportMessage); 
         } 
     }

     jQuery.event.add(window, "resize", resizeFrame);

     window.onload = function () { 
         resizeFrame(); 
         connectSocketServer(); 
     } 
</script>

本文介紹了 WebSocket 規範和 WebSocket 接口,以及和傳統的實時技術相比在性能上的優點,而且演示了怎樣使用 WebSocket 構建一個實時的 Web 應用,最後咱們介紹了當前的主流瀏覽器對 HTML5 的支持狀況。微軟也明確表達了將來對 HTML5 的支持,並且這些支持咱們能夠在 Windows 8 和 IE10 裏看到,咱們也在各類移動設備,平板電腦上看到了 HTML5 和 WebSocket 的身影。WebSocket 將會成爲將來開發實時 Web 應用的生力軍應該是毫無懸念的了,做爲 Web 開發人員,關注 HTML5,關注 WebSocket。

參考文章:

相關文章
相關標籤/搜索