是否有具有“向服务器返回值”功能的 SignalR 替代方案?

Posted

技术标签:

【中文标题】是否有具有“向服务器返回值”功能的 SignalR 替代方案?【英文标题】:Is there SignalR alternative with "return value to server" functionality? 【发布时间】:2020-04-14 10:07:01 【问题描述】:

我的目标:将数据传递给特定连接到服务器的客户端,并在不调用服务器的情况下获得结果方法。

我尝试使用 SignalR 来执行此操作(因为它对我来说是非常简单的工具),但我无法获得结果(现在我知道为什么了)。我正在开发 ASP.NET Core 3.1。

我的问题:是否有具有“向服务器返回值”功能的 SignalR 替代方案(在目标客户端上调用带有参数的方法并获取结果)?

【问题讨论】:

当然这很容易存档。您只需使用您的数据调用客户端方法,然后执行您想要的操作,最后调用服务器方法来传递您的响应。您的问题到底是什么?如何实现? 你的意思是服务器向特定客户端发起请求(什么?),客户端应该返回结果? 你需要调用一个客户端(从服务器向客户端发送数据)然后在前端做一些事情并返回结果吧? 我无法运行其他方法,因为我通过 Web API 在特定客户端调用方法(我的视图仅使用 REST Api) 它是否适用于 Core 2.0? 【参考方案1】:

SignalR 通常用于有多个客户端和客户端连接到的单个服务器的设置。这使得客户端调用服务器并期望返回结果是一件正常的事情。由于服务器通常并不真正关心连接了哪些单个客户端,并且由于服务器通常向一组客户端广播(例如使用组),因此通信方向主要用于通知或广播.单目标消息是可能的,但没有用于请求/响应模式的内置机制。

为了与 SignalR 一起工作,您需要有一种方法让客户端回调服务器。因此,您将需要一个集线器操作来将响应发送到。

仅此一点并不难,但您可能需要将客户端呼叫与集线器接收的传入结果消息链接起来。为此,您必须构建一些东西。

这里有一个示例实现,可以帮助您入门。 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);
    

在hub中,你需要回调动作来调用请求客户端来完成任务:

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');

当然,这个实现是非常基础的,需要一些东西,比如正确的错误处理和支持超时任务,如果客户端没有正确响应。也可以扩展它以支持任意调用,而无需您手动创建所有这些方法(例如,通过在集线器上拥有一个能够完成任何任务的回调方法)。

【讨论】:

很好的答案,但 OP 专门要求使用 SignalR 替代方案

以上是关于是否有具有“向服务器返回值”功能的 SignalR 替代方案?的主要内容,如果未能解决你的问题,请参考以下文章

是否有明确的 SignalR 集线器版本控制策略,以便旧的 JS 代码可以继续工作?

SignalR Redis 背板组同步

SignalR 具有 MVC IIS 客户端模拟当前 Windows 用户

将 SignalR 用于桌面应用程序是不是正确?

是否可以从 Postman 调用 SignalR Hub

如何测试托管在Azure Appservice上的SignalR应用(负载+功能结合)?