實際上關於SignalR的介紹網上有很多,這里不做過多贅述,我們來看下官方網站的描述。
【摘錄自http://signalr.net/】
What is ASP.NET SignalR
ASP.NET SignalR is a new library for ASP.NET developers that makes it incredibly simple to add real-time web functionality to your applications. What is "real-time web" functionality? It's the ability to have your server-side code push content to the connected clients as it happens, in real-time.
You may have heard of WebSockets, a new HTML5 API that enables bi-directional communication between the browser and server. SignalR will use WebSockets under the covers when it's available, and gracefully fallback to other techniques and technologies when it isn't, while your application code stays the same.
SignalR also provides a very simple, high-level API for doing server to client RPC (call JavaScript functions in your clients' browsers from server-side .NET code) in your ASP.NET application, as well as adding useful hooks for connection management, e.g. connect/disconnect events, grouping connections, authorization.
What can you do with ASP.NET SignalR?
SignalR can be used to add any sort of "real-time" web functionality to your ASP.NET application. While chat is often used as an example, you can do a whole lot more. Any time a user refreshes a web page to see new data, or the page implements Ajax long polling to retrieve new data, is candidate for using SignalR.
It also enables completely new types of applications, that require high frequency updates from the server, e.g. real-time gaming. For a great example of this see the ShootR game(作者:這個游戲很給力)
實際上很早以前我就有過使用server push技術的想法,不過一直沒有實現,總是有一些問題。一般來說原理就是一個長連接,好比在client與server之間建立一個通信通道,維護請求與響應,我們都知道,請求之后響應就會回來,但是如果我們把請求hold 住, 不撒手, 當server需要push時,在把這個響應推回給client。SignalR對這部分處理的相當精彩。
我們來創建一個mvc4的項目,通過NuGet來獲取SignalR的所需組件。
對於SignalR來說,他需要將自身的hub進行注冊,也就是說需要注冊到路由中,這個hub可以理解為分發器或者推送器。
所以,創建RegisterHubs.cs

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Routing; [assembly: WebActivatorEx.PreApplicationStartMethod(typeof(Web.Mvc.SingalRDemo.App_Start.RegisterHubs), "Start")] namespace Web.Mvc.SingalRDemo.App_Start { public class RegisterHubs { public static void Start() { // Register the default hubs route: ~/signalr/hubs RouteTable.Routes.MapHubs(); } } }
其中需要WebActivatorEx來進行注入,所以需要獲得這個組件。
第二,我們來組織一下頁面,並不復雜,
創建一個controller,就叫做home吧,

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace Web.Mvc.SingalRDemo.Controllers { public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { return View(); } } }
第三,創建view,

@model dynamic @{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2> Index</h2> my name : <span></span> <br /> <select id="userSelect"> </select><input type="text" id="message" /><input type="button" value="send" id="send" /> <div id="braod"> </div>
第四,修改BundleConfig,

bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js", "~/Scripts/jquery.signalR-1.0.1.js", "~/Scripts/modernizr-2.5.3.js" ));
第五,修改_Layout.cshtml,

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") </head> <body> @RenderBody() @Scripts.Render("~/bundles/jquery") <script src="../signalr/hubs"></script> <script src="~/Scripts/demo.js"></script> @RenderSection("scripts", required: false) </body> </html>
這里要注意,<script src="../signalr/hubs"></script>這句是一定要加的,而且需要加在 jquery.signalR-1.0.1.js的下面。大家也會發現,在下面會有一個自己的demo.js,為了方便就放這里了。
第六,來創建hub,

using System; using System.Collections.Generic; using System.Linq; using System.Web; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; namespace Web.Mvc.SingalRDemo.Models { [HubName("chat")] public class ChatHub : Hub { private static List<User> users = new List<User>(); public void send(string name, string message) { Clients.All.push(name, message); } public void sendTo(string from, string to, string message) { var user = users.Where(u => u.Id == to).SingleOrDefault(); if (user != null) { Clients.Client(to).pushOne(from, message); } } public void bind() { User u = new User() { Name = "user - " + Guid.NewGuid(), Id = Context.ConnectionId }; users.Add(u); Clients.All.bindList(users); Clients.Client(u.Id).setName(u.Name); } public override System.Threading.Tasks.Task OnConnected() { return base.OnConnected(); } public override System.Threading.Tasks.Task OnDisconnected() { users.Remove(users.Where(u => u.Id == Context.ConnectionId).SingleOrDefault()); return base.OnDisconnected(); } } }
這里說幾句,HubName跟actionName類似。在hub中,定義了三個服務器端的方法,用來被客戶端調用,而在這些方法中也有一些方法並沒被定義,這個是需要在demo.js中被定義的,用來被服務器端調用。
Clients.All,這句相當於廣播。
Clients.Client(to),這句相當於知道了對方的ConnectionId,來進行特定點的推送。
這里邏輯並不復雜。
附帶上user的代碼,

using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace Web.Mvc.SingalRDemo.Models { public class User { public string Id { get; set; } public string Name { get; set; } } }
第七,來創建demo.js和hub進行溝通,

$(function () { var chat = $.connection.chat; $.extend(chat.client, { push: function (a, b) { $("#braod").append("<li>" + a + " : " + b + "</li>"); }, pushOne: function (a, b) { $("#braod").append("<li>" + a + " say to you : " + b + "</li>"); }, bindList: function (result) { $('#userSelect').empty(); $('#userSelect').append("<option value='All'>All</option>"); $.each(result, function (index, u) { $('#userSelect').append("<option value='" + u.Id + "'>" + u.Name + "</option>"); }); }, setName:function(name) { $('span').text(name); } }); $.connection.hub.start().done(function () { refreshNaleList(); }); $("#send").click(function () { var select = $('#userSelect'); if (select.val() == 'All') { chat.server.send($("span").text(), $("#message").val()); } else { chat.server.sendTo($("span").text(), select.val(), $("#message").val()); } }); function refreshNaleList() { chat.server.bind(); } });
這里簡單說幾句,var chat = $.connection.chat;相當於獲得hub對象,由他來建立通道。
chat.client這里,來給client注冊一些方法,如果服務器端進行調用這些方法,那么客戶端就會執行,參數神馬的一看就明白了。
chat.server,服務器端的方法通過這種形式進行調用。
這樣就會得到一方有動作,所有連接的客戶端都會得到消息,而不用進行輪詢。
具體效果,跑起來就明白了,程序挺簡單。
最后,我和同事進行了測試,如果在有3方連接,在操作過程中,其中一方掉線了(我們拔掉了網線),此時我客戶端發消息,服務器端得到消息進行推送,推送完成后,掉線一方插上網線,過了一會,消息就被推送過來了,看起來他在服務器端進行了一些信息的維護,估計是有一個時間的,超過一個時間連接就失效了。這一點很厲害。不用自己維護了。
Ok,it is that.