Asp.net MVC企業級開發(04)---SignalR消息推送

Asp.net SignalR是微軟爲實現實時通訊而開發的一個類庫。能夠適用於如下場景: javascript

   

  • 聊天室,如在線客服系統,IM系統等
  • 股票價格實時更新
  • 消息的推送服務
  • 遊戲中人物位置的實時推送

       

SignalR能夠進行遠程分佈式實時通訊,都是使用遠程代理來實現,其中有兩大內部對象,第一個是Persisten Connection,用於客戶端和服務器端的持久鏈接,第二個是Hub(集線器)對象,主要用於信息交互,將服務器端的數據推送(push)至客戶端,大體原理以下: html

  1. 客戶端創建與服務器端的鏈接
  2. 客戶端調用服務器端的方法
  3. 服務器端經過客戶端發送的請求,響應數據,調用客戶端的方法將數據推送至客戶端

4.1 SignalR的基本使用

接下來咱們經過消息實時推送的案例來學習 SignalR 的使用步驟。具體操做步驟以下: java

   

  1. 建立一個應用程序,我這裏建立的是MVC應用程序
  2. 在MVC項目的Models文件夾中添加新項 SignalR集線器類。如圖所示。

   

建立完成以後,在應用程序的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特性用來給集線器類定義別名。

   

  1. 在項目中添加 OWIN StartUp 類。如圖所示。

       

代碼以下:

public class Startup

{

public void Configuration(IAppBuilder app)

{

//註冊管道,使用默認的虛擬地址,根目錄下的"/signalr",

//固然你也能夠本身定義

app.MapSignalR();

}

}

   

  1. 建立控制器和視圖

    建立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>

   

4.2 使用SignalR實現點對點聊天

   

介紹了羣發消息的實現,下面來學習如何實現像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來轉發對應的消息給對應的客戶端了。這樣也就完成了端對端聊天的功能。另外,若是用戶若是不在線的話,服務端能夠把消息保存到數據庫中,等對應的客戶端上線的時候,再從數據庫中查看該客戶端是否有消息須要推送,有的話,從數據庫取出數據,將該數據推送給該客戶端。

   

下面咱們來梳理下端對端聊天功能的實現思路:

  1. 客戶端登入的時候記錄下客戶端的ConnnectionId,並將用戶加入到一個靜態數組中,該數據爲了記錄全部在線用戶。
  2. 用戶能夠點擊在線用戶中的用戶聊天,在發送消息的時候,須要將ConnectionId一併傳入到服務端。
  3. 服務端根據傳入的消息內容和ConnectionId調用Clients.Client(connnection).sendMessage方法來進行轉發到對應的客戶端。

   

根據上面的思路,先來實現集線器中的代碼:

[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>

相關文章
相關標籤/搜索