2017-11-30 21 views
0

私は、.NETのHttpListenerクラスを使用してwebsocketサーバーを作成しています。async-awaitとfire-and-forgetのアプローチを組み合わせる

本質的には、クライアントが接続して各クライアントをHandleClient(WebSocket client)にするのを待つHandleListener()機能があります。だから、私は現在持っている:

private async void HandleListener() 
    { 
     try 
     { 
      while (listener != null && listener.IsListening) 
      { 
       HttpListenerContext listenerContext = await listener.GetContextAsync(); 
       WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null); 
       WebSocket webSocket = webSocketContext.WebSocket; 
       clients.Add(webSocket); 
       await HandleClient(webSocket); 
      } 
     } 
     catch (HttpListenerException) { } // Got here probably because StopWSServer() was called 
    } 

private async Task HandleClient(WebSocket client) { ... } 

問題は、私は複数のクライアントを処理することはできません。最初のクライアントが接続されている限り、HandleListener()の実行が停止しているようです。

を電話番号HandleClient()に転送しようとしましたが、「この電話は待たれていないため」というエラーが表示されます。私はHandleClient()async voidメソッドにすることができますが、これはイベントハンドラではありません。それはループ内ですべての上に、やっているので、リスナーが死んでいるまで
はところで、HandleClient()async Taskであることの理由は次のとおりです。

recieveResult = await client.ReceiveAsync(recievedBuffer, CancellationToken.None); 

私が理解から、ファイア・アンド・フォーゲットアプローチは、全体的に悪く、非同期の実装では実現できないようです。しかし、HandleClient()であり、私は必要なものを達成する他の方法は見当たりません。


EDIT:HandleClient()の現在の実装を追加しました:

private async Task HandleClient(WebSocket client) 
    { 
     try 
     { 
      ArraySegment<byte> recievedBuffer = new ArraySegment<byte>(new byte[BUFFER_SIZE]); 
      while (listener != null && listener.IsListening && client.State == WebSocketState.Open) 
      { 
       WebSocketReceiveResult recieveResult; 
       using (var ms = new MemoryStream()) 
       { 
        do 
        { 
         recieveResult = await client.ReceiveAsync(recievedBuffer, CancellationToken.None); 
         ms.Write(recievedBuffer.Array, recievedBuffer.Offset, recieveResult.Count); 
        } 
        while (!recieveResult.EndOfMessage); 
        switch (recieveResult.MessageType) 
        { 
         case WebSocketMessageType.Close: 
          RemoveClient(client, WebSocketCloseStatus.NormalClosure, string.Empty); 
          break; 
         case WebSocketMessageType.Binary: 
          RemoveClient(client, WebSocketCloseStatus.InvalidMessageType, "Cannot accept binary frame"); 
          break; 
         case WebSocketMessageType.Text: 
          OnRecieve?.Invoke(client, System.Text.Encoding.UTF8.GetString(ms.ToArray())); 
          break; 
        } 
       } 
      } 
     } 
     catch (WebSocketException ex) 
     { 
      RemoveClient(client, WebSocketCloseStatus.InternalServerError, ex.Message); 
     } 
    } 
+0

「HandleClient」とは何ですか?あなたはそれが火であり、忘れていると言います。しかし、なぜ非同期ですか? – Sefe

+0

これは '... await client.ReceiveAsync ... 'をループ内で行うため、非同期です。私はこれを含めるために質問を編集しました。 – galah92

+0

しかしそれは火ではなく、忘れている。受信が完了するのを待っているときは、あなたは忘れていません。問題はあなたのコードの構造にあるようです。 'HandleClient'からの1行は、それを解決するのに十分ではありません。 – Sefe

答えて

1

あなたは以下のように方法を書いたと言って、それのための私の平均値を、コードを書いたので、それはそのように行います。この方法では

private async void HandleListener() 
    { 
     try 
     { 
      while (listener != null && listener.IsListening) 
      { 
       HttpListenerContext listenerContext = await listener.GetContextAsync(); 
       WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null); 
       WebSocket webSocket = webSocketContext.WebSocket; 
       clients.Add(webSocket); 
       await HandleClient(webSocket); 
      } 
     } 
     catch (HttpListenerException) { } // Got here probably because StopWSServer() was called 
    } 

それはawait制御が発生したときに一部が、それが完了した後、次の通話開始しまっ待つまで、発信者のorignalに戻り取得します。

チェック画像の下

、これはどのように待っていて、あなただけの火をたいとタスク

の完了を待ついけないことを意味する。この

private void HandleListener() 
    { 
     try 
     { 
      while (listener != null && listener.IsListening) 
      { 
       HttpListenerContext listenerContext = await listener.GetContextAsync(); 
       WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null); 
       WebSocket webSocket = webSocketContext.WebSocket; 
       clients.Add(webSocket); 
       HandleClient(webSocket); 
      } 
     } 
     catch (HttpListenerException) { } // Got here probably because StopWSServer() was called 
    } 

のようにしてみてくださいよりも、忘れてしまった場合は、非同期は

enter image description here

の作品

+0

これは非常に危険なアプローチです。あなたは 'HandleClient'が' async'それ自体とそれが何であるか分かりません。 – Sefe

+0

実際には、 'HandleClient'は' async'です。 – galah92

+0

@Sefe - 知っていますが、彼は火を欲しがって、なぜコードが –

0

コンパイラの警告を防止するには、次のようなメソッドを使用します。

public static class TaskExtensions { 
    public static void Forget(this Task task) { 

    } 
} 

は、ちょうどあなたがこのルートを行けば

HandleClient(webSocket).Forget() 

は、あなたが(例えばのtry-catchに全体を包む)何とかHandleClient内のすべての例外を処理していることを確認ください。この特定のケースでは、このアプローチでは本質的に「悪い」ものは何もありません。

別のアプローチは、次のようになります。HandleClientを待っ

HandleClient(webSocket).ContinueWith(task => { 
    if (task.IsFaulted && task.Exception != null) { 
     // handle it here 
    } 
}); 

あなた自身を見るように、この場合はオプションではありません。

関連する問題