這篇文章主要為大家分享了Asp.net SignalR應用並實現群聊功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
ASP.NET SignalR 是為ASP .NET 開發人員提供的一個程式庫,可以簡化開發人員將即時Web 功能新增至應用程式的過程。即時 Web 功能是指這樣一種功能:當所連接的客戶端變得可用時伺服器程式碼可以立即向其推送內容,而不是讓伺服器等待客戶端請求新的資料。 (來自官方介紹。)
SignalR官網
-1、寫這篇的原因
上篇文章B/S(Web)即時通訊解決方案中,並沒有詳情介紹SignalR,所以另一篇專門介紹SignalR,本文的重點是Hub功能。
0、先看最終實現效果
#github.com/Emrys5/SignalRGroupChatDemo
# 1.準備工作
1.1、在NuGet上先下載SignalR的套件。
1.2、設定Owin與SignalR
1.2.1、新建Startup類,註冊SignalR
public class Startup { public void Configuration(IAppBuilder app) { app.MapSignalR(); } }
然後在web.config配置Startup類,在configuration=>appSettings節點中加入
1.2.2、在頁面引入SignalR的js
1、由於SignalR前端是基於jQuery的,所以頁面需引入jQuery。
2、引入SignalR的js 。
3、引入最重要的hubs js,這個js其實不存在,SignalR會反射取得所有供客戶端呼叫的方法放入hubs js中。
<script src="~/Scripts/jquery-1.10.2.js"></script> <script src="~/Scripts/jquery.signalR-2.2.1.min.js"></script> <script src="~/signalr/hubs"></script>
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)
客戶端程式碼
<script type="text/javascript"> var ticker = $.connection.simpleHub; $.connection.hub.start(); $("#btn").click(function () { // 链接完成以后,可以发送消息至服务端 ticker.server.sendMsg("需要发送的消息"); }); </script>
服務端程式碼
[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("发送给客户端的消息"); } }
客戶端程式碼
<script type="text/javascript"> var ticker = $.connection.groupChatHub; $.connection.hub.start(); ticker.client.msg = function (data) { console.log(data); } </script>
這裡示範了怎麼傳送訊息到客戶端,也是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
3、SignalR實作群組聊天
以上的介紹和程式碼已經可以實作b=>s和s=>b了,那就實現群組聊天和單獨聊天就比較簡單了。
由于功能比较简单,所有我把用户名存到了cookie里,也就说第一次进来时需要设置cookie。
还有就是在hub中要实现OnConnected、OnDisconnected和OnReconnected,然后在方法中设置用户和connectionid和统计在线用户,以便聊天使用。
hub代码
/// <summary> /// SignalR Hub 群聊类 /// </summary> [HubName("groupChatHub")] // 标记名称供js调用 public class GroupChatHub : Hub { /// <summary> /// 用户名 /// </summary> private string UserName { get { var userName = Context.RequestCookies["USERNAME"]; return userName == null ? "" : HttpUtility.UrlDecode(userName.Value); } } /// <summary> /// 在线用户 /// </summary> private static Dictionary<string, int> _onlineUser = new Dictionary<string, int>(); /// <summary> /// 开始连接 /// </summary> /// <returns></returns> public override Task OnConnected() { Connected(); return base.OnConnected(); } /// <summary> /// 重新链接 /// </summary> /// <returns></returns> 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); } /// <summary> /// 结束连接 /// </summary> /// <param name="stopCalled"></param> /// <returns></returns> 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); } /// <summary> /// 发送消息,供客户端调用 /// </summary> /// <param name="user">用户名,如果为0,则是发送给所有人</param> /// <param name="msg">消息</param> 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<string> { "GROUP-" + UserName, "GROUP-" + user }).publshMsg(FormatMsg(UserName, msg)); } } /// <summary> /// 格式化发送的消息 /// </summary> /// <param name="name"></param> /// <param name="msg"></param> /// <returns></returns> 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代码
<script type="text/javascript"> $(function () { // 链接hub var ticker = $.connection.groupChatHub; $.connection.hub.start(); // 接收服务端发送的消息 $.extend(ticker.client, { // 接收聊天消息 publshMsg: function (data) { $("#msg").append("<li><span class='p'>" + data.Name + ":</span>" + data.Msg + " <span class='time'>" + data.Time + "</span></li>") $("#msg").parents("p")[0].scrollTop = $("#msg").parents("p")[0].scrollHeight; }, // 接收在线人员,然后加入Select,以供单独聊天选中 publshUser: function (data) { $("#count").text(data.length); $("#users").empty(); $("#users").append('<option value="0">所有人</option>'); for (var i = 0; i < data.length; i++) { $("#users").append('<option value="' + data[i] + '">' + data[i] + '</option>') } } }); // 发送消息按钮 $("#btn-send").click(function () { var msg = $("#txt-msg").val(); if (!msg) { alert('请输入内容!'); return false; } $("#txt-msg").val(''); // 主动发送消息,传入发送给谁,和发送的内容。 ticker.server.sendMsg($("#users").val(), msg); }); }); </script>
html代码
<h2> 群聊系统(<span id="count">1</span>人在线):@ViewBag.UserName </h2> <p style="overflow:auto;height:300px"> <ul id="msg"></ul> </p> <select id="users" class="form-control" style="max-width:150px;"> <option value="0">所有人</option> </select> <input type="text" onkeydown='if (event.keyCode == 13) { $("#btn-send").click() }' class="form-control" id="txt-msg" placeholder="内容" style="max-width:400px;" /> <br /> <button type="button" id="btn-send">发送</button>
这样就消息了群聊和发送给特定的人聊天功能。
3.1、封装主动发送消息的单例
/// <summary> /// 主动发送给用户消息,单例模式 /// </summary> public class GroupChat { /// <summary> /// Clients,用来主动发送消息 /// </summary> private IHubConnectionContext<dynamic> Clients { get; set; } private readonly static GroupChat _instance = new GroupChat(GlobalHost.ConnectionManager.GetHubContext<GroupChatHub>().Clients); private GroupChat(IHubConnectionContext<dynamic> clients) { Clients = clients; } public static GroupChat Instance { get { return _instance; } } /// <summary> /// 主动给所有人发送消息,系统直接调用 /// </summary> /// <param name="msg"></param> 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、结语
啥也不说了直接源码
github.com/Emrys5/SignalRGroupChatDemo
以上是Asp.net SignalR的應用並實現群組聊天功能的實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!