【问题标题】:Bi-directional communication with ASPNET Core and websockets与 ASPNET Core 和 websockets 的双向通信
【发布时间】:2017-02-17 19:40:58
【问题描述】:

我没有想法,如何实现。这是问题所在,我已经尝试过。

问题

有一个消息队列,其中包含来自外部系统的消息。客户端必须打开与服务器的 websocket 连接,并在插入这些消息后立即从队列中接收这些消息。除此之外,客户端可以通过相同的 websocket 连接向服务器发送命令(客户端和服务器之间的通信必须具有尽可能低的延迟)。

工具

我更喜欢使用最低级别但仍然实用的官方库,因此选择Microsoft.AspNetCore.WebSockets (1.0.0)。该应用程序作为启用了 websockets (WSS) 的 Web 应用程序在 Azure 应用程序服务上运行。该应用程序是netcoreapp1.1。不需要 SSE/长轮询/等后备 (SignalR)。

解决方案 #1 - 已测试

服务器应该运行一个循环到:

  1. 如果消息队列不为空,则发送任何更新。
  2. 等待传入消息超时。

可以这样实现:

while (_request_not_aborted_) // The condition is simplified.
{
    if (_there_is_a_new_message_in_message_queue_)
    {
        // Send all available messages from the message queue.
        await SendAsync();
    }

    using (var timeoutCancellationTokenSource = new CancellationTokenSource())
    {
        var timeoutCancellationToken = timeoutCancellationTokenSource.Token;
        var receiveTask = ReceiveAsync(timeoutCancellationToken, webSocket);

        // Wait for any incoming message for 1000ms and cancel the task if
        // the client hasn't sent any yet.
        if (await Task.WhenAny(receiveTask, Task.Delay(1000, timeoutCancellationToken)) ==
            receiveTask)
        {
            var result = await receiveTask;
            // Process the message.
        }

        timeoutCancellationTokenSource.Cancel();
    }
}

解决方案 #1 - 出了什么问题?

一旦timeoutCancellationTokenSource被取消,websocket连接进入“Aborted”状态,客户端断开。

可能相关问题:https://github.com/aspnet/WebSockets/issues/68

解决方案 #2 - 已测试

使用 SSE 将数据从服务器流式传输到客户端,并使用常规 HTTPS 请求发回消息。

解决方案 #2 - 出了什么问题?

HTTPS 请求延迟不可接受,IE/Edge 尚不支持 SSE。

解决方案 #3

使用多个 websocket 连接,一个用于服务器->客户端,另一个用于客户端->服务器通信。

解决方案 #3 - 出了什么问题?

但这会增加断开连接出错的机会,而且总的来说,这听起来很糟糕,因为 websocket 技术应该提供双向通信。

解决方案 #4

使用解决方案 #2 并删除超时逻辑,但每 1000 毫秒从客户端向服务器发送 NOOP 消息,以允许 ReceiveAsync 正常完成。

解决方案 #4 - 出了什么问题?

听起来像一个讨厌的解决方法。

请求帮助/想法

也许知道任何其他解决方案,服务器如何相对同时侦听传入消息并向客户端发送消息?

其他发现

  1. “每个 WebSocket 对象并行支持一个发送和一个接收。” MSDN

【问题讨论】:

  • 消息队列用什么?
  • Azure 云存储

标签: c# asp.net websocket .net-core kestrel-http-server


【解决方案1】:

写下问题有助于从另一个角度看待问题,所以我用解决方案#5来回答我自己的问题。我还没有发现任何问题。

“每个 WebSocket 对象并行支持一个发送和一个接收。” 引起了我的注意,我决定同时运行发送和接收任务,并继续重新启动已完成的任务。

循环应该是:

var sendTask = SendAsync(cancellationToken, webSocket);
var receiveTask = ReceiveAsync(cancellationToken, webSocket);

while (!cancellationToken.IsCancellationRequested)
{
    if (await Task.WhenAny(sendTask, receiveTask) == sendTask)
    {
        // The server has finished sending to the client or it had nothing to send.
        await sendTask;
        sendTask = SendAsync(cancellationToken, webSocket);

        continue;
    }

    var message = await receiveTask;
    // TODO: Process message here
    receiveTask = ReceiveAsync(cancellationToken, webSocket);
}

await webSocket.CloseAsync(
    WebSocketCloseStatus.NormalClosure,
    string.Empty,
    cancellationToken);

并且 SendAsync() 方法应该包含:

while(_there_is_a_messages_in_queue_)
{     
    var data = "message"; // Simplified. Should be taken from the message queue.

    await webSocket.SendAsync(
        new System.ArraySegment<byte>(System.Text.Encoding.UTF8.GetBytes(data)),
        WebSocketMessageType.Text,
        true,
        cancellationToken);
}

await Task.Delay(1000);

有一个延迟任务允许:

  1. ReceiveAsync() 方法在客户端发送任何数据时首先完成。
  2. 通过不如此激进地轮询消息队列来节省 CPU 和带宽。

我确信我可能留下了一些未发现的边缘情况,所以如果您将来会阅读这篇文章,请告诉我您是否会发现此解决方案有任何明显的问题。

附:所有 ConfigureAwait(false) 已被删除,以提高可读性。

附言我不接受我自己的回答,以允许您添加有关任何可能解决方案的任何有用信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-24
    • 1970-01-01
    • 2020-06-29
    • 1970-01-01
    • 2022-01-22
    • 2012-09-26
    • 1970-01-01
    相关资源
    最近更新 更多