2013年2月18日月曜日

[.NET] TcpListenerのよさげな使い方

小ネタですが、TcpListenerのおそらくよさげな使い方についてです。ポイントは、TcpListenerはAccept要求をキューに溜め込む仕様のため、Acceptの処理をすばやく実行しないと、新しい要求が失敗する可能性があるということです。

とりあえずコード(C#)

// TcpListenerの使用例
// クライアントとのデータのやり取りはここでやる(Thread)
void ConnectionProc(object target)
{
    TcpClient tcpClient = (TcpClient)target;
    // ここでtcpClientからストリームを取得してReadとかWriteとかCloseとかを実行する
    // ちなみにクライアント側から接続が切断されると、Readでサイズ0が返ってくる
}
// 接続を受け入れる。
void AcceptConnection(TcpListener tcpListener)
{
    TcpClient tcpClient = tcpListener.AcceptTcpClient();
    Thread thread = new Thread(ConnectionProc);
    thread.IsBackground = true;
    thread.Start(tcpClient);
}
// Acceptを受け入れる処理(Thread)
void AcceptProc(object target)
{
    TcpListener tcpListener = (TcpListener)target;
    while (true)
    {
        try
        {
            while (tcpListener.Pending())
            {
                AcceptConnection(tcpListener);
            }
            AcceptConnection(tcpListener);
        }
        catch (Exception)
        {
            // TcpListenter.Stop()が呼び出されるとSocketExceptionがThrowされる
            break;
        }
    }
}

//  Acceptを開始する処理
// tcpListenerはどこかで作成されているとする
tcpListener.Start();
Thread thread = new Thread(AcceptProc);
thread.IsBackground = true;
thread.Start(tcpListener);

正直言ってwhile (tcpListener.Pending()){…}のコードはロジック的にはなくても同じに思えるのですが、どうやらこのように書いておくと、Acceptを処理しているときに直ぐに別な接続が来た場合、早く応答するようです。
もし、TcpListenerを使っていて、クライアントからの接続に失敗するようだったら、このような方法を試してみるのがいいかもしれません。