華文網

Asp.net SignalR 應用並實現群聊功能 開原始程式碼

ASP.NET SignalR 是為 ASP.NET 開發人員提供的一個庫,可以簡化開發人員將即時 Web 功能添加到應用程式的過程。即時 Web 功能是指這樣一種功能:當所連接的用戶端變得可用時伺服器代碼可以立即向其推送內容,

而不是讓伺服器等待用戶端請求新的資料。(來自官方介紹。)

-1、寫這篇的原因

在上篇文章B/S(Web)即時通訊解決方案中,並沒有詳情介紹SignalR,所以另起一篇專門介紹SignalR,本文的側重點是Hub功能。

0、先看最終實現效果

1、準備工作

1.2、配置Owin與SignalR1.2.1、新建Startup類,註冊SignalR1 public class Startup 2 { 3 public void Configuration(IAppBuilder app) 4 { 5 app.MapSignalR; 6 } 7 }

然後在web.config配置Startup類,

在configuration=>appSettings節點中添加

1.2.2、在頁面引入SignalR的js

1、由於SignalR前端是基於jQuery的,所以頁面需引入jQuery。

2、引入SignalR的js 。

3、引入最重要的hubs js,這個js其實並不存在,SignalR會反射獲取所有供用戶端調用的方法放入hubs js中。

1.2.3、新建GroupChatHub類,並繼承Hub抽象類別

在hub類中的方法就是提供給用戶端調用的js方法。

在js中就可以用signalr調用SendMsg。

[HubName("simpleHub")] public class SimpleHub : Hub { public void SendMsg(string msg) { } }

這樣基本上前期準備工作就做完了,後面就是具體的操作。

2、原理與簡單的程式設計

其實原理如果簡單點理解就很簡單,

因為http是無狀態的,所以每次請求以後都會與伺服器斷開連結,那就是說用戶端可以很容易找到伺服器,但是伺服器如果想給你用戶端發送消息就比較麻煩,如果不明白的可以參考上一篇文章 B/S(Web)即時通訊解決方案。

SignalR就很好的解決了這個問題,也就說實現了實現了流覽器與伺服器的全雙工通信。

2.1、用戶端至服務端(B=>S)

用戶端代碼

服務端代碼

[HubName("simpleHub")] public class SimpleHub : Hub { public void SendMsg(string msg) { // 獲取連結id var connectionId = Context.ConnectionId; // 獲取cookie var cookie = Context.RequestCookies; } }

其中SimpleHub就是我們定義的繼承Hub類SimpleHub,

然後我們可以用特性HubName進行重命名。

然後開始連結。

在連結完成以後,我們就可以調用在SimpleHub類中調用的方法。這就就很簡單的實現了用戶端至服務端發送消息。

我們還可以在Context中獲取我們想要的東西,比如連結id,cookie等。

2.2、服務端至用戶端(S=>B)

服務端代碼

[HubName("simpleHub")] public class SimpleHub : Hub { public void SendMsg(string msg) { Clients.All.msg("發送給用戶端的消息"); } }

用戶端代碼

這裡演示了怎麼發送消息至用戶端,也是SignalR比較重要的功能,這裡有兩個問題需要解決。

問題一、這裡是發送消息給所有連著的用戶端,如果是單個用戶端或者是一批用戶端應該怎麼發送。

問題二、我們在調用msg給個用戶端發送消息時是在接收消息以後做的回饋,然後發送消息給用戶端,這樣就很類似ajax了,服務端並沒有主動給用戶端發送消息。

解決:

問題一、Clients可以給特性的一群或者一個用戶端發送消息

// 所有人 Clients.All.msg("發送給用戶端的消息"); // 特定 cooectionId Clients.Client("connectionId").msg("發送給用戶端的消息"); // 特定 group Clients.Group("groupName").msg("發送給用戶端的消息");

這是比較常用的三個,

當然還有很多,比如AllExcept,Clients。

在SignalR2.0中還添加了Others,OthersInGroup,OthersInGroups等等。

問題二、我們可以在需要發送消息的地方調用GlobalHost.ConnectionManager.GetHubContext.Clients中獲取Clients。獲取Clients並發送消息我們最好寫成單例模式,因為這種需求很符合單例,群聊中有詳細的代碼。

3、SignalR實現群聊

以上的介紹和代碼已經可以實現b=>s和s=>b了,那實現群聊和單獨聊天就比較簡單了。

由於功能比較簡單,所有我把用戶名存到了cookie裡,也就說第一次進來時需要設置cookie。

還有就是在hub中要實現OnConnected、OnDisconnected和OnReconnected,然後在方法中設置用戶和connectionid和統計線上用戶,以便聊天使用。

hub代碼

/// /// SignalR Hub 群聊類 /// [HubName("groupChatHub")] // 標記名稱供js調用 public class GroupChatHub : Hub { /// /// 用戶名 /// private string UserName { get { var userName = Context.RequestCookies["USERNAME"]; return userName == null ? "" : HttpUtility.UrlDecode(userName.Value); } } /// /// 線上用戶 /// private static Dictionary _onlineUser = new Dictionary; /// /// 開始連接 /// /// public override Task OnConnected { Connected; return base.OnConnected; } /// /// 重新連結 /// /// public override Task OnReconnected { Connected; return base.OnReconnected; } private void Connected { // 處理線上人員 if (!_onlineUser.ContainsKey(UserName)) // 如果名稱不存在,則是新用戶 { // 加入線上人員 _onlineUser.Add(UserName, 1); // 向用戶端發送線上人員 Clients.All.publshUser(_onlineUser.Select(i => i.Key)); // 向用戶端發送加入聊天消息 Clients.All.publshMsg(FormatMsg("系統消息", UserName + "加入聊天")); } else { // 如果是已經存在的使用者,則把線上連結的個數+1 _onlineUser[UserName] = _onlineUser[UserName] + 1; } // 加入Hub Group,為了發送單獨消息 Groups.Add(Context.ConnectionId, "GROUP-" + UserName); } /// /// 結束連接 /// /// /// public override Task OnDisconnected(bool stopCalled) { // 人員連結數-1 _onlineUser[UserName] = _onlineUser[UserName] - 1; // 判斷是否斷開了所有的連結 if (_onlineUser[UserName] == 0) { // 移除線上人員 _onlineUser.Remove(UserName); // 向用戶端發送線上人員 Clients.All.publshUser(_onlineUser.Select(i => i.Key)); // 向用戶端發送退出聊天消息 Clients.All.publshMsg(FormatMsg("系統消息", UserName + "退出聊天")); } // 移除Hub Group Groups.Remove(Context.ConnectionId, "GROUP-" + UserName); return base.OnDisconnected(stopCalled); } /// /// 發送消息,供用戶端調用 /// /// 用戶名,如果為0,則是發送給所有人 /// 消息 public void SendMsg(string user, string msg) { if (user == "0") { // 發送給所有使用者消息 Clients.All.publshMsg(FormatMsg(UserName, msg)); } else { //// 發送給自己消息 //Clients.Group("GROUP-" + UserName).publshMsg(FormatMsg(UserName, msg)); //// 發送給選擇的人員 //Clients.Group("GROUP-" + user).publshMsg(FormatMsg(UserName, msg)); // 發送給自己消息 Clients.Groups(new List { "GROUP-" + UserName, "GROUP-" + user }).publshMsg(FormatMsg(UserName, msg)); } } /// /// 格式化發送的消息 /// /// /// /// private dynamic FormatMsg(string name, string msg) { return new { Name = name, Msg = HttpUtility.HtmlEncode(msg), Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }; } }

js代碼

html代碼

群聊系統(1人線上):@ViewBag.UserName 所有人
發送

這樣就消息了群聊和發送給特定的人聊天功能。

3.1、封裝主動發送消息的單例/// /// 主動發送給使用者消息,單例模式 /// public class GroupChat { /// /// Clients,用來主動發送消息 /// private IHubConnectionContext Clients { get; set; } private readonly static GroupChat _instance = new GroupChat(GlobalHost.ConnectionManager.GetHubContext.Clients); private GroupChat(IHubConnectionContext clients) { Clients = clients; } public static GroupChat Instance { get { return _instance; } } /// /// 主動給所有人發送消息,系統直接調用 /// /// public void SendSystemMsg(string msg) { Clients.All.publshMsg(new { Name = "系統消息", Msg = msg, Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }); } }

如果需要發送消息,直接調用SendSystemMsg即可。

GroupChat.Instance.SendSystemMsg("消息");

4、結語

啥也不說了直接源碼

最後望對各位有所幫助,本文原創,歡迎拍磚和推薦。

也就說第一次進來時需要設置cookie。

還有就是在hub中要實現OnConnected、OnDisconnected和OnReconnected,然後在方法中設置用戶和connectionid和統計線上用戶,以便聊天使用。

hub代碼

/// /// SignalR Hub 群聊類 /// [HubName("groupChatHub")] // 標記名稱供js調用 public class GroupChatHub : Hub { /// /// 用戶名 /// private string UserName { get { var userName = Context.RequestCookies["USERNAME"]; return userName == null ? "" : HttpUtility.UrlDecode(userName.Value); } } /// /// 線上用戶 /// private static Dictionary _onlineUser = new Dictionary; /// /// 開始連接 /// /// public override Task OnConnected { Connected; return base.OnConnected; } /// /// 重新連結 /// /// public override Task OnReconnected { Connected; return base.OnReconnected; } private void Connected { // 處理線上人員 if (!_onlineUser.ContainsKey(UserName)) // 如果名稱不存在,則是新用戶 { // 加入線上人員 _onlineUser.Add(UserName, 1); // 向用戶端發送線上人員 Clients.All.publshUser(_onlineUser.Select(i => i.Key)); // 向用戶端發送加入聊天消息 Clients.All.publshMsg(FormatMsg("系統消息", UserName + "加入聊天")); } else { // 如果是已經存在的使用者,則把線上連結的個數+1 _onlineUser[UserName] = _onlineUser[UserName] + 1; } // 加入Hub Group,為了發送單獨消息 Groups.Add(Context.ConnectionId, "GROUP-" + UserName); } /// /// 結束連接 /// /// /// public override Task OnDisconnected(bool stopCalled) { // 人員連結數-1 _onlineUser[UserName] = _onlineUser[UserName] - 1; // 判斷是否斷開了所有的連結 if (_onlineUser[UserName] == 0) { // 移除線上人員 _onlineUser.Remove(UserName); // 向用戶端發送線上人員 Clients.All.publshUser(_onlineUser.Select(i => i.Key)); // 向用戶端發送退出聊天消息 Clients.All.publshMsg(FormatMsg("系統消息", UserName + "退出聊天")); } // 移除Hub Group Groups.Remove(Context.ConnectionId, "GROUP-" + UserName); return base.OnDisconnected(stopCalled); } /// /// 發送消息,供用戶端調用 /// /// 用戶名,如果為0,則是發送給所有人 /// 消息 public void SendMsg(string user, string msg) { if (user == "0") { // 發送給所有使用者消息 Clients.All.publshMsg(FormatMsg(UserName, msg)); } else { //// 發送給自己消息 //Clients.Group("GROUP-" + UserName).publshMsg(FormatMsg(UserName, msg)); //// 發送給選擇的人員 //Clients.Group("GROUP-" + user).publshMsg(FormatMsg(UserName, msg)); // 發送給自己消息 Clients.Groups(new List { "GROUP-" + UserName, "GROUP-" + user }).publshMsg(FormatMsg(UserName, msg)); } } /// /// 格式化發送的消息 /// /// /// /// private dynamic FormatMsg(string name, string msg) { return new { Name = name, Msg = HttpUtility.HtmlEncode(msg), Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }; } }

js代碼

html代碼

群聊系統(1人線上):@ViewBag.UserName 所有人
發送

這樣就消息了群聊和發送給特定的人聊天功能。

3.1、封裝主動發送消息的單例/// /// 主動發送給使用者消息,單例模式 /// public class GroupChat { /// /// Clients,用來主動發送消息 /// private IHubConnectionContext Clients { get; set; } private readonly static GroupChat _instance = new GroupChat(GlobalHost.ConnectionManager.GetHubContext.Clients); private GroupChat(IHubConnectionContext clients) { Clients = clients; } public static GroupChat Instance { get { return _instance; } } /// /// 主動給所有人發送消息,系統直接調用 /// /// public void SendSystemMsg(string msg) { Clients.All.publshMsg(new { Name = "系統消息", Msg = msg, Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }); } }

如果需要發送消息,直接調用SendSystemMsg即可。

GroupChat.Instance.SendSystemMsg("消息");

4、結語

啥也不說了直接源碼

最後望對各位有所幫助,本文原創,歡迎拍磚和推薦。