视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
SignalR中丰富多彩的消息推送方式的实现代码
2020-11-27 22:34:38 责编:小采
文档


在上一篇 SignalR 文章中,演示了如何通过 SignalR 实现了简单的聊天室功能;本着简洁就是美的原则,这一篇我们也来聊聊在 SignalR 中的用户和组的概念,理解这些基础知识有助于更好的开发基于 SignalR 的应用,通过对用户和分组的理解,进一步扩展出对用户和分组的管理,以及消息推送的各种方式,为全面接入 SignalR 做准备。

1. 用户

在 SignalR 中,用户表示连接,一个用户代表一个连接,一个“系统用户”可以创建多个连接身份,通过函数集线器,可以给一个用户的所有连接发送消息;比如一个“系统用户”拥有多个连接,这些连接分别是 Web连接、AndroId手机客户端连接,IOS手机客户端连接、或者其它客户端连接,“系统用户”分别登录了这些客户端,同时创建了多个连接;默认情况下这些连接都通过 ClaimTypes.NameIdentifier 在 ClaimsPrincipal 于用户标识进行关联。

** 注意:用户标识符是区分大小写的,为了实现一个客户多个连接,本例还简单实现了一个基于 ClaimsIdentity 登录接口,算是意外惊喜。

1.1 用户连接管理

为了直观的观察到用户是可以拥有多连接的,需要建立一个本地静态对象,用于存储用户连接

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 中删除。这样就实现了一个简单的用户连接管理。

在上面的代码中,当前用户昵称是根据 var userName = this.Context.User.Identity.Name; 这行代码获取的,为了取得这个用户昵称,我们实现了一个简单的 UserIdentity 登录,然后将 User 信息写入到 Cookie 中,最后才可以通过 var userName = this.Context.User.Identity.Name; 获得当前登录用户昵称(熟悉 ID 登录流程的同学应该不会感到陌生,实际上我也很少使用 ID 验证)

1.2 给单个用户发送消息

 [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 将消息推送到目标客户端连接中。

2. 分组

分组的概念类似于聊天室,每个房间就是一个的分组,用户可以选择加入 A 房间,也可以选择加入 B 房间,如果业务允许,一个用户还可以加入多个分组(房间),通过使用分组对用户进行管理,可以实现一个或者多个聊天房间,用户可以加入分组,也可以将用户从分组中删除(类似离开房间),这里的用户并发真正意义上的“系统用户”,而是指系统用户创建的那些 SignalR连接。

** 注意:当连接断开后重新发起连接的时候,SignalR 不会保留组成员身份,必须重新加入分组。

下面的代码演示了如何对分组进行操作,要对分组进行操作,主要包含三个方面:

2.1 加入分组

 public async Task AddToGroupAsync(string groupName)
 {
 await Groups.AddToGroupAsync(this.Context.ConnectionId, groupName);
 }

2.2 离开分组

 public async Task RemoveFromGroupAsync(string groupName)
 {
 await Groups.RemoveFromGroupAsync(this.Context.ConnectionId, groupName);
 }

2.3 发送消息到指定分组

 public async Task SendToGroupAsync(string groupName, ChatMessage message)
 {
 await Clients.Group(groupName).SendAsync(groupName, new object[] { message });
 }

对分组的操作非常的简单,几乎都是一行代码的事情,不得不说,微软的封装实在是太好了。

3. SignalR的推送消息的其它方式

通过上面对用户和分组的学习,再去扩展学习其它推送消息的方式,就非常的好理解和上手,在 SignalR 内部还有多种推送消息的方式,他们分别是

3.1 All(全站推送)

3.2 Others(全站推送排除自己)

3.3 OthersInGroup(指定分组推送,排除自己)

3.4 AllExcept(除指定列表外的所有人)

3.5 演示代码

 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);
 }

4. 一个简单的示例

本示例代码包含两个简单的界面

4.1 登录

4.2 各种方式发送消息

结束语

最近在做一个开源项目,还处于试用阶段,准备写个使用的 WIKI 出来,看看大家是否感兴趣,此 SingalR 系列只能不定期更新了,抱歉。

演示代码下载

已托管到 GitHub 仓库

https://github.com/lianggx/Examples/tree/master/SignalR/Ron.SignalRLesson2

下载本文
显示全文
专题