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

.NET Core 3.1 SignalR:Hub外方法如何等待客户端响应并返回值

实现.NET Core 3.1 SignalR服务端等待客户端响应并返回值

要实现Hub外方法等待客户端响应,核心思路是利用TaskCompletionSource结合线程安全字典跟踪每个请求的异步任务,具体实现步骤如下:

1. 改造Hub类,添加响应处理逻辑

在MyHub中维护一个静态线程安全字典,存储请求ID与等待任务源的映射,同时提供供客户端调用的Response方法来完成任务:

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

public class MyHub : Hub
{
    // 线程安全字典,存储请求ID对应的等待任务源
    private static readonly ConcurrentDictionary<string, TaskCompletionSource<string>> _responseTasks = 
        new ConcurrentDictionary<string, TaskCompletionSource<string>>();

    // 客户端调用此方法返回响应结果
    public async Task Response(string requestId, string response)
    {
        if (_responseTasks.TryRemove(requestId, out var taskSource))
        {
            taskSource.SetResult(response);
        }
    }

    // 供外部注册请求任务源的方法
    internal static void RegisterResponseTask(string requestId, TaskCompletionSource<string> taskSource)
    {
        _responseTasks.TryAdd(requestId, taskSource);
    }
}

2. 改造Hub外请求方法为异步

将原方法改为异步形式,通过依赖注入的IHubContext发送请求,并等待任务完成(添加超时处理避免无限等待):

注意:.NET Core SignalR不再使用GlobalHost.ConnectionManager,需通过依赖注入获取IHubContext<MyHub>

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

public class UserRequestService
{
    private readonly IHubContext<MyHub> _hubContext;

    // 通过依赖注入获取IHubContext
    public UserRequestService(IHubContext<MyHub> hubContext)
    {
        _hubContext = hubContext;
    }

    public async Task<string> GetResponseFromUserAsync(string userId, string request, int timeoutSeconds = 30)
    {
        var requestId = Guid.NewGuid().ToString();
        var taskSource = new TaskCompletionSource<string>();

        // 注册当前请求的任务源
        MyHub.RegisterResponseTask(requestId, taskSource);

        try
        {
            // 向指定用户发送请求
            await _hubContext.Clients.User(userId).SendAsync("Request", requestId, request);

            // 等待响应或超时
            var completedTask = await Task.WhenAny(taskSource.Task, Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)));
            
            if (completedTask == taskSource.Task)
            {
                return await taskSource.Task;
            }
            else
            {
                throw new TimeoutException($"等待用户[{userId}]响应超时,超时时间:{timeoutSeconds}秒");
            }
        }
        finally
        {
            // 清理字典中的任务源,避免内存泄漏
            _responseTasks.TryRemove(requestId, out _);
        }
    }
}

3. 客户端实现请求处理逻辑

C#客户端需要监听服务端的Request事件,处理完成后调用服务端Response方法返回结果:

// 客户端连接Hub后注册Request事件监听
_hubConnection.On<string, string>("Request", async (requestId, request) =>
{
    // 替换为你的业务逻辑,比如弹出对话框获取用户输入
    string userResponse = await ShowUserRequestDialog(request);

    // 调用服务端方法返回响应
    await _hubConnection.SendAsync("Response", requestId, userResponse);
});

关键注意事项

  • 异步优先:SignalR所有操作均为异步,必须使用async/await,避免同步阻塞导致死锁。
  • 线程安全:使用ConcurrentDictionary确保多请求并发时的线程安全。
  • 超时处理:必须添加超时逻辑,防止客户端未响应导致任务长期挂起。
  • 资源清理:在finally块中移除字典中的任务源,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 04:35:20