.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
相关产品推荐
相关产品推荐

