0

接続をリッスンすることから始まるプログラムがあります。私は、サーバーが接続を受け入れ、その個々の接続を処理のためにユーザー クラスに渡すパターンを実装したいと考えていました。将来のパケット受信とデータの処理です。

クラスの非同期使用がSocket怖くないことがわかる前に、同期パターンで問題が発生しました。しかし、その後、さらに問題が発生しました。は非同期であるため、while (true)ループ内でBeginAccept()は、プログラムは常にこのループを移動し、最終的にOutOfMemoryException. 接続をリッスンし、すぐにその接続の責任を他のクラスに引き渡すものが必要でした。

そこで、Microsoft の例を読んで、ManualResetEvent. ループが再びリッスンを開始する準備ができたときを実際に指定できました! しかし、スタック オーバーフローに関するいくつかの質問を読んだ後、私は混乱してしまいました。

私の心配は、接続を非同期に受け入れたとしても、ループに再び入るときに新しい接続をリッスンしようとしている間、プログラム全体がブロックされることです。複数のユーザーを扱っている場合、これは理想的ではありません。

私は非同期 I/O の世界に非常に慣れていないので、私の語彙やフレーズの誤用についての最も怒っているコメントでも感謝します。

コード:

static void Main(string[] args)
{
    MainSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
    MainSocket.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.74"), 1626));
    MainSocket.Listen(10);

    while (true)
    {
        Ready.Reset();
        AcceptCallback = new AsyncCallback(ConnectionAccepted);
        MainSocket.BeginAccept(AcceptCallback, MainSocket);
        Ready.WaitOne();
    }
}

static void ConnectionAccepted(IAsyncResult IAr)
{
    Ready.Set();
    Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
}
4

1 に答える 1

1

古いスタイルのWaitHandleイベントを使用する Microsoft の例は機能しますが、率直に言って、非同期コードを実装するには非常に奇妙で扱いにくい方法です。主にメインスレッドを人為的に同期させる方法として、イベントが例にあるように感じます。しかし、それは本当に正しいアプローチではありません。

1 つのオプションは、ソケットを非同期的に受け入れさえしないことです。代わりに、ソケットが接続されているときに非同期 I/O を使用し、メイン スレッドで同期ループを使用してソケットを受け入れます。これは結局、Microsoft のサンプルが行うこととほとんど同じですが、メイン スレッド (受け入れ操作を開始する) と完了を処理する IOCP スレッドとの間を行き来するのではなく、すべての受け入れロジックをメイン スレッドに保持します。 .

もう 1 つのオプションは、メイン スレッドに別の処理を与えることです。簡単な例として、これは、プログラムをシャットダウンする必要があることを示すユーザー入力を単に待機している可能性があります。もちろん、実際のプログラムでは、メイン スレッドが何か役に立つことがあります (たとえば、GUI プログラムでメッセージ ループを処理するなど)。

メインスレッドに別の処理が与えられている場合は、意図したとおりに非同期を使用できます。BeginAccept()つまり、メソッドを呼び出して受け入れ操作を開始し、その操作が完了するまでメソッドを再度呼び出さないでください。最初の呼び出しはサーバーを初期化するときに発生しますが、その後のすべての呼び出しは完了コールバックで発生します。

その場合、完了コールバック メソッドは次のようになります。

static void ConnectionAccepted(IAsyncResult IAr)
{
    Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
    MainSocket.BeginAccept(ConnectionAccepted, MainSocket);
}

つまりBeginAccept()、完了コールバック自体でメソッドを呼び出すだけです。(オブジェクトを明示的に作成する必要がないことに注意してくださいAsyncCallback。コンパイラは、ユーザーに代わってメソッド名を暗黙的に正しいデリゲート型インスタンスに変換します)。

于 2015-05-16T05:05:27.600 に答える