ASP.NET SingalR 点对点聊天实现思路总结
前一段时间写了一个简单的聊天室,是群聊的方式。博客地址:http://www.cnblogs.com/panzi/p/4980346.html。还有一种需求就是常见的尤其是培训机构的主页面,经常会有1对1咨询聊天窗口。那么用singalR如何实现1对1聊天呢。
其实很简单。我们先看看SingalR里的IHubConnectionContext接口代码:
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); }
这里我们就用 T Client(string connectionId); 这个方法。调用方式为 Clients.Client("connectionId").clientFun(msg); //(clientFun为自定义客户端接收消息方法名)具体细节不在描述,这里比较关键的就是,如何知道对方的ConnectionId,因为ConnectionId是自动生成的而且,每次刷新页面都会变,SingalR本身又不带统计在线用户的方法,所以,这个需要自己去实现。思路很清晰,这里先用 静态List做用户在线列表信息存储。代码如下:
/// <summary> /// 简单用户统计model /// </summary> public class HubUser { /// <summary> /// 连接服务器之后,自动生成的connectionId /// </summary> public string ConnectionId { get; set; } /// <summary> /// 客户端用户的主键ID /// 一般和业务相关的用户ID /// </summary> public string ClientUserId { get; set; } /// <summary> /// 聊天所在组 /// </summary> public string GroupId { get; set; } }
public sealed class OnlineUserPool { private static Lazy<List<HubUser>> _onlineUser = new Lazy<List<HubUser>>(); public static List<HubUser> OnlineUser { get { return _onlineUser.Value; } } /// <summary> /// 添加用户,一般在用户 连接服务器或者用户重新连接的时候 /// </summary> /// <param name="user"></param> public static void AddUser(HubUser user) { DeleteUser(user); _onlineUser.Value.Add(user); } /// <summary> /// 删除某个在线用户 /// </summary> /// <param name="clientUserId"></param> /// <param name="connectionId"></param> public static void DeleteUser(HubUser user, bool unConnected = true) { var onlineUser = IsOnline(user); if (onlineUser != null) { _onlineUser.Value.Remove(onlineUser); } } public static HubUser IsOnline(HubUser user) { if (user == null) { throw new ArgumentNullException(); } string clientUserId = user.ClientUserId; string connectionId = user.ConnectionId; if (!string.IsNullOrEmpty(clientUserId)) { return _onlineUser.Value.FirstOrDefault(x => x.ClientUserId == clientUserId); } else { return _onlineUser.Value.FirstOrDefault(x => x.ConnectionId == connectionId); } } /// <summary> /// 获取在线总数 /// </summary> /// <returns></returns> public static long GetUserCount() { return _onlineUser.Value.Count; } }
可以看到 OnlineUserPool 类实现了往静态列表添加用户,删除用户等一系列操作。
添加用户操作需要,在用户接入到聊天室的时候执行:
public Task Join(ZjMessage message) { message.connectionId = Context.ConnectionId; //就是用户加入的时候 OnlineUserPool.AddUser(new HubUser { ClientUserId = message.userid, ConnectionId = Context.ConnectionId }); message.msg = "当前已经有:" + OnlineUserPool.GetUserCount() + " 人在线"; return Clients.All.receiveMessage(new { type = "join", msg = message }); }
删除用户操作就在重写OnDisconnect方法里执行,需要根据ConnectionId删除
public override Task OnDisconnected(bool stopCalled) { ZjMessage message = new ZjMessage(Context.ConnectionId); //用户离开 //用户断线,需要将该用户从列表中删除,(应该考虑短暂失去连接的可能性,不能直接从列表删除。) OnlineUserPool.DeleteUser(new HubUser { ConnectionId = Context.ConnectionId }); return Clients.All.receiveMessage(new { type = "left", msg = message }); }
所以,当你想点对点发送消息的时候,将对方userId传送到服务器,然后服务器从在线列表里面查询出相应的connectionID,然后将消息推送到该connectionID的用户,就实现了在线两个人聊天了。当然,用静态列表的方式也不是很好,如果用户量庞大,会不会出什么问题呢,我具体没研究过。一般的方案,是放在专门的缓存服务器存储,或者NOSQL数据库存储也可以吧,方案有很多,由于没有具体做过也不敢多费口舌。这个思路是没问题的,当然也会有更好的方法吧。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?