会话管理


  会话管理用来管理用户的标识,它与 ConnectionId 进行映射,可双向查找。SessionManager 是一个泛型类,它接收一个任意的类型作为用户标识,该用户标识类需要重写 ToString 方法,以输出用户标识字符串,作为检索依据。 下面建立一个自定义的用户标识类,包含用户ID、用户类型和用户名称,其标识使用 Type:Id 字符串,如下所示:

public class User
{
    public int Id { get; set; }

    public int Type { get; set; }

    public string Name { get; set; }

    public override string ToString()
    {
        return $"{Type}:{Id}";
    }
}

  在 WebSocketHandler 实现类中,实现如下代码:

public class ChatHandler : WebSocketHandler
{
    private SessionManager<User> _manager = new SessionManager<User>();

    /// <summary>
    /// 客户端连接上后,使用该方法添加用户标识。
    /// </summary>
    /// <param name="userId"></param>
    /// <param name="userType"></param>
    /// <param name="userName"></param>
    public void Connect(int userId, int userType, string userName)
    {
        _manager.Add(ConnectionId, new User { Id = userId, Type = userType, Name = userName });
    }

    /// <summary>
    /// 向用户发送消息。
    /// </summary>
    /// <param name="userId"></param>
    /// <param name="userType"></param>
    /// <param name="message"></param>
    public void Send(int userId, int userType, string message)
    {
        //根据标识查找 connectioId,然后向其发送通知
        var connectioId = _manager.FindConnection(new User { Id = userId, Type = userType });
        if (!string.IsNullOrEmpty(connectioId))
        {
            Clients.Client(connectioId).SendAsync("OnReceiveMsg", message);
        }
    }

    protected override void OnDisconnected()
    {
        _manager.Remove(ConnectionId);

        base.OnDisconnected();
    }

    protected override void OnHeartBeating()
    {
        //心跳时刷新用户会话
        _sessionManager.Refresh(ConnectionId);

        base.OnHeartBeating();
    }
}

  FindIdentity 方法可以通过 ConnectionId 查找对应的用户标识,如下所示:

public class ChatHandler : WebSocketHandler
{
    private SessionManager<User> _manager = new SessionManager<User>();

    protected override void OnDisconnected()
    {
        var user = _sessionManager.FindIdentity(ConnectionId);

        _sessionManager.Remove(ConnectionId);

        //某人下线后,通知所有人,并更新在线人数
        Clients.All.SendAsync("onNotifyOffline", user.Name, _sessionManager.Count);
        base.OnDisconnected();
    }
}

  GetIdentityTable 方法用于获取 ConnectionId 与用户标识的映射字典,GetIdentityList 方法用于获取用户标识列表,如下所示:

public class ChatHandler : WebSocketHandler
{
    private SessionManager<User> _manager = new SessionManager<User>();

    /// <summary>
    /// 客户端连接上后,使用该方法添加用户标识。
    /// </summary>
    /// <param name="userId"></param>
    /// <param name="userType"></param>
    /// <param name="userName"></param>
    public void Connect(int userId, int userType, string userName)
    {
        _manager.Add(ConnectionId, new User { Id = userId, Type = userType, Name = userName });
        
        var users = _sessionManager.GetIdentityList().OrderBy(s => s.Name).ToList();
        //通知所有人,刷新在线列表
        Clients.All.SendAsync("onUpdateOnlineList", users);
        //通知其他人,某人上线了
        Clients.Other.SendAsync("onNotifyOnline", userName);
    }
}

💡 小提示

  使用分布式部署后,会话管理器会使用分布式缓存管理器,因此你需要配置 Reids 之类的分布式缓存管理器。