標籤: WebSocket SignalRjavascript
最近由於項目中涉及到了實時數據的傳輸,特意去了解了一下當前Web應用中常見的實時交互手段,固然一開始也不只限於Web客戶端。從c#自帶的Socket類,到Html5中的WebSocket,再到Asp .Net利器SignalR,總算將這塊知識點及應用入門了,固然今天的主要內容仍是Web端的消息交互技術(Ajax,Comet,WebSocket等),這些技術難度有中有低,應用場所也有不一樣,最後咱們要根據項目狀況來選擇恰當的技術。接下來便簡單介紹一下php
應用技術 | 說明 | 優缺點 |
---|---|---|
輪詢(polling) | 這應該是最多見的一種實現數據交互的方式,開發人員控制客戶端以必定時間間隔中向服務器發送Ajax查詢請求大,可是也所以,當服務器端內容並無顯著變化時,這種鏈接方式將帶來不少無效的請求,形成服務器資源損耗。適合併發量小,實時性要求低的應用模型,更像是定時任務。 | 優勢:實現最爲簡單,配置簡單,出錯概率小 缺點:每次都是一次完整的http請求,易延遲,有效請求命中率少,併發較大時,服務器資源損耗大 |
長輪詢(long polling) | 長輪詢是對輪詢的改進,客戶端經過請求鏈接到服務器,並保持一段時間的鏈接狀態,直到消息更新或超時才返回Response並停止鏈接,能夠有效減小無效請求的次數。屬於Comet實現 | 優勢:有效減小無效鏈接,實時性較高 缺點:客戶端和服務器端保持鏈接形成資源浪費,服務器端信息更新頻繁時,long polling並不比polling高效,而且當數據量很大時,會形成連續的polls不斷產生,性能上反而更糟糕 |
iframe流 | iframe流方式是在頁面中插入一個隱藏的iframe,利用其src屬性在服務器和客戶端之間建立一條長連接,服務器向iframe傳輸數據(一般是HTML,內有負責插入信息的javascript),來實時更新頁面。屬於Comet實現 | 優勢:實時性高,瀏覽器兼容度好 缺點:客戶端和服務器端保持長鏈接形成資源浪費 |
WebSocket | WebSocket是HTML5提供的一種在單個 TCP 鏈接上進行全雙工通信的協議,目前chrome、Firefox、Opera、Safari等主流版本均支持,Internet Explorer從10開始支持。另外由於WebSocket 提供瀏覽器一個原生的 socket實現,因此直接解決了 Comet 架構很容易出錯的問題,而在整個架構的複雜度上也比傳統的實現簡單得多。 | 優勢:服務器與客戶端之間交換的數據包檔頭很小,節約帶寬。全雙工通訊,服務器能夠主動傳送數據給客戶端。 缺點:舊版瀏覽器不支持 |
Tips:瀏覽器和客戶端之間想要進行WebSocket通訊的話,從一開始的握手階段,就要從HTTP協議升級爲WebSocket協議,這是服務器根據WebSocket發送的請求包決定的。關於WebSocket的具體介紹(規範和語法和狀態轉換)能夠參考使用 HTML5 WebSocket 構建實時 Web 應用.html
WebSocket本質上是一個基於TCP的持久化協議,相對於HTTP這種非持久的協議來講,它可以更好的節省服務器資源和帶寬,而且真正實現實時通訊。如下是它與傳統技術的性能對比圖(Websocket.org提供)
咱們能夠看到相比於傳統技術,在流量和負載逐漸增大時,WebSocket的性能表現是遠遠超過它們的。
上文提到WebSocket在實際運用時是在握手階段從http請求升級上來的,讓咱們來看一個Websocket請求的例子——這裏借用下維基百科的內容前端
1.客戶端到服務器端 GET / HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: example.com Origin: null Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ== Sec-WebSocket-Version: 13 2.服務器端到客戶端 HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s= Sec-WebSocket-Origin: null Sec-WebSocket-Location: ws://example.com/
注意「1.」中的UpGrade:websocket
和Connection: Upgrade
這兩個核心屬性表示本次是一個特殊的http請求,目的就是要將瀏覽器端和服務器端的通信協議從HTTP協議—升級—>WebSocket協議,其餘屬性都是客戶端向服務器端提供的握手信息。
Sec-WebSocket-Version: 13
表明這是13版修訂協議,Sec-WebSocket-Key
是隨機生成的,服務器端會把Sec-WebSocket-Key
加上一個魔幻字符串「258EAFA5-E914-47DA-95CA-C5AB0DC85B11」。使用SHA-1加密,以後進行BASE-64編碼,將結果作爲Sec-WebSocket-Accept
頭的值,返回給客戶端,代表服務器端贊成建立Websocket請求。java
如下用控制檯和WebSocket簡單實現下服務器和客戶端的全雙工通訊,其中後臺使用了SuperWebSocket技術
後臺示例代碼:jquery
using System; using SuperSocket.SocketBase; using SuperWebSocket; using static System.Console; namespace SuperWebSocketDemo { class Program { private static void Main(string[] args) { var server = new WebSocketServer(); server.NewSessionConnected += ServerNewSessionConnected; server.NewMessageReceived += ServerNewMessageRecevied; server.SessionClosed += ServerSessionClosed; try { server.Setup("127.0.0.1", 4141); server.Start(); } catch (Exception ex) { WriteLine(ex.Message); } ReadKey(); } private static void ServerSessionClosed(WebSocketSession session, CloseReason value) { WriteLine(session.Origin); } public static void ServerNewMessageRecevied(WebSocketSession session, string value) { WriteLine(value); session.Send("已收到:"+value); } /// <param name="session"></param> public static void ServerNewSessionConnected(WebSocketSession session) { WriteLine(session.Origin); } } }
前臺示例代碼,經過websocket的api咱們能夠很容易實現主要邏輯git
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8"/> <title></title> </head> <body> <input type="button" id="send" onclick="send()" value="發送"> <input type="text" id="message"> <script type="text/javascript"> //WebSocket的四個主要方法open onclose onerror send var wsClient=new WebSocket( 'ws://localhost:4141'); wsClient.open=function(e){ console.log("Connected!"); } wsClient.onclose=function(e){ console.log("Disconnected!"); } wsClient.onmessage=function(e){ console.log("接收消息:"+e.data); } wsClient.onerror=function(e){ console.log(e.data); } function send(){ var oText=document.getElementById("message"); wsClient.send(oText.value); } </script> </body> </html>
最終結果以下圖:
github
關於Web實時技術和WebSocket的介紹便到這裏了,咱們甚至能夠用c#本身實現一個WebSocket的服務器,詳情請看《如何編寫一個WebSocket服務器》以及用c#實現的一個簡單的WebSocket服務器《C# socket編程實踐》,固然仍是推薦SuperWebSocket。固然這篇只是簡單的介紹而已,我本身的WebSocket實現(結合規範實現)也會在以後的博客中貼出。web
假如還寫得動……ajax
至此,數種常見的Web實時交互技術已經總覽一遍了,對於Asp Web開發者來講,.Net平臺爲咱們提供了一種簡潔高效智能的實時信息交互技術——SignalR,它集成了上述數種技術,並能根據配置自動或者手動選擇最佳應用。
SignalR是一個.Net開源庫,用於構建須要實時進行用戶交互和數據更新的Web應用,如在線聊天,遊戲,天氣或者股票信息更新等實時應用程序。SignalR簡化了構建實時應用的過程,它包括了一個Asp .Net服務器端庫和一個Js端庫,集成了數種常見的消息傳輸方式,如long polling
,WebSocket
,並提供相應的Api供開發人員選擇如何調用,幫助其能夠簡單快速地實現客戶端與服務器端相互間的實時通訊。
當環境條件合適時,SignalR將WebSocket做爲底層傳輸方式的優先實現,固然,它也能很高效地回退到其餘技術。同時,SignalR提供了很是良好的Api以供遠程調用(RPC) 瀏覽器中的js代碼。
接下來,看看SignalR的傳輸方式和通訊模型,這是SignalR的核心所在。
SignalR基本適用於任何能夠用上述技術實現的場合,可是對寄宿平臺版本有要求。如.Net Framework 平臺,SignalR庫須要4.5及以上版本的支持,而Mono上也實現了SignalR。若是是最新的.Net Core 1.0,建議你們直接使用SelfHost方式寄宿。
傳輸方式 | 選擇條件 |
---|---|
long polling | 1.IE8或更早版本 2.鏈接啓動時JSONP參數設置爲TRUE 3.Forever Frame不可用 |
WebSocket | 1.正在使用跨域鏈接,而且符合如下條件(如下不知足任一條則使用長輪詢) (1).客戶端支持CORS (2).客戶端支持WebSocket (3).服務器端支持WebSocket 2.不配置使用JSONP,鏈接不跨域而且客戶端和服務器端都支持WebSocket (1).客戶端支持CORS (2).客戶端支持WebSocket (3).服務器端支持WebSocket |
ServerSendEvent | 客戶端或服務器端不支持Websocket |
Forever Frame | EventSource不可用(基本上除了IE外都支持) |
若是開發人員想要讓客戶端按照特定的方式和順序進行數據傳輸,能夠經過使用$.connection.start({transport:['webSockets','foeverFrame',……]})
,當客戶端和服務器端並不支持指定方式時,程序將按照默認規則匹配傳輸方式。
用於指定傳輸方式的字符串常量定義以下:
不指定傳輸方式時,SignalR會以Http方式發起請求,比對客戶端和服務器端後,假如WebSocket可用,則自動升級到WebSocket模式,WebSocket是最理想的傳輸方式,除了能高效使用服務器內存,低延遲,還能實現客戶端和服務器端的全雙工通訊。開發人員能夠經過SignalR中js庫的$.connection.chatHub.logging = true;
來啓用hub事件的日誌記錄
SignalR包括兩種客戶端和服務器端之間進行通訊的模型,Persistent Connections
和Hubs
。
通訊模型 | 說明 |
---|---|
Persistent Connections | Persistent Connections表示一個發送單個,編組,廣播信息的簡單終結點。開發人員經過使用持久性鏈接Api,直接訪問SignalR公開的底層通訊協議。 |
Hubs | Hubs是基於鏈接Api的更高級別的通訊管道,它容許客戶端和服務器上彼此直接調用方法,SignalR可以很神奇地處理跨機器的調度,使得客戶端和服務器端可以輕鬆調用在對方端上的方法。使用Hub還容許開發人員將強類型的參數傳遞給方法而且綁定模型 |
Hubs的示例網上已經不少了(文章末尾附上連接),這邊先來快速開始一個SignalR使用永久鏈接的Demo.
關於永久鏈接和Hubs的區別,這裏有些很棒的解釋.
建立一個默認的Asp .Net Mvc項目
使用Install-Package Microsoft.AspNet.SignalR
安裝SignalR包
新增Connections
文件夾,添加SignalR永久鏈接類ChatConnections
using System.Threading.Tasks; using Microsoft.AspNet.SignalR; namespace SignalRUsingPersistentConnectionsDemo.Connections { public class ChatConnection : PersistentConnection { protected override Task OnConnected(IRequest request, string connectionId) { return Connection.Send(connectionId, "Welcome!"); } protected override Task OnReceived(IRequest request, string connectionId, string data) { return Connection.Broadcast(data); } } }
using Microsoft.Owin; using Owin; using SignalRUsingPersistentConnectionsDemo; using SignalRUsingPersistentConnectionsDemo.Connections; [assembly: OwinStartup(typeof (Startup))] namespace SignalRUsingPersistentConnectionsDemo { public class Startup { public void Configuration(IAppBuilder app) { // 有關如何配置應用程序的詳細信息,請訪問 http://go.microsoft.com/fwlink/?LinkID=316888 // 配置上文實現的ChatConnections app.MapSignalR<ChatConnection>("/Connections/ChatConnection"); } } }
前端js實現消息廣播,並實時記錄
@{ Layout = null; } <script src="~/Scripts/jquery-1.10.2.min.js"></script> <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script> <script type="text/javascript"> $(function () { var connection = $.connection("/Connections/ChatConnection"); $('#displayname').val(prompt('Enter your name:', '')); $("msg").focus(); connection.received(function (data) { $('#messages').append('<li>' + data + '</li>'); }); connection.start().done(function() { $("#broadcast").click(function () { connection.send($('#displayname').val()+':'+$('#msg').val()); }); }); }); </script> <input type="text" id="msg" /> <input type="button" id="broadcast" value="broadcast"/> <input type="hidden" id="displayname" /> <ul id="messages"></ul>
除了Web實時應用以外,你也能夠用其餘應用程序實現實時交互,如控制檯。只須要Install-Package Microsoft.AspNet.SignalR.Client
命令,示例編碼以下
using Microsoft.AspNet.SignalR.Client; using static System.Console; namespace DotnetClientWithSignalRDemo { internal class Program { private static void Main(string[] args) { var connection = new Connection("http://localhost:1508/Connections/ChatConnection"); connection.Received += WriteLine; connection.Start().Wait(); string line; while ((line = ReadLine()) != null) { connection.Send(line).Wait(); } } } }
效果如圖,一樣實現了雙向通訊
至此,常見的Web實時交互技術和SignalR簡單介紹就告一段落了,具體的進階使用我可能會在後續博文貼出,如Hubs通訊模型解析,分組鏈接信息傳輸等,不過建議你們直接去閱讀SignalR官方文檔,查閱相關的Api就能夠了,我相信普通技術的運用相對於理解仍是要方便的。
這裏額外推薦一篇SignalR的深刻解讀【打破砂鍋系列】SignalR傳輸方式剖析,這個博主也深刻介紹了SignalR的自動選擇和傳輸機制,也是一篇很棒的文章。