You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SignalR中显示已连接(在线)客户端列表及数量

实现SignalR在线用户统计与列表展示

服务器端Hub改造

我们用静态ConcurrentDictionary存储在线用户(多线程环境下保证线程安全),重写OnConnectedAsync和OnDisconnectedAsync处理用户连接与断开,添加方法获取在线用户列表,同时在用户上下线时主动通知前端更新数据。

修改后的ChatHub代码:

using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;

namespace SignalRChat.Hubs
{
    public class ChatHub : Hub
    {
        // 静态字典存储在线用户:Key为ConnectionId,Value为用户名
        private static readonly ConcurrentDictionary<string, string> _onlineUsers = new ConcurrentDictionary<string, string>();

        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }

        // 用户连接时触发
        public override async Task OnConnectedAsync()
        {
            // 已认证场景:从当前用户身份获取用户名
            // 未认证场景:可通过前端传参获取,比如Context.GetHttpContext().Request.Query["user"]
            var userName = Context.User.Identity?.Name ?? $"匿名用户-{Context.ConnectionId.Substring(0, 8)}";
            
            // 将当前连接用户加入字典
            _onlineUsers.TryAdd(Context.ConnectionId, userName);
            
            // 通知所有客户端最新在线状态
            await Clients.All.SendAsync("UserJoined", userName);
            await PushOnlineUsersToAll();
            
            await base.OnConnectedAsync();
        }

        // 用户断开连接时触发
        public override async Task OnDisconnectedAsync(Exception? exception)
        {
            // 从字典移除当前用户
            if (_onlineUsers.TryRemove(Context.ConnectionId, out var userName))
            {
                // 通知所有客户端最新在线状态
                await Clients.All.SendAsync("UserLeft", userName);
                await PushOnlineUsersToAll();
            }
            
            await base.OnDisconnectedAsync(exception);
        }

        // 供前端调用获取在线用户数据
        public async Task GetOnlineUsers()
        {
            var userList = _onlineUsers.Values.ToList();
            await Clients.Caller.SendAsync("ReceiveOnlineUsers", userList.Count, userList);
        }

        // 私有方法:向所有客户端推送完整在线用户数据
        private async Task PushOnlineUsersToAll()
        {
            var userList = _onlineUsers.Values.ToList();
            await Clients.All.SendAsync("ReceiveOnlineUsers", userList.Count, userList);
        }
    }
}

前端页面(CSHTML)修改

添加展示在线用户总数和列表的HTML区域:

<div class="online-status">
    <p>当前在线用户数:<span id="onlineCount">0</span></p>
    <h5>在线用户列表</h5>
    <ul id="userList" class="list-group"></ul>
</div>

<!-- 保留原有聊天输入区域 -->
<div class="input-group mb-3">
    <input type="text" id="userInput" class="form-control" placeholder="用户名">
    <input type="text" id="messageInput" class="form-control" placeholder="输入消息">
    <button class="btn btn-primary" id="sendButton">发送</button>
</div>
<div id="messagesList"></div>

前端JavaScript逻辑更新

连接Hub后主动获取初始在线用户数据,监听服务器推送的上下线事件,实时更新页面:

"use strict";

var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();

// 接收在线用户数据并更新页面
connection.on("ReceiveOnlineUsers", function (count, userList) {
    document.getElementById("onlineCount").textContent = count;
    var userListElement = document.getElementById("userList");
    userListElement.innerHTML = ""; // 清空旧列表
    
    userList.forEach(function (user) {
        var li = document.createElement("li");
        li.className = "list-group-item";
        li.textContent = user;
        userListElement.appendChild(li);
    });
});

// 可选:用户上线提示
connection.on("UserJoined", function (userName) {
    alert(userName + " 已上线");
});

// 可选:用户下线提示
connection.on("UserLeft", function (userName) {
    alert(userName + " 已下线");
});

// 原有接收消息逻辑
connection.on("ReceiveMessage", function (user, message) {
    var li = document.createElement("li");
    li.textContent = `${user}: ${message}`;
    document.getElementById("messagesList").appendChild(li);
});

// 发送消息逻辑
document.getElementById("sendButton").addEventListener("click", function (event) {
    var user = document.getElementById("userInput").value;
    var message = document.getElementById("messageInput").value;
    connection.invoke("SendMessage", user, message).catch(function (err) {
        return console.error(err.toString());
    });
    event.preventDefault();
});

// 启动连接后立即获取在线用户数据
connection.start().then(function () {
    connection.invoke("GetOnlineUsers").catch(function (err) {
        return console.error(err.toString());
    });
}).catch(function (err) {
    return console.error(err.toString());
});

关键说明

  1. 内存字典选择:ConcurrentDictionary适配SignalR多实例场景,静态集合能在多个Hub实例间共享数据,并发操作更安全。
  2. 用户标识适配:如果无身份认证,可让前端通过QueryString传递用户名,连接地址改为withUrl("/chatHub?user=" + encodeURIComponent(userName)),再在OnConnectedAsync中通过Context.GetHttpContext().Request.Query["user"]获取。
  3. 实时更新逻辑:通过ReceiveOnlineUsers推送完整用户列表,确保页面数据始终准确;UserJoined/UserLeft可做辅助提示。

内容的提问来源于stack exchange,提问作者jvcent

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 17:47:06