読者です 読者をやめる 読者になる 読者になる

TcpClient などで非同期通信を行うときの諸注意らしきもの

BeginAcceptTcpClient メソッドを呼び出してから、EndAcceptTcpClient メソッドを
呼ぶまでの間に IAsyncResult インスタンスの AsyncWaitHandle プロパティが RAISE*1 しているかどうかを
調べる必要があります。

AsyncWaitHandle プロパティが RAISE 状態になつてゐても、クライアントからの受信接続要求
が来てゐるわけではありません。
タイムアウトで RAISE 状態になつてゐる可能性は十分にあります。
また、複数のクライアントからの受信接続要求に答えるときはどうすれば良いのでせう?
ひとつづつ片付けていきませう。

非同期操作が完了したが受信接続要求が来てゐなかつた場合

前述したやうに非同期操作の終了イコオル受信接続要求が来たということではありません。
仮にさうなつてゐたとしたら、AsyncWaitHandle プロパティがいつまで経つても RAISE 状態にはならず、
ずっと待ち続けることになるかもしれません。
IAsyncResult インスタンスを使つた非同期操作がこのやうになつてゐるのは安全のためなのです*2
ということで、通信が成功したかどうかを確実に知る方法が必要です。
それにはとりあえず、ManualResetEvent クラスを遣います。
この ManualResetEvent オブジェクトはえーっとその、非同期書き込みが行われた時に RAISE 状態になる、ということにしておきませう。

ManualResetEvent accept_completion = new ManualResetEvent( false );

次に、BeginAcceptTcpClient メソッドを呼びます。

IAsyncResult acceptance = listener.BeginAcceptTcpClient( OnAcceptCompleted,
                                                         accept_completion );

BeginAcceptTcpClient メソッドの第二引数(state) に accept_completion を渡します。
コールバックの OnAcceptCompleted メソッドはこんな感じになっています:

void OnAcceptCompleted(IAsyncResult ar) {
    ManualResetEvent accept_completion = ar.AyncState as ManualResetEvent;

    if ( ar.IsCompleted && ar.CompletedSynchronously )
        accept_completion.Set();
}

「完了」と「同期的に完了」の違いが分かりませんがどちらも真なら accept_completion を RAISE 状態にします。
たまに IsCompleted プロパティが真になっているときだけでも読み込みや書き込みに成功していたりするときがあるようです。
gdgdな感じでした。

*1:シグナル状態とも

*2:多分…