是否有具有 "return value to server" 功能的 SignalR 替代品?
Is there SignalR alternative with "return value to server" functionality?
我的目标: 将数据传递给特定 连接到服务器 的客户端并得到结果 没有调用服务器方法。
我尝试使用 SignalR 来执行此操作(因为它对我来说是非常简单的工具),但我无法获得结果(现在我知道为什么了)。我正在研究 ASP.NET 核心 3.1。
我的问题: 是否有具有 "return value to server" 功能的 SignalR 替代方案(在目标客户端上调用带有参数的方法并获得结果)?
SignalR 通常用于有多个客户端和客户端连接的单个服务器的设置中。这使得客户端调用服务器并期望返回结果成为一件很正常的事情。由于服务器通常并不真正关心 个人 客户端连接的是什么,并且由于服务器通常向一组客户端广播(例如使用组),因此通信方向主要用于通知或广播。单目标消息是可能的,但没有用于 request/response 模式的内置机制。
为了使它与 SignalR 一起工作,您将需要有一种方法让客户端回调服务器。因此,您将需要一个集线器操作来将响应发送到。
仅此一点并不困难,但可能需要 link 一个客户端调用,其中包含集线器收到的传入结果消息。为此,您必须构建一些东西。
这是一个让您入门的示例实现。 MyRequestClient
是一个单例服务,它基本上封装了消息传递,并为您提供了一个异步方法,该方法将调用客户端,并且仅在客户端通过调用集线器上的回调方法响应后才完成:
public class MyRequestClient
{
private readonly IHubContext<MyHub> _hubContext;
private ConcurrentDictionary<Guid, object> _pendingTasks = new ConcurrentDictionary<Guid, object>();
public MyRequestClient(IHubContext<MyHub> hubContext)
{
_hubContext = hubContext;
}
public async Task<int> Square(string connectionId, int number)
{
var requestId = Guid.NewGuid();
var source = new TaskCompletionSource<int>();
_pendingTasks[requestId] = source;
await _hubContext.Clients.Client(connectionId).SendAsync("Square", nameof(MyHub.SquareCallback), requestId, number);
return await source.Task;
}
public void SquareCallback(Guid requestId, int result)
{
if (_pendingTasks.TryRemove(requestId, out var obj) && obj is TaskCompletionSource<int> source)
source.SetResult(result);
}
}
在集线器中,您需要回调操作来调用请求客户端以完成任务:
public class MyHub : Hub
{
private readonly ILogger<MyHub> _logger;
private readonly MyRequestClient _requestClient;
public MyHub(ILogger<MyHub> logger, MyRequestClient requestClient)
{
_logger = logger;
_requestClient = requestClient;
}
public Task SquareCallback(Guid requestId, int number)
{
_requestClient.SquareCallback(requestId, number);
return Task.CompletedTask;
}
// just for demo purposes
public Task Start()
{
var connectionId = Context.ConnectionId;
_ = Task.Run(async () =>
{
var number = 42;
_logger.LogInformation("Starting Square: {Number}", number);
var result = await _requestClient.Square(connectionId, number);
_logger.LogInformation("Square returned: {Result}", result);
});
return Task.CompletedTask;
}
}
Start
集线器操作仅用于演示目的,以便能够使用有效的连接 ID 启动它。
在客户端,需要实现客户端方法,完成后调用指定的回调方法:
connection.on('Square', (callbackMethod, requestId, number) => {
const result = number * number;
connection.invoke(callbackMethod, requestId, result);
});
最后,您可以通过客户端调用 Start
操作来尝试:
connection.invoke('Start');
当然,这个实现是非常基础的,需要一些东西,比如正确的错误处理和支持在客户端没有正确响应时超时任务。也可以扩展它以支持任意调用,而无需您手动创建所有这些方法(例如,通过在集线器上使用一个能够完成任何任务的回调方法)。
我的目标: 将数据传递给特定 连接到服务器 的客户端并得到结果 没有调用服务器方法。
我尝试使用 SignalR 来执行此操作(因为它对我来说是非常简单的工具),但我无法获得结果(现在我知道为什么了)。我正在研究 ASP.NET 核心 3.1。
我的问题: 是否有具有 "return value to server" 功能的 SignalR 替代方案(在目标客户端上调用带有参数的方法并获得结果)?
SignalR 通常用于有多个客户端和客户端连接的单个服务器的设置中。这使得客户端调用服务器并期望返回结果成为一件很正常的事情。由于服务器通常并不真正关心 个人 客户端连接的是什么,并且由于服务器通常向一组客户端广播(例如使用组),因此通信方向主要用于通知或广播。单目标消息是可能的,但没有用于 request/response 模式的内置机制。
为了使它与 SignalR 一起工作,您将需要有一种方法让客户端回调服务器。因此,您将需要一个集线器操作来将响应发送到。
仅此一点并不困难,但可能需要 link 一个客户端调用,其中包含集线器收到的传入结果消息。为此,您必须构建一些东西。
这是一个让您入门的示例实现。 MyRequestClient
是一个单例服务,它基本上封装了消息传递,并为您提供了一个异步方法,该方法将调用客户端,并且仅在客户端通过调用集线器上的回调方法响应后才完成:
public class MyRequestClient
{
private readonly IHubContext<MyHub> _hubContext;
private ConcurrentDictionary<Guid, object> _pendingTasks = new ConcurrentDictionary<Guid, object>();
public MyRequestClient(IHubContext<MyHub> hubContext)
{
_hubContext = hubContext;
}
public async Task<int> Square(string connectionId, int number)
{
var requestId = Guid.NewGuid();
var source = new TaskCompletionSource<int>();
_pendingTasks[requestId] = source;
await _hubContext.Clients.Client(connectionId).SendAsync("Square", nameof(MyHub.SquareCallback), requestId, number);
return await source.Task;
}
public void SquareCallback(Guid requestId, int result)
{
if (_pendingTasks.TryRemove(requestId, out var obj) && obj is TaskCompletionSource<int> source)
source.SetResult(result);
}
}
在集线器中,您需要回调操作来调用请求客户端以完成任务:
public class MyHub : Hub
{
private readonly ILogger<MyHub> _logger;
private readonly MyRequestClient _requestClient;
public MyHub(ILogger<MyHub> logger, MyRequestClient requestClient)
{
_logger = logger;
_requestClient = requestClient;
}
public Task SquareCallback(Guid requestId, int number)
{
_requestClient.SquareCallback(requestId, number);
return Task.CompletedTask;
}
// just for demo purposes
public Task Start()
{
var connectionId = Context.ConnectionId;
_ = Task.Run(async () =>
{
var number = 42;
_logger.LogInformation("Starting Square: {Number}", number);
var result = await _requestClient.Square(connectionId, number);
_logger.LogInformation("Square returned: {Result}", result);
});
return Task.CompletedTask;
}
}
Start
集线器操作仅用于演示目的,以便能够使用有效的连接 ID 启动它。
在客户端,需要实现客户端方法,完成后调用指定的回调方法:
connection.on('Square', (callbackMethod, requestId, number) => {
const result = number * number;
connection.invoke(callbackMethod, requestId, result);
});
最后,您可以通过客户端调用 Start
操作来尝试:
connection.invoke('Start');
当然,这个实现是非常基础的,需要一些东西,比如正确的错误处理和支持在客户端没有正确响应时超时任务。也可以扩展它以支持任意调用,而无需您手动创建所有这些方法(例如,通过在集线器上使用一个能够完成任何任务的回调方法)。