在上一篇 SignalR 文章中,演示了如何经过 SignalR 实现了简单的聊天室功能;本着简洁就是美的原则,这一篇咱们也来聊聊在 SignalR 中的用户和组的概念,理解这些基础知识有助于更好的开发基于 SignalR 的应用,经过对用户和分组的理解,进一步扩展出对用户和分组的管理,以及消息推送的各类方式,为全面接入 SignalR 作准备。git
在 SignalR 中,用户表示链接,一个用户表明一个链接,一个“系统用户”能够建立多个链接身份,经过函数集线器,能够给一个用户的全部链接发送消息;好比一个“系统用户”拥有多个链接,这些链接分别是 Web链接、AndroId手机客户端链接,IOS手机客户端链接、或者其它客户端链接,“系统用户”分别登陆了这些客户端,同时建立了多个链接;默认状况下这些链接都经过 ClaimTypes.NameIdentifier 在 ClaimsPrincipal 于用户标识进行关联。github
** 注意:用户标识符是区分大小写的,为了实现一个客户多个链接,本例还简单实现了一个基于 ClaimsIdentity 登陆接口,算是意外惊喜。并发
为了直观的观察到用户是能够拥有多链接的,须要创建一个本地静态对象,用于存储用户链接async
public class WeChatHub : Hub { public Dictionary<string, List<string>> UserList { get; set; } = new Dictionary<string, List<string>>(); public void Send(ChatMessage body) { Clients.All.SendAsync("Recv", body); } public override Task OnConnectedAsync() { var userName = this.Context.User.Identity.Name; var connectionId = this.Context.ConnectionId; if (!UserList.ContainsKey(userName)) { UserList[userName] = new List<string>(); UserList[userName].Add(connectionId); } else if (!UserList[userName].Contains(connectionId)) { UserList[userName].Add(connectionId); } Console.WriteLine("哇,有人进来了:{0},{1},{2}", this.Context.UserIdentifier, this.Context.User.Identity.Name, this.Context.ConnectionId); return base.OnConnectedAsync(); } public override Task OnDisconnectedAsync(Exception exception) { var userName = this.Context.User.Identity.Name; var connectionId = this.Context.ConnectionId; if (UserList.ContainsKey(userName)) { if (UserList[userName].Contains(connectionId)) { UserList[userName].Remove(connectionId); } } Console.WriteLine("靠,有人跑路了:{0}", this.Context.ConnectionId); return base.OnDisconnectedAsync(exception); } }
上面的代码包含了一个内部成员 UserList,用于存储用户的每一个链接,在用户进行 SignalR 链接时,将当前链接存储到 UserList 中,当链接断开的时候,将当前链接从 UserList 中删除。这样就实现了一个简单的用户链接管理。ide
在上面的代码中,当前用户昵称是根据 var userName = this.Context.User.Identity.Name; 这行代码获取的,为了取得这个用户昵称,咱们实现了一个简单的 UserIdentity 登陆,而后将 User 信息写入到 Cookie 中,最后才能够经过 var userName = this.Context.User.Identity.Name; 得到当前登陆用户昵称(熟悉 ID 登陆流程的同窗应该不会感到陌生,实际上我也不多使用 ID 验证)函数
[Authorize(Roles = "User")] [HttpPost("SendToUser")] public async Task<IActionResult> SendToUser([FromBody] UserInfoViewModel model) { ChatMessage message = new ChatMessage() { Type = 1, Content = model.Content, UserName = model.UserName }; if (this.chatHub.UserList.ContainsKey(model.UserName)) { var connections = this.chatHub.UserList[model.UserName].First(); await this.chatHub.Clients.Client(connections).SendAsync("Recv", new object[] { message }); } return Json(new { Code = 0 }); }
在 UserController 中,定义了上面的接口 SendToUser ,客户端传入用户昵称和消息,而后服务端就会去根据 ChatHub.UserList 成员查找目标用户的链接信息,最后,经过 SendAsync 将消息推送到目标客户端链接中。学习
分组的概念相似于聊天室,每一个房间就是一个独立的分组,用户能够选择加入 A 房间,也能够选择加入 B 房间,若是业务容许,一个用户还能够加入多个分组(房间),经过使用分组对用户进行管理,能够实现一个或者多个聊天房间,用户能够加入分组,也能够将用户从分组中删除(相似离开房间),这里的用户并发真正意义上的“系统用户”,而是指系统用户建立的那些 SignalR链接。this
** 注意:当链接断开后从新发起链接的时候,SignalR 不会保留组成员身份,必须从新加入分组。3d
下面的代码演示了如何对分组进行操做,要对分组进行操做,主要包含三个方面:code
public async Task AddToGroupAsync(string groupName) { await Groups.AddToGroupAsync(this.Context.ConnectionId, groupName); }
public async Task RemoveFromGroupAsync(string groupName) { await Groups.RemoveFromGroupAsync(this.Context.ConnectionId, groupName); }
public async Task SendToGroupAsync(string groupName, ChatMessage message) { await Clients.Group(groupName).SendAsync(groupName, new object[] { message }); }
对分组的操做很是的简单,几乎都是一行代码的事情,不得不说,微软的封装实在是太好了。
经过上面对用户和分组的学习,再去扩展学习其它推送消息的方式,就很是的好理解和上手,在 SignalR 内部还有多种推送消息的方式,他们分别是
List<string> blackList = new List<string>(); public async Task OtherSendAsync(ChatMessage body) { // 给当前链接到 Hub 上的全部链接发送消息,至关于广播 await Clients.All.SendAsync("Recv", body); // 给当前链接对象发送消息 await Clients.Caller.SendAsync("Recv", body); // 给其它全部链接的客户端发送消息,除了当前正在链接的客户端 await Clients.Others.SendAsync("Recv", body); // 查找当前全部链接的客户端(排除本身),若是是已加入此分组,则给他们推送消息 await Clients.OthersInGroup("groupName").SendAsync("Recv", body); // 给除了 blackList(黑名单)以外的全部人发送消息 await Clients.AllExcept(blackList).SendAsync("Recv", body); }
本示例代码包含两个简单的界面
最近在作一个开源项目,还处于试用阶段,准备写个使用的 WIKI 出来,看看你们是否感兴趣,此 SingalR 系列只能不按期更新了,抱歉。
已托管到 GitHub 仓库
https://github.com/lianggx/Examples/tree/master/SignalR/Ron.SignalRLesson2