• 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数据库存储也可以吧,方案有很多,由于没有具体做过也不敢多费口舌。这个思路是没问题的,当然也会有更好的方法吧。

  • 相关阅读:
    迭代器和生成器
    装饰器
    函数进阶二
    函数进阶
    函数的初识
    python基础七
    python基础六
    python基础五
    python基础四
    python2与python3的区别
  • 原文地址:https://www.cnblogs.com/panzi/p/5056365.html
Copyright © 2020-2023  润新知