2

私は現在、トランスポート (長期接続) のために TCP/IP に依存するアプリケーション プロトコル ライブラリを実装しています。

C#5 の async/await コンストラクトを使用して TAP パターンに依存する優れた非同期実装を実現しようとしています。これは主に、これまで理論でしか見たことのない概念を実践するためです。

クライアントは、リモート サーバーに接続して要求を送信できます。クライアントはサーバーからの応答と要求を受け取ります (全二重モード)。

クライアント コードの観点からは、サーバーに要求を送信し、関連する応答を受信するためのライブラリへの非同期呼び出しは、次のように単純です。

var rsp = await session.SendRequestAsync(req);

プロトコル ライブラリ内から、リクエストを作成してバイトに変換し (ネットワーク ストリームで送信する)、ストリームを呼び出しWriteAsync、リクエストを送信する直前に作成された Task を待機します。TaskCompletionSourceこのオブジェクトは、基本的に関連する応答が受信されるのを待っており (そして tcs に結果を設定)、クライアントの呼び出し元に応答を返します。

この部分は問題ないようです。

現在、「問題」は、サーバーがクライアントにリクエストを送信している部分に関係しています。サーバーがクライアントに送信できるさまざまなタイプの要求があります。

私のプロトコル ライブラリは、非同期ループを使用して基になるストリームをリッスンします (受信応答またはサーバーからの要求を受信します)。このループは、ストリーム上で非同期に応答/要求を読み取り、サーバーからの要求の場合は、要求の種類 (ReceivedRequestTypeA など) に対応するイベントを発生させます。クライアント コードは、これらのイベントをサブスクライブして、サーバーから特定の種類の要求を受信したときに通知を受けることができます。これらのイベント引数には、要求に関連付けられたすべてのパラメーターと、クライアントによって設定される応答オブジェクトが含まれます。これらは、イベント ハンドラー コードが完了すると、ライブラリによってストリーム上で非同期に送信されます。

非同期リッスン ループのコードは次のとおりです。while trueあまりきれいではない (代わりにキャンセル パターンを使用する必要があります) を気にしないでください。ただし、これはポイントではありません。

private async Task ListenAsync()
{
  while(true) 
  {
    Request req = await ReadRequestAsync();
    await OnReceivedRequest(req);
  }
}

したがって、ループはReadRequestAsync、完全な要求または応答が利用可能になるまで、ストリーム内のいくつかのバイトを非同期的に読み取るだけの非同期メソッドを呼び出しています。次に、リクエストを非同期メソッドに転送しますOnReceivedRequest。このコードを以下に示します。

private async Task OnReceivedRequest(Request req)
{
   var eventArgs = new ReceivedRequestEventArgs { Req = req };

   if (req is RequestTypeA)
   { ReceivedRequestTypeA(this, eventArgs); }

   [... Other request types ...]

   await SendResponseAsync(eventArgs.Resp);
} 

この非同期メソッドは、適切なリクエスト タイプのイベントを発生させます。クライアント コードはこのイベントにサブスクライブされるため、適切なイベント ハンドラー メソッドが呼び出されます。クライアント コードは、要求に対して必要なことを実行し、応答を作成して EventArgs オブジェクト (イベント ハンドラー メソッドの終わり) に設定します。ライブラリの OnReceivedRequest でコードが再開され、応答が非同期的に送信されます (基になるストリームで WriteAsync を呼び出します)。

クライアント側のイベントハンドラーコードが長いブロック操作を実行している場合、ライブラリの非同期ループを完全にブロックできるため、これは良いアプローチではないと思います(さようなら、完全に非同期のプロトコルライブラリです。クライアントコードのため)。イベントに非同期タスク ベースのデリゲートを使用し、それを待機している場合も同じことが起こります。

GetRequestTypeAAsync()イベントを使用する代わりに、ライブラリ内のオブジェクトを使用して実装される非同期メソッドをTaskCompletionSource作成し、tcs の結果を .xml のリクエストで設定できると考えていましたOnReceivedRequest。クライアント コード側では、ReceivedRequestTypeAイベントをサブスクライブする代わりに、コードはむしろループ arround で構成されますGetRequestTypeAAsync()。それでもクライアントコードはサーバーに送信されるライブラリへの応答を何らかの方法で提供する必要があるため、これがどのように機能するかわかりません...

今、私の脳は完全にぼんやりしていて、明確に考えることができません。素敵なデザインの提案は大歓迎です。

ありがとう !

4

1 に答える 1

3

私はasync/ awaitTCP/ IPソケットにも取り組んでおり、TPLデータフローを確認することを強くお勧めします。2つのs(1つは読み取り用、もう1つは書き込み用)を使用して、使いやすいasyncエンドポイントを作成するのは非常に簡単です。BufferBlock

私のシステムでは、TCP/IPソケットラッパーISourceBlock<ArraySegment<byte>>は生の読み取りを表す単純なものを公開します。次に、これはメッセージフレーミングTransformManyBlockを実行するにリンクされ、そこからバイトを実際のメッセージインスタンスに解析するにリンクできます。TransformBlock

RequestTypeこのアプローチは、他のすべてのメッセージタイプが継承する基本クラスがある場合に最適に機能します。RequestType次に、データフローパイプラインの最後からメッセージインスタンスを(非同期に)受信する単一の受信タスクを持つことができます。

于 2012-08-16T00:37:59.173 に答える