与ASPNET Core和websockets进行双向通信

时间:2017-02-17 19:40:58

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

我没有想法,如何实施。这是问题,我已经尝试过了。

问题

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

工具

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

解决方案#1 - 已经过测试

服务器应该运行循环:

  1. 如果邮件队列不为空,则发送任何更新。
  2. 等待传入的消息超时。
  3. 可以这样实现:

    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连接,一个用于server->客户端,另一个用于客户端>服务器通信。

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

    但是这会增加连接断开错误的可能性,一般来说听起来很糟糕,因为websocket技术应该提供双向通信。

    解决方案#4

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

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

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

    请求帮助/想法

    也许知道服务器如何监听收到的消息并在相对同时向客户端发送消息的任何其他解决方案?

    其他调查结果

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

1 个答案:

答案 0 :(得分:2)

写下问题有助于从另一个角度看事情,所以我用解决方案#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和带宽。
  3. 我相信我可能会留下一些边缘案例,所以如果您将来会阅读这篇文章,请告诉我您是否会看到此解决方案存在任何明显问题。

    P.S。已删除所有ConfigureAwait(false)以提高可读性。

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