Asp.net SignalR是微軟爲實現實時通訊而開發的一個類庫。能夠適用於如下場景: javascript
SignalR能夠進行遠程分佈式實時通訊,都是使用遠程代理來實現,其中有兩大內部對象,第一個是Persisten Connection,用於客戶端和服務器端的持久鏈接,第二個是Hub(集線器)對象,主要用於信息交互,將服務器端的數據推送(push)至客戶端,大體原理以下: html
接下來咱們經過消息實時推送的案例來學習 SignalR 的使用步驟。具體操做步驟以下: java
建立完成以後,在應用程序的Scripts文件夾裏面會自動生成兩個js文件,如圖所示: jquery
在建立的Hub類中添加以下代碼: 數據庫
//hub別名,方便前臺調用 數組 [HubName("getMsg")] 瀏覽器 public class MyHub : Hub 服務器 { app public void Send(string title,string msg) 分佈式 { //調用客戶端的sendMessage()方法 Clients.All.sendMessage(title,msg); } } |
其中Clients.All是dynamic類型,sendMessaage()方法是視圖中js定義的function。HubName特性用來給集線器類定義別名。
代碼以下:
public class Startup { public void Configuration(IAppBuilder app) { //註冊管道,使用默認的虛擬地址,根目錄下的"/signalr", //固然你也能夠本身定義 app.MapSignalR(); } } |
建立MessageController,並建立兩個用於顯示視圖的action。
控制器代碼:
public class MessageController : Controller { //發送消息 public ActionResult SendMessage() { return View(); } //接收消息 public ActionResult ReceiveMessage() { return View(); } } |
發送消息視圖代碼:
<h2>發送消息</h2> <div> 標題:<input type="text" id="title" /> </div><br /> <div> 內容:<textarea id="message" rows="4" cols="30"></textarea> </div> <br /> <div> <input type="button" id="sendmessage" value="發送" /> </div> <script src="~/Scripts/jquery.signalR-2.2.2.js"></script> <!--引用自動生成的SignalR 集線器(Hub)腳本.在運行的時候在瀏覽器的Source下可看到 --> <script src="~/signalr/hubs"></script> <script type="text/javascript"> $(function () { // 引用自動生成的集線器代理(此處使用別名getMsg) var chat = $.connection.getMsg; // 集成器鏈接開始 $.connection.hub.start().done(function () { // 服務鏈接完成,給發送按鈕註冊單擊事件 $('#sendmessage').click(function () { // 調用服務器端集線器的Send方法 chat.server.send($("#title").val(), $('#message').val()); }); }); }); </script>
|
接收消息視圖代碼:
<h2>接收消息</h2> <div> <br /> <div id="msgcontent"></div> </div> <script src="~/Scripts/jquery.signalR-2.2.2.js"></script> <script src="~/signalr/hubs"></script> <script type="text/javascript"> $(function () { // 引用自動生成的集線器代理 var chat = $.connection.getMsg; // 定義服務器端調用的客戶端sendMessage來顯示新消息 chat.client.sendMessage = function (title, message) { // 向頁面發送接收的消息 var html = "<div>標題:" + title + "消息內容:" + message + "</div>"; $("#msgcontent").after(html); }; // 集成器鏈接開始 $.connection.hub.start(); }); </script>
|
運行程序的時候,頁面就與SignalR的服務創建了鏈接,具體的創建鏈接的代碼就是:$.connection.hub.start()。這句代碼的做用就是與SignalR服務創建鏈接,後面的done函數代表創建鏈接成功後爲發送按鈕註冊了一個click事件,當客戶端輸入內容點擊發送按鈕後,該Click事件將會觸發,觸發執行的操做爲: chat.server.send($("#title").val(), $('#message').val());。這句代碼表示調用服務端的send函數,而服務端的Send方法又調用全部客戶端的sendMessage函數,而客戶端中sendMessage函數就是將信息添加到對應的消息列表中。這樣就實現了廣播消息的功能了。
在服務端聲明的全部Hub信息,都會生成JavaScript輸出到客戶端,爲了驗證這一點,能夠在Chrome中F12來查看源碼就明白了,具體以下圖所示:
看到上圖,也就明白了爲何頁面須要引入"signalr/hubs"腳本庫了。
<!--引用SignalR庫. --> <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script> <!--引用自動生成的SignalR 集線器(Hub)腳本 --> <script src="~/signalr/hubs"></script> |
介紹了羣發消息的實現,下面來學習如何實現像QQ同樣的點對點聊天。
Clients.All.sendMessage(title, message)表示調用全部客戶端的SendMessage方法。除了All屬性外,還具備其餘屬性,能夠在VS中按F12來查看Clients對象的全部屬性或方法,具體的定義以下:
public interface IHubConnectionContext<T> { T All { get; } // 表明全部客戶端 T AllExcept(params string[] excludeConnectionIds); //除了參數中的全部客戶端 T Client(string connectionId); // 特定的客戶端,實現端對端聊天的關鍵 T Clients(IList<string> connectionIds); // 參數中的客戶端 T Group(string groupName, params string[] excludeConnectionIds); // 指定客戶端組,能夠實現羣聊 T Groups(IList<string> groupNames, params string[] excludeConnectionIds); T User(string userId); // 特定的用戶 T Users(IList<string> userIds); // 參數中的用戶 } |
SignalR會每個客戶端分配一個ConnnectionId,這樣咱們就能夠經過ConnnectionId來找到特定的客戶端了。咱們在向某個客戶端發送消息的時候,除了要將消息傳入,也須要將發送給對方的ConnectionId輸入,這樣服務端就能根據傳入的ConnectionId來轉發對應的消息給對應的客戶端了。這樣也就完成了端對端聊天的功能。另外,若是用戶若是不在線的話,服務端能夠把消息保存到數據庫中,等對應的客戶端上線的時候,再從數據庫中查看該客戶端是否有消息須要推送,有的話,從數據庫取出數據,將該數據推送給該客戶端。
下面咱們來梳理下端對端聊天功能的實現思路:
根據上面的思路,先來實現集線器中的代碼:
[HubName("Chat")] public class ChatHub : Hub { // 靜態屬性,在線用戶列表 public static List<UserInfo> OnlineUsers = new List<UserInfo>(); //登陸 public void Login(string userId,string userName) { //獲取客戶端的ConnectionId var connnectId = Context.ConnectionId; OnlineUsers.Add(new UserInfo { ConnectionId = connnectId, UserId = userId, UserName = userName }); // 全部客戶端同步在線用戶 Clients.All.loadUser(OnlineUsers); }
/// <summary> /// 發送私聊 /// </summary> /// <param name="toUserId">接收方用戶鏈接ID</param> /// <param name="message">內容</param> public void SendPrivateMessage(string toUserId, string message) { var fromUserId = Context.ConnectionId;
var toUser = OnlineUsers.FirstOrDefault(x => x.ConnectionId == toUserId); var fromUser = OnlineUsers.FirstOrDefault(x => x.ConnectionId == fromUserId);
if (toUser != null && fromUser != null) { // 調用指定用戶的客戶端方法 Clients.Client(toUserId).receivePrivateMessage(fromUser.UserName, message); } else { //表示對方不在線 Clients.Caller.absentSubscriber(); } }
/// <summary> /// 斷線時調用 /// </summary> /// <param name="stopCalled"></param> /// <returns></returns> public override Task OnDisconnected(bool stopCalled) { var user = OnlineUsers.FirstOrDefault(u => u.ConnectionId == Context.ConnectionId);
// 判斷用戶是否存在,存在則刪除 if (user == null) return base.OnDisconnected(stopCalled);
Clients.All.onUserDisconnected(user.ConnectionId, user.UserName); //調用客戶端用戶離線通知 // 刪除用戶 OnlineUsers.Remove(user); return base.OnDisconnected(stopCalled); } } |
客戶端代碼:
<h2>聊天系統</h2> <div class="container"> @using (Html.BeginForm("login", "Chat", FormMethod.Post, new { @class = "form-inline" })) { <label>用戶Id:</label> @Html.TextBox("userId", "", new { @class = "form-control" }) <label>用戶名:</label> @Html.TextBox("userName", "", new { @class = "form-control" }) <input type="button" id="btnLogin" value="登陸" class="btn btn-default" /> } </div> <hr /> <div class="container"> <div class="col-md-3"> <div class="panel panel-default"> <div class="panel-heading"> 在線用戶 </div> <div class="panel-body"> <ul id="userList">
</ul> </div> </div> </div> <div class="col-md-8"> <div class="panel panel-default"> <div class="panel-heading"> 聊天內容 </div> <div class="panel-body"> <ul id="msgList" >
</ul> </div> <div class="panel-footer"> <label>消息To:</label> <label id="toUser"></label> @Html.TextBox("msg", "", new { @class = "form-control form-inline" }) <input type="button" id="btnSend" value="發送" class="btn btn-default" /> </div> </div> </div> </div>
@section scripts{ <script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script> <script src="~/signalr/hubs"></script> <script> //用戶點擊(選擇用戶) function selectUser(li) { var connectionId = $(li).attr("cid"); $('#toUser').text(connectionId); } $(function () { var chat = $.connection.Chat;
//刷新在線列表 chat.client.loadUser = function (allUsers) { $('#userList').html(""); for (var i = 0; i < allUsers.length; i++) { var li = $('<li cid="' + allUsers[i].ConnectionId+'" onclick="selectUser(this)">' + allUsers[i].UserId + ':' + allUsers[i].UserName + '</li> '); $('#userList').append(li); } } //接收消息 chat.client.receivePrivateMessage = function (from, msg) { var li = $("<li>來自:" + from + "<br/>" + msg + "</li>"); $("#msgList").append(li); }
$.connection.hub.start().done(function () { console.log("鏈接完成"); //登陸 $('#btnLogin').click(function () { chat.server.login($("#userId").val(), $('#userName').val()) })
$('#btnSend').click(function () { chat.server.sendPrivateMessage($('#toUser').text(), $('#msg').val()) }) }); }) </script> |