SignalR实时通信

SignalR

.NET8 使用API构建SignalR服务Demo

  1. Program中注册和配置SignalR服务

    //注册SignalR服务
    builder.Services.AddSignalR();
    
    //添加跨域支持(如果时跨域Web请求SignalR服务需要配置)
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowAllOrigins",
            p => p.WithOrigins("http://localhost:5287").AllowAnyMethod().AllowAnyHeader().AllowCredentials());
    });
    
    //配置SignalR服务 为当前应用程序地址+/myhub
    app.MapHub<MyHub>("/myhub").RequireCors("AllowAllOrigins");
    //使用CORS策略
    app.UseCors("AllowAllOrigins"); //AllowAllOrigins 表明使用的策略名称
    

    提示:在客户端中调用SignalR 无跨域问题,web端需要处理

  2. 实现MyHub类

        [EnableCors("AllowAllOrigins")]
        public class MyHub : Hub
        {
            // 定义用户映射类(示例)
            private static readonly ConcurrentDictionary<string, string> _userConnections =
                new ConcurrentDictionary<string, string>();
    
            public async Task SendMessage(paramsEntity param)
            {
                await Clients.All.SendAsync("ReceiveMessage", param);
            }
    
            /// <summary>
            /// 向其他用户发送消息
            /// </summary>
            /// <param name="param"></param>
            /// <returns></returns>
            public async Task SendOtherMessage(paramsEntity param)
            {
                await Clients.Others.SendAsync("ReceiveMessage", param);
            }
    
            [HubMethodName("RegisterUser")]
            // Hub 方法:注册用户与连接的映射
            public async Task RegisterUser(string userId)
            {
                // 检查用户是否已存在
                if (_userConnections.TryGetValue(userId, out var oldConnectionId))
                {
                    // 断开旧连接
                    try
                    {
                        //向客户端发送断开连接标识
                        await Clients.Client(oldConnectionId).SendAsync("ForceDisconnect", "您的账号已在其他设备登录");
    
                        //服务端主动强制关闭连接
                    }
                    catch (Exception ex)
                    {
                        // 记录错误但不影响新连接
                        Console.WriteLine($"断开旧连接失败: {ex.Message}");
                    }
    
                    // 从映射中移除旧连接
                    _userConnections.TryRemove(userId, out _);
                }
                _userConnections.AddOrUpdate(userId, Context.ConnectionId, (key, oldValue) => Context.ConnectionId);
                // 方法1:直接使用已有的 ReceiveMessage 通知
                await Clients.Others.SendAsync("ReceiveMessage", new paramsEntity
                {
                    user = "系统通知",
                    message = $"{userId} 已上线"
                });
    
                // 方法2:如果客户端已注册 UserConnected,也可以同时发送
                //await Clients.All.SendAsync("UserConnected", userId);
            }
    
            [HubMethodName("SendPrivateMessage")]
            // Hub 方法:向指定用户发送消息
            public async Task SendToUser(string userId, paramsEntity param)
            {
                if (_userConnections.TryGetValue(userId, out var connectionId))
                {
                    await Clients.Client(connectionId).SendAsync("ReceiveMessage", param);
                }
            }
    
            [HubMethodName("Disconnected")]
            // 处理客户端断开连接
            public override async Task OnDisconnectedAsync(Exception? exception)
            {
                // 从映射中移除断开的连接
                var userId = _userConnections.FirstOrDefault(x => x.Value == Context.ConnectionId).Key;
                if (userId != null)
                {
                    _userConnections.TryRemove(userId, out _);
                    await Clients.Others.SendAsync("ReceiveMessage", new paramsEntity
                    {
                        user = "系统通知",
                        message = $"{userId} 已下线"
                    });
                }
    
                await base.OnDisconnectedAsync(exception);
            }
    
        }
    
        public class paramsEntity
        {
            public string user { get; set; }
            public string message { get; set; }
        }
    
  3. Web前端实现

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>SignalR 测试工具</title>
        <script src="https://cdn.tailwindcss.com"></script>
        <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
        <!--<script src="https://cdn.jsdelivr.net/npm/@microsoft/signalr@6.0.10/dist/browser/signalr.min.js"></script>-->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>
        <!-- Tailwind 配置 -->
        <script>
            tailwind.config = {
                theme: {
                    extend: {
                        colors: {
                            primary: '#165DFF',
                            secondary: '#0FC6C2',
                            success: '#00B42A',
                            warning: '#FF7D00',
                            danger: '#F53F3F',
                            dark: '#1D2129',
                            'dark-2': '#4E5969',
                            'light-1': '#F2F3F5',
                            'light-2': '#E5E6EB',
                        },
                        fontFamily: {
                            inter: ['Inter', 'system-ui', 'sans-serif'],
                        },
                    },
                }
            }
        </script>
    
        <style type="text/tailwindcss">
            @layer utilities {
                .content-auto {
                    content-visibility: auto;
                }
    
                .scrollbar-hide::-webkit-scrollbar {
                    display: none;
                }
    
                .scrollbar-hide {
                    -ms-overflow-style: none;
                    scrollbar-width: none;
                }
    
                .message-appear {
                    animation: fadeIn 0.3s ease-in-out;
                }
    
                @keyframes fadeIn {
                    from {
                        opacity: 0;
                        transform: translateY(10px);
                    }
    
                    to {
                        opacity: 1;
                        transform: translateY(0);
                    }
                }
            }
        </style>
    </head>
    <body class="bg-gray-50 font-inter text-dark min-h-screen flex flex-col">
        <!-- 导航栏 -->
        <header class="bg-white shadow-sm sticky top-0 z-10">
            <div class="container mx-auto px-4 py-3 flex items-center justify-between">
                <div class="flex items-center space-x-2">
                    <i class="fa fa-comments text-primary text-2xl"></i>
                    <h1 class="text-xl font-bold text-dark">SignalR 测试工具</h1>
                </div>
                <div class="flex items-center space-x-4">
                    <button id="connectBtn" class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition flex items-center">
                        <i class="fa fa-plug mr-2"></i>
                        <span>连接</span>
                    </button>
                    <span id="connectionStatus" class="text-dark-2 text-sm hidden">
                        <i class="fa fa-circle-o text-warning"></i> 未连接
                    </span>
                </div>
            </div>
        </header>
    
        <!-- 主内容区 -->
        <main class="flex-grow container mx-auto px-4 py-6">
            <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
                <!-- 消息发送区域 -->
                <div class="lg:col-span-1">
                    <div class="bg-white rounded-xl shadow-sm p-6 h-full flex flex-col">
                        <h2 class="text-lg font-semibold mb-4 flex items-center">
                            <i class="fa fa-paper-plane text-primary mr-2"></i> 发送消息
                        </h2>
    
                        <div class="flex-grow">
                            <div class="mb-4">
                                <label for="messageType" class="block text-sm font-medium text-dark-2 mb-1">消息类型</label>
                                <select id="messageType" class="w-full px-3 py-2 border border-light-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition">
                                    <option value="broadcast">广播消息</option>
                                    <option value="private">私信消息</option>
                                    <option value="group">群组消息</option>
                                </select>
                            </div>
                            <div id="userNameInputField" class="mb-4">
                                <label for="userNameInput" class="block text-sm font-medium text-dark-2 mb-1">用户名</label>
                                <input type="text" id="userNameInput" placeholder="输入用户名" class="w-full px-3 py-2 border border-light-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition">
                            </div>
    
                            <div id="recipientField" class="mb-4 hidden">
                                <label for="recipient" class="block text-sm font-medium text-dark-2 mb-1">接收者</label>
                                <input type="text" id="recipient" placeholder="输入接收者ID" class="w-full px-3 py-2 border border-light-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition">
                            </div>
    
                            <div class="mb-4">
                                <label for="messageContent" class="block text-sm font-medium text-dark-2 mb-1">消息内容</label>
                                <textarea id="messageContent" rows="8" placeholder="输入要发送的消息..." class="w-full px-3 py-2 border border-light-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition resize-none"></textarea>
                            </div>
    
                            <div class="flex space-x-3">
                                <button id="sendMessageBtn" class="flex-grow px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed">
                                    <i class="fa fa-send mr-2"></i> 发送消息
                                </button>
                                <button id="clearInputBtn" class="px-4 py-2 border border-light-2 text-dark-2 rounded-lg hover:bg-light-1 transition">
                                    <i class="fa fa-trash"></i>
                                </button>
                            </div>
                        </div>
                    </div>
                </div>
    
                <!-- 消息接收区域 -->
                <div class="lg:col-span-2">
                    <div class="bg-white rounded-xl shadow-sm p-6 h-full flex flex-col">
                        <div class="flex items-center justify-between mb-4">
                            <h2 class="text-lg font-semibold flex items-center">
                                <i class="fa fa-comments text-primary mr-2"></i> 消息接收
                            </h2>
                            <div class="flex space-x-2">
                                <div class="relative">
                                    <input type="text" id="messageFilter" placeholder="搜索消息..." class="pl-8 pr-3 py-1.5 text-sm border border-light-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition">
                                    <i class="fa fa-search absolute left-3 top-2 text-dark-2/60"></i>
                                </div>
                                <button id="clearMessagesBtn" class="px-3 py-1.5 text-sm border border-light-2 text-dark-2 rounded-lg hover:bg-light-1 transition">
                                    <i class="fa fa-eraser mr-1"></i> 清空
                                </button>
                            </div>
                        </div>
    
                        <div id="messageContainer" class="flex-grow overflow-y-auto scrollbar-hide bg-light-1/50 rounded-lg p-4 mb-4">
                            <!-- 消息将动态添加到这里 -->
                            <div class="text-center text-dark-2/60 py-10">
                                <i class="fa fa-comments-o text-3xl mb-2 block"></i>
                                <p>尚未接收到任何消息</p>
                                <p class="text-xs mt-1">连接服务器并发送消息后开始接收</p>
                            </div>
                        </div>
    
                        <div class="flex items-center justify-between text-xs text-dark-2/60">
                            <div>
                                <span id="totalMessages">0</span> 条消息
                            </div>
                            <div>
                                最后更新: <span id="lastUpdate">-</span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </main>
    
        <!-- 页脚 -->
        <footer class="bg-white py-4 border-t border-light-2">
            <div class="container mx-auto px-4 text-center text-dark-2/70 text-sm">
                <p>SignalR 测试工具 &copy; 2023</p>
            </div>
        </footer>
    
        <script src="../lib/jquery/dist/jquery.js"></script>
        <script>
            // SignalR 连接对象
            let connection = null;
            let isConnected = false;
    
            // DOM 元素
            const connectBtn = document.getElementById('connectBtn');
            const connectionStatus = document.getElementById('connectionStatus');
            const sendMessageBtn = document.getElementById('sendMessageBtn');
            const clearInputBtn = document.getElementById('clearInputBtn');
            const clearMessagesBtn = document.getElementById('clearMessagesBtn');
            const messageContent = document.getElementById('messageContent');
            const messageContainer = document.getElementById('messageContainer');
            const messageType = document.getElementById('messageType');
            const recipientField = document.getElementById('recipientField');
            const recipient = document.getElementById('recipient');
            const totalMessages = document.getElementById('totalMessages');
            const lastUpdate = document.getElementById('lastUpdate');
            const messageFilter = document.getElementById('messageFilter');
            let user = document.getElementById('userNameInput').value;
            // 初始化
            init();
    
            function init() {
                // 禁用发送按钮直到连接建立
                sendMessageBtn.disabled = true;
    
                // 监听消息类型变化
                messageType.addEventListener('change', () => {
                    if (messageType.value === 'private') {
                        recipientField.classList.remove('hidden');
                    } else {
                        recipientField.classList.add('hidden');
                    }
                });
    
                // 连接按钮点击事件
                connectBtn.addEventListener('click', connectToSignalR);
    
                // 发送消息按钮点击事件
                sendMessageBtn.addEventListener('click', sendMessage);
    
                // 清空输入按钮点击事件
                clearInputBtn.addEventListener('click', () => {
                    messageContent.value = '';
                    messageContent.focus();
                });
    
                // 清空消息按钮点击事件
                clearMessagesBtn.addEventListener('click', () => {
                    messageContainer.innerHTML = `
                  <div class="text-center text-dark-2/60 py-10">
                    <i class="fa fa-comments-o text-3xl mb-2 block"></i>
                    <p>消息已清空</p>
                  </div>
                `;
                    totalMessages.textContent = '0';
                });
    
                // 消息过滤
                messageFilter.addEventListener('input', filterMessages);
            }
    
            // 连接到 SignalR 服务器
            function connectToSignalR() {
                if (isConnected) {
                    disconnect();
                    return;
                }
    
                //$.ajax({
                //    url: "http://localhost:5299/WeatherForecast?message=123",
                //    type: 'POST',
                //    data: {},
                //    success: function () {
                //    },
                //    dataType: "json",
                //    contentType: "application/json"
                //});
    
    
                // 创建 SignalR 连接
                connection = new signalR.HubConnectionBuilder()
                    .withUrl("http://localhost:5299/myhub") // 替换为您的 SignalR 服务器地址
                    .configureLogging(signalR.LogLevel.Information)
                    .build();
                
    
                // 定义接收消息的处理程序
                connection.on("ReceiveMessage", (res) => {
                    user = document.getElementById('userNameInput').value;
                    if (res.user != user) {
                        addMessage(res.user, res.message, "received");
                    }
                });
    
                //监听强制断开连接
                connection.on("ForceDisconnect", (message) => {
                    alert(message);
    
                    // 客户端主动断开连接
                    connection.stop().then(() => {
                        // 更新UI状态
                        updateConnectionStatus("disconnected");
                        connectBtn.innerHTML = '<i class="fa fa-plug mr-2"></i><span>连接</span>';
                        connectBtn.classList.remove('bg-danger');
                        connectBtn.classList.add('bg-primary');
                        sendMessageBtn.disabled = true;
                    });
                });
    
                // 连接状态变化
                connection.onreconnecting((error) => {
                    updateConnectionStatus("reconnecting", error);
                });
    
                connection.onreconnected((connectionId) => {
                    updateConnectionStatus("connected", connectionId);
                });
    
                connection.onclose((error) => {
                    updateConnectionStatus("disconnected", error);
                    isConnected = false;
                    connectBtn.innerHTML = '<i class="fa fa-plug mr-2"></i><span>连接</span>';
                    connectBtn.classList.remove('bg-danger');
                    connectBtn.classList.add('bg-primary');
                    sendMessageBtn.disabled = true;
                });
    
                // 启动连接
                connection.start()
                    .then(() => {
                        isConnected = true;
                        updateConnectionStatus("connected");
                        connectBtn.innerHTML = '<i class="fa fa-plug mr-2"></i><span>断开</span>';
                        connectBtn.classList.remove('bg-primary');
                        connectBtn.classList.add('bg-danger');
                        sendMessageBtn.disabled = false;
    
                        // 添加连接成功消息
                        addMessage("系统", "已成功连接到服务器", "system");
                        user = $('#userNameInput').val();
                        if (user == "") {
                            alert("请先登录")
                        } else {
                            connection.invoke("RegisterUser", user);
                        }
                    })
                    .catch(error => {
                        console.error("连接失败: ", error);
                        updateConnectionStatus("disconnected", error);
                        addMessage("系统", `连接失败: ${error.message}`, "error");
                    });
            }
    
            // 断开连接
            function disconnect() {
                if (connection) {
                    connection.stop()
                        .then(() => {
                            isConnected = false;
                            updateConnectionStatus("disconnected");
                            connectBtn.innerHTML = '<i class="fa fa-plug mr-2"></i><span>连接</span>';
                            connectBtn.classList.remove('bg-danger');
                            connectBtn.classList.add('bg-primary');
                            sendMessageBtn.disabled = true;
                            addMessage("系统", "已断开与服务器的连接", "system");
                        })
                        .catch(error => {
                            console.error("断开连接失败: ", error);
                            addMessage("系统", `断开连接失败: ${error.message}`, "error");
                        });
                }
            }
    
            // 更新连接状态显示
            function updateConnectionStatus(status, info = null) {
                let statusText, statusIcon, statusColor;
    
                switch (status) {
                    case "connected":
                        statusText = "已连接";
                        statusIcon = "fa-circle";
                        statusColor = "text-success";
                        break;
                    case "disconnected":
                        statusText = "未连接";
                        statusIcon = "fa-circle-o";
                        statusColor = "text-warning";
                        break;
                    case "reconnecting":
                        statusText = "重连中...";
                        statusIcon = "fa-refresh fa-spin";
                        statusColor = "text-warning";
                        break;
                }
    
                connectionStatus.innerHTML = `<i class="fa ${statusIcon} ${statusColor}"></i> ${statusText}`;
                connectionStatus.classList.remove('hidden');
    
                if (info) {
                    console.log(`Connection ${status}:`, info);
                }
            }
    
            // 发送消息
            function sendMessage() {
                const content = messageContent.value.trim();
                if (!content) {
                    alert("请输入消息内容");
                    return;
                }
    
                const type = messageType.value;
                let recipientValue = recipient.value.trim();
    
                // 根据消息类型调用不同的方法
                if (type === "private" && !recipientValue) {
                    alert("请输入接收者ID");
                    return;
                }
    
                // 添加发送的消息到界面
                addMessage("我", content, "sent");
    
                // 调用服务器方法
                //调用服务器接口发送消息
                if (type === "private") {
                    const userName = document.getElementById('userNameInput').value || '匿名用户'
                    const recipient = document.getElementById('recipient').value || '匿名用户'
                    //$.post("http://localhost:5299/SignalR", { receptID: recipient, param:{ user: userName, message: content } }, function (res) {
    
                    //}, "json");
                    connection.invoke("SendPrivateMessage", recipientValue,{ user: userName, message: content })
                        .catch(error => {
                            console.error("发送私信失败: ", error);
                            addMessage("系统", `发送私信失败: ${error.message}`, "error");
                        });
                } else if (type === "group") {
                    connection.invoke("SendGroupMessage", "TestGroup", content)
                        .catch(error => {
                            console.error("发送群组消息失败: ", error);
                            addMessage("系统", `发送群组消息失败: ${error.message}`, "error");
                        });
                } else {
                    // 广播消息
                    //connection.invoke("SendMessage", content)
                    //    .catch(error => {
                    //        console.error("发送广播消息失败: ", error);
                    //        addMessage("系统", `发送广播消息失败: ${error.message}`, "error");
                    //    });
                    const userName = document.getElementById('userNameInput').value || '匿名用户'
                    $.post("http://localhost:5299/WeatherForecast", { user: userName, message: content }, function (res) {
    
                    },"json");
                }
    
                // 清空输入框
                messageContent.value = '';
                messageContent.focus();
            }
    
            // 添加消息到界面
            function addMessage(sender, content, type) {
                // 移除空消息提示
                if (messageContainer.querySelector('.text-center')) {
                    messageContainer.innerHTML = '';
                }
    
                // 创建消息元素
                let messageClass, iconClass, bgColor;
    
                switch (type) {
                    case "sent":
                        messageClass = "ml-auto";
                        iconClass = "fa-paper-plane text-primary";
                        bgColor = "bg-primary/10";
                        break;
                    case "received":
                        messageClass = "mr-auto";
                        iconClass = "fa-comment-o text-secondary";
                        bgColor = "bg-white";
                        break;
                    case "system":
                        messageClass = "mx-auto text-center";
                        iconClass = "fa-info-circle text-dark-2";
                        bgColor = "bg-light-2/50";
                        break;
                    case "error":
                        messageClass = "mx-auto text-center";
                        iconClass = "fa-exclamation-circle text-danger";
                        bgColor = "bg-danger/10";
                        break;
                }
    
                const messageElement = document.createElement('div');
                messageElement.className = `mb-3 max-w-[85%] ${messageClass} message-appear`;
    
                const timestamp = new Date().toLocaleTimeString();
    
                if (type !== "system" && type !== "error") {
                    messageElement.innerHTML = `
                  <div class="flex items-start">
                    <div class="mr-2 mt-1">
                      <i class="fa ${iconClass} text-xl"></i>
                    </div>
                    <div>
                      <div class="text-xs text-dark-2/60 mb-0.5">${sender} <span class="ml-2">${timestamp}</span></div>
                      <div class="${bgColor} p-3 rounded-lg shadow-sm break-words">
                        ${formatMessage(content)}
                      </div>
                    </div>
                  </div>
                `;
                } else {
                    messageElement.innerHTML = `
                  <div class="${bgColor} p-2 rounded-lg text-xs inline-block">
                    <i class="fa ${iconClass} mr-1"></i> ${content} <span class="ml-2">${timestamp}</span>
                  </div>
                `;
                }
    
                messageContainer.appendChild(messageElement);
    
                // 滚动到底部
                messageContainer.scrollTop = messageContainer.scrollHeight;
    
                // 更新消息计数和最后更新时间
                updateMessageStats();
            }
    
            // 格式化消息内容
            function formatMessage(content) {
                // 简单的HTML转义
                const escapedContent = content
                    .replace(/&/g, '&amp;')
                    .replace(/</g, '&lt;')
                    .replace(/>/g, '&gt;')
                    .replace(/"/g, '&quot;')
                    .replace(/'/g, '&#039;');
    
                // 替换换行符为<br>
                return escapedContent.replace(/\n/g, '<br>');
            }
    
            // 更新消息统计信息
            function updateMessageStats() {
                const count = messageContainer.querySelectorAll('.message-appear').length;
                totalMessages.textContent = count;
                lastUpdate.textContent = new Date().toLocaleTimeString();
            }
    
            // 过滤消息
            function filterMessages() {
                const filter = messageFilter.value.toLowerCase();
                const messages = messageContainer.querySelectorAll('.message-appear');
    
                messages.forEach(message => {
                    const content = message.textContent.toLowerCase();
                    if (content.includes(filter)) {
                        message.style.display = 'block';
                    } else {
                        message.style.display = 'none';
                    }
                });
            }
        </script>
    </body>
    </html>
    
  4. 控制台实现

    static async Task Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
    
        //创建连接
        var connection = new HubConnectionBuilder().WithUrl("http://localhost:5299/myHub")
            .Build();
    
        connection.On<paramsEntity>("ReceiveMessage", (res ) =>
        {
            Console.WriteLine($"收到来自 {res.user} 的消息: {res.message}");
        });
        try
        {
            // 启动连接
            await connection.StartAsync();
            Console.WriteLine("已成功连接到SignalR服务!");
    
            // 发送消息示例
            Console.WriteLine("请输入您的名字:");
            var userName = Console.ReadLine();
    
            await connection.InvokeAsync("RegisterUser", userName);
    
            Console.WriteLine("请输入要发送的消息 (输入'退出'结束程序):");
            while (true)
            {
                var message = Console.ReadLine();
    
                if (message?.ToLower() == "退出")
                {
                    break;
                }
    
                // 调用服务端方法
                await connection.InvokeAsync("SendOtherMessage", new paramsEntity { user=userName, message=message });
            }
            await connection.InvokeAsync("Disconnected");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"连接出错: {ex.Message}");
        }
        finally
        {
            // 关闭连接
            if (connection.State == HubConnectionState.Connected)
            {
                
                await connection.StopAsync();
            }
            Console.WriteLine("已断开与SignalR服务的连接。");
        }
    
        Console.WriteLine("按任意键退出...");
        Console.ReadKey();
    }
    
    1. 总结:SignalR 在B/S中需要定义Hub服务,客户端与服务端通过约定的方法名,如上述中的ReceiveMessage ForceDisconnect 服务端通过调用SendAsync 向ReceiveMessage发送消息param,客户端通过on监听接受消息。这种是由服务端向客户端发送消息。

      await Clients.All.SendAsync("ReceiveMessage", param);
      
    2. 客户端需要向客户端发送消息

      1. B/S中通过引用 signalr.js

        <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>
        

        创建 SignalR 连接

        // 创建 SignalR 连接
        connection = new signalR.HubConnectionBuilder()
            .withUrl("http://localhost:5299/myhub") // 替换为您的 SignalR 服务器地址
            .configureLogging(signalR.LogLevel.Information)
            .build();
                    
        

        通过invoke方法调用后端服务MyHub定义的方法 SendPrivateMessage

        connection.invoke("SendPrivateMessage", recipientValue,{ user: userName, message: content })
            .catch(error => {
                console.error("发送私信失败: ", error);
                addMessage("系统", `发送私信失败: ${error.message}`, "error");
            });
        

        后端定义的SendPrivateMessage 指定客户端会话

        [HubMethodName("SendPrivateMessage")]
        // Hub 方法:向指定用户发送消息
        public async Task SendToUser(string userId, paramsEntity param)
        {
            if (_userConnections.TryGetValue(userId, out var connectionId))
            {
                await Clients.Client(connectionId).SendAsync("ReceiveMessage", param);
            }
        }
        
posted @ 2025-07-15 16:25  坐听风声雨声  阅读(35)  评论(0)    收藏  举报