2017-02-17 36 views
1

我出出主意,如何可以實現。這是問題,以及我已經嘗試過的。與ASPNET核心雙向通信和WebSockets的

問題

有一個消息隊列與來自外部系統來的消息。客戶端必須打開與服務器的websocket連接,並在插入時立即從隊列接收這些消息。除此之外,客戶端可以在同一WebSocket連接(在客戶端和服務器之間的通信必須具有儘可能低的等待時間儘可能)發送命令到服務器。

工具

我更喜歡使用的最低水平,但仍然實用,官方庫,這樣的選擇是Microsoft.AspNetCore.WebSockets(1.0.0)。該應用程序作爲啓用了Websockets(WSS)的Web應用程序在Azure App Service上運行。該應用程序是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連接進入「中止」狀態,客戶端斷開連接。

可能相關的問題:https://github.com/aspnet/WebSockets/issues/68

解決方案#2 - 已經測試

使用SSE從服務器流式傳輸數據到客戶端,並使用常規的HTTPS請求,將消息發送回。

解決方案#2 - 有什麼不對?

的HTTPS請求的等待時間是不可接受的,並且未在IE /邊緣尚不支持SSE。

溶液#3

使用多個WebSocket連接,一個用於服務器 - >客戶端,另一個用於客戶端 - >服務器的通信。

解決方案#3 - 有什麼不對?

但是,這會增加斷開連接錯誤的機會,並且一般來說聲音不好,因爲websocket技術應該提供雙向通信。

溶液#4

使用溶液#2的超時邏輯移除,但是從客戶機到服務器的每1000毫秒,以允許ReceiveAsync正常完成發送NOOP消息。

解決方案#4 - 有什麼問題?

聽起來像一個討厭的解決方法。

請求幫助/想法

也許知道任何其他解決方案的服務器可以如何監聽傳入的消息,並在相對同時將消息發送到客戶端?

附加發現

  1. 「恰好一個發送和一個接收被支撐在平行各WebSocket的對象上。」 MSDN
+0

你在使用什麼消息隊列? – jao

+0

Azure雲存儲 – Zygimantas

回答

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和帶寬。

我相信我可能已經發現了一些邊緣案例,因此如果您將來要閱讀這篇文章,請告訴我您是否會看到此解決方案的任何明顯問題。

P.S.所有ConfigureAwait(false)已被刪除,以提高可讀性。

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