前言
以前在培訓ASP.NET WebAPI的時候有提過SignalR這個技術,但當時只是講了是用來作什麼的,並無多說。由於本身也是畫圖找資料的時候見到的。後來當一直關注的前端大神賢心發佈LayIM2.0以後,因而對Web聊天產生了興趣。那麼在.NET平臺下的Web聊天有哪些呢?查找資料發現了ASP.NET SignalR。因而乎...So...Just do it!javascript
簡介
按照慣例,先介紹一下什麼是SignalR。簡單的說,ASP .NET SignalR 是一個ASP .NET 下的類庫,能夠在ASP .NET 的Web項目中實現實時通訊。
什麼是實時通訊的Web呢?就是讓客戶端(Web頁面)和服務器端能夠互相通知消息及調用方法,固然這是實時操做的。
WebSockets是HTML5提供的新的API,能夠在Web網頁與服務器端間創建Socket鏈接,當WebSockets可用時(即瀏覽器支持Html5)SignalR使用WebSockets,當不支持時SignalR將使用其它技術來保證達到相同效果。
SignalR固然也提供了很是簡單易用的高階API,使服務器端能夠單個或批量調用客戶端上的JavaScript函數,而且很是 方便地進行鏈接管理,例如客戶端鏈接到服務器端,或斷開鏈接,客戶端分組,以及客戶端受權,使用SignalR都很是 容易實現。
做用
SignalR 將與客戶端進行實時通訊帶給了ASP .NET 。固然這樣既好用,並且也有足夠的擴展性。之前用戶須要刷新頁面或使用Ajax輪詢才能實現的實時顯示數據,如今只要使用SignalR,就能夠簡單實現了。
最重要的是您無需從新創建項目,使用現有ASP .NET項目便可無縫使用SignalR。html
Owin規範
OWIN是Open Web Server Interface for .NET的首字母縮寫,他的定義以下:
OWIN在.NET Web Servers與Web Application之間定義了一套標準接口,OWIN的目標是用於解耦Web Server和Web Application。基於此標準,鼓勵開發者開發簡單、靈活的模塊,從而推動.NET Web Development開源生態系統的發展。
正如你看到的這樣,OWIN是接口、契約,而非具體的代碼實現,僅僅是規範(specifications),因此要實現自定義基於OWIN的Web Server必需要實現此規範。
歷時兩年(2010-2012),OWIN的規範終於完成而且當前版本是1.0,在OWIN的官網上能夠看到更具體的信息。
實際上,OWIN的規範很是簡單,他定義了一系列的層(Layer),而且他們的順序是以堆(Stack)的形式定義,以下所示。OWIN中的接口被稱之爲應用程序委託或者AppFunc,用來在這些層之間通訊。前端
OWIN定義了4層:
Host:主要負責應用程序的配置和啓動進程,包括初始化OWIN Pipeline、運行Server。
Server:綁定套接字並監聽的HTTP請求而後將Request和Response的Body、Header封裝成符合OWIN規範的字典併發送到OWIN Middleware Pipeline中
Middleware:稱之爲中間件、組件,位於Server與Application之間,用來處理髮送到Pipeline中的請求
Application:這是具體的應用程序代碼,只不過咱們將他們註冊到OWIN Pipeline中去處理HTTP 請求,成爲OWIN管道的一部分
Application Delegate
OWIN規範另外一個重要的組成部分是接口的定義,用於Server和Middleware的交互。他並非嚴格意義上的接口,而是一個委託而且每一個OWIN中間件組件必須提供。
詳見:http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-owin.html
Katana
微軟引入並推廣OWIN,同時依照OWIN規範,實現了Katana。Host.SystemWeb, Host.HttpListener 都是Katana的一個組件。
Katana的基本原則
● 可移植性:從HostàServeràMiddleware,每一個Pipeline中的組件都是可替換的,而且第三方公司和開源項目的Framework都是能夠在OWIN Server上運行,也就是說不受平臺限制,從而實現跨平臺。
● 模塊化:每個組件都必須保持足夠獨立性,一般只作一件事,以混合模塊的形式來知足實際的開發需求
● 輕量和高效:由於每個組件都是模塊化開發,並且能夠輕鬆的在Pipeline中插拔組件,實現高效開發
Katana 體系結構
Katana實現了OWIN的Layers,因此Katana的體系結構和OWIN一致,以下所示:java
1.)Host :宿主Host被OWIN規範定義在第一層(最底層),他的職責是管理底層的進程(啓動、關閉)、初始化OWIN Pipeline、選擇Server運行等。
Katana爲咱們提供了3中選擇:
● IIS / ASP.NET :使用IIS是最簡單和向後兼容方式,在這種場景中OWIN Pipeline經過標準的HttpModule和HttpHandler啓動。使用此Host你必須使用System.Web做爲OWIN Server
● Custom Host :若是你想要使用其餘Server來替換掉System.Web,而且能夠有更多的控制權,那麼你能夠選擇建立一個自定義宿主,如使用Windows Service、控制檯應用程序、Winform來承載Server。
● OwinHost :若是你對上面兩種Host還不滿意,那麼最後一個選擇是使用Katana提供的OwinHost.exe:他是一個命令行應用程序,運行在項目的根部,啓動HttpListener Server並找到基於約束的Startup啓動項。OwinHost提供了命令行選項來自定義他的行爲,好比:手動指定Startup啓動項或者使用其餘Server(若是你不須要默認的HttpListener Server)。
2.)Server
Host以後的Layer被稱爲Server,他負責打開套接字並監聽Http請求,一旦請求到達,根據Http請求來構建符合OWIN規範的Environment Dictionary(環境字典)並將它發送到Pipeline中交由Middleware處理。Katana對OWIN Server的實現分爲以下幾類:
● System.Web:如前所述那樣,System.Web和IIS/ASP.NET Host二者彼此耦合,當你選擇使用System.Web做爲Server ,Katana System.Web Server把本身註冊爲HttpModule和HttpHandler而且處理髮送給IIS的請求,最後將HttpRequest、HttpResponse對象映射爲OWIN環境字典並將它發送至Pipeline中處理。
● HttpListener:這是OwinHost.exe和自定義Host默認的Server。
● WebListener:這是ASP.NET vNext默認的輕量級Server,他目前沒法使用在Katana中
3)Middleware
Middleware(中間件)位於Host、Server以後,用來處理Pipeline中的請求,Middleware能夠理解爲實現了OWIN應用程序委託AppFun的組件。
Middleware處理請求以後並能夠交由下一個Pipeline中的Middleware組件處理,即鏈式處理請求,經過環境字典能夠獲取到全部的Http請求數據和自定義數據。Middleware能夠是簡單的Log組件,亦能夠爲複雜的大型Web Framework,諸如:ASP.NET Web API、Nancy、SignlR等,以下圖所示:Pipeline中的Middleware用來處理請求:jquery
4.)Application
最後一層即爲Application,是具體的代碼實現,好比ASP.NET Web API、SignalR具體代碼的實現。瀏覽器
詳見:http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-katana.html服務器
說了那麼多,接下來開始上代碼。併發
開始以前,準備工做:必須安裝.NET Framework 4.5 , Visual Studio 2013+,VS2012須要安裝其餘軟件和配置,也可能會出現一些位置問題(我用的VS2015)mvc
持久鏈接:
一、新建一個空項目asp.net
二、新建一個OWIN Startup類,名稱隨意,這裏以Startup爲例。
三、新建SignalR永久鏈接類,名稱隨意,這裏以MyConnection爲例。
當該類添加完成後,會自動添加SignalR的相關引用和Script(如圖所示)
四、打開Startup.cs添加對MyConnection的映射
五、新建一個HTML頁面,名稱隨意。在新建的HTML頁面上引用jquery-1.10.2.js和jquery.signalR-2.1.2.min.js
六、與服務端通信,寫入下圖紅框內的代碼
打開瀏覽器,F12打開控制檯,如圖顯示,說明鏈接成功。
爲何會是輸出Welcome呢?
$.connection("/myconn");建立了一個鏈接,鏈接是哪兒呢,「/myconn」就是以前在Startup類中定義的MyConnection中映射路徑。當創建鏈接後,調用OnConnected方法,以下:
如今來進行消息發送
1 var conn = $.connection("/myconn");//建立鏈接
2 //開始鏈接
3 conn.start(function () { 4 console.log("已鏈接"); 5 }).done(function() { 6 conn.send("你好!"); 7 }); 8 //接受消息
9 conn.received(function (data) { 10 console.log(data); 11 });
SignalR.js提供了發送接口send() <注意:start()方法是異步方法,send()只能在回調函數中執行,或者自定義參數標識。>
那麼服務端是怎麼接收的呢?
仍是在MyConnection中,此處將你發送的信息給廣播出去
下邊用持久鏈接類來演示一個簡單的聊天室的功能,只是簡單實現功能,只作演示,勿噴。
直接上代碼:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using System.Web; 6 using Microsoft.AspNet.SignalR; 7 using Newtonsoft.Json; 8 9 namespace SignalRDemo 10 { 11 public class MyConnection : PersistentConnection 12 { 13 14 /// <summary> 15 /// 創建新鏈接時調用 16 /// </summary> 17 /// <param name="request">當前鏈接的請求</param> 18 /// <param name="connectionId">進行從新鏈接的客戶端的 ID。</param> 19 /// <returns>鏈接操做完成時將完成的 System.Threading.Tasks.Task。 </returns> 20 protected override Task OnConnected(IRequest request, string connectionId) 21 { 22 return Connection.Send(connectionId, "Welcome!"); 23 } 24 25 /// <summary> 26 /// 從鏈接接收數據時調用 27 /// </summary> 28 /// <param name="request">當前鏈接的請求</param> 29 /// <param name="connectionId"> 發送數據的鏈接的 ID。</param> 30 /// <param name="data">發送到鏈接的負載。</param> 31 /// <returns>接收操做完成時將完成的 System.Threading.Tasks.Task。</returns> 32 protected override Task OnReceived(IRequest request, string connectionId, string data) 33 { 34 var mode = JsonConvert.DeserializeObject<MyChart>(data); 35 if (mode.Action == "welcome") 36 { 37 this.Groups.Add(connectionId, mode.RoomName); 38 39 //除了當前這我的,該房間的其餘人都會受到welcom new user的消息。 40 return this.Groups.Send(mode.RoomName, string.Format("歡迎新用戶進入房間: {0}", mode.RoomName), connectionId); 41 } 42 else 43 { 44 //發送的消息,排除當前這我的 45 return this.Groups.Send(mode.RoomName, string.Format("用戶{0}說:{1}", connectionId, mode.Data), connectionId); 46 } 47 } 48 } 49 50 51 public class MyChart 52 { 53 public string RoomName { get; set; } 54 public string Data { get; set; } 55 public string Action { get; set; } 56 57 } 58 }
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title></title> 6 <meta charset="utf-8" /> 7 <script src="Scripts/jquery-1.10.2.js"></script> 8 <script src="Scripts/jquery.signalR-2.1.2.min.js"></script> 9 10 </head> 11 <body> 12 <h1>SignalR</h1> 13 <input type="button" value="房間一" id="room1" /> 14 <input type="text" id="msg" /> 15 <input type="button" value="發送" id="send" /> 16 <script type="text/javascript"> 17 $(function () { 18 var conn = $.connection("/myconn"); 19 //conn.logging = true; 20 var isconn = false; 21 conn.start(function () { 22 isconn = true; 23 }); 24 25 $("#room1").click(function () { 26 if (isconn) { 27 conn.send({ RoomName: "room1", Action: "welcome" }); 28 } 29 }); 30 $("#send").click(function () { 31 if (isconn) { 32 conn.send({ 33 RoomName: "room1", 34 Action: "2", 35 Data: $("#msg").val() 36 }); 37 } 38 39 }); 40 conn.connectionSlow(function (data) { 41 42 }); 43 conn.disconnected(function (data) { 44 45 }); 46 conn.error(function (data) { }); 47 conn.received(function (data) { 48 console.log(data); 49 }); 50 }); 51 52 </script> 53 </body> 54 </html>
效果以下:
以上咱們都是在「持久鏈接」層上面作的操做,相似socket這樣的模式。。。咱們真正有用的操做是由一個OnReceived方法。
這樣看起來,是否是很單一?並且也很是不人性化,那麼爲了解決這種問題,singlaR給咱們封裝了更高一層的應用。
那麼這個就叫作Hub,這個Hub使用的是相似於RPC的調用模式。
關於Hub,將會在下一篇文章中來說細細講解。
官方教程https://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr