ASP.NET SignalR入門

前言
  以前在培訓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 }
MyConnection.cs

 

 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>
HtmlPage.html

 效果以下:

Demo

 

以上咱們都是在「持久鏈接」層上面作的操做,相似socket這樣的模式。。。咱們真正有用的操做是由一個OnReceived方法。

這樣看起來,是否是很單一?並且也很是不人性化,那麼爲了解決這種問題,singlaR給咱們封裝了更高一層的應用。

那麼這個就叫作Hub,這個Hub使用的是相似於RPC的調用模式。

關於Hub,將會在下一篇文章中來說細細講解。

官方教程https://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr

相關文章
相關標籤/搜索