none
TcpListenerの開始または終了方法について RRS feed

  • 質問

  • はじめまして、katsuoと申します。
    業務でTcpListenerを用いたWindowsサービスを使用しているのですが、不定期にエラーが発生しているため、質問させて下さい。

    Windowsサービスは20個のスレッドを作成し以下のように動作します。
    1、 スレッドを開始します。(スレッドはTimerを用いてループ処理を行います)
    2、 それぞれ別のポートを割当てTcpListenerを生成します。
    3、 2で生成したTcpListenerのStart()を実行します。
    4、 2で生成したTcpListenerのAcceptTcpClient()を実行しTcpClientを生成します。
    5、 4で生成したTcpClientのGetStream()を実行しNetworkStreamを生成します。
    6、 2で生成したTcpListenerのStop()を実行します。
    7、 5で生成したNetworkStreamを用いて業務処理を行います。
    8、 4で生成したTcpClientのClient.Disconnect(False)を実行します。
    9、 4で生成したTcpClientのClose()を実行します。
    10、 4で生成したTcpClientのDispose()を実行します。
    11、 5で生成したNetworkStreamのDispose()を実行します。
    12、 4で生成したTcpClientと5で生成したNetworkStreamにNothingを設定します。
    13、 2に戻ります。


    このとき、基本的には正常に動作しているのですが、あるタイミングに3を実行した際にエラーが発生します。
    エラーの内容は以下です。

    通常、各ソケット アドレスに対してプロトコル、ネットワーク アドレス、またはポートのどれか 1 つのみを使用できます。
    System.Net.Sockets.SocketException (0x80004005): 通常、各ソケット アドレスに対してプロトコル、ネットワーク アドレス、またはポートのどれか 1 つのみを使用できます。
       場所 System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
       場所 System.Net.Sockets.Socket.Bind(EndPoint localEP)
       場所 System.Net.Sockets.TcpListener.Start(Int32 backlog)


    エラーの内容は既にポートが使用されているため使用できないというものです。
    6でStop()を実行しているため、止まっていると認識しているのですが、間違いでしょうか?
    発生する頻度も不定期のため、どのように対応すればよいか困っています。
    タイトルにあるように、開始方法または停止方法に問題があるのではないかと考えています。
    何かご意見があれば教えて頂けないでしょうか?
    よろしくお願い致します。

    2015年4月6日 2:57

回答

  • おそらく4でacceptしたコネクションが、8でこちら主導で切断したときに、TIME_WAIT状態になって残るのでしょう
    (クライアントのFINが先に到着すれば、残らないんですが)

    対策としては、ListenerSocketにSO_REUSEADDRを立てるか、6をやめて13から4で戻る(要はListenerSocketを開きっぱなしにする)ようにするか、切断にSO_LINGER 使うなどの手があります。
    個人的には、ListenerSocketが開いたり閉まったりするのは、あんまり動作として好きではないですね。


    jzkey

    • 回答としてマーク katsuo0829 2015年4月6日 8:04
    2015年4月6日 3:30

すべての返信

  • おそらく4でacceptしたコネクションが、8でこちら主導で切断したときに、TIME_WAIT状態になって残るのでしょう
    (クライアントのFINが先に到着すれば、残らないんですが)

    対策としては、ListenerSocketにSO_REUSEADDRを立てるか、6をやめて13から4で戻る(要はListenerSocketを開きっぱなしにする)ようにするか、切断にSO_LINGER 使うなどの手があります。
    個人的には、ListenerSocketが開いたり閉まったりするのは、あんまり動作として好きではないですね。


    jzkey

    • 回答としてマーク katsuo0829 2015年4月6日 8:04
    2015年4月6日 3:30
  • jzkeyさん

    早速の返信ありがとうございます。

    >おそらく4でacceptしたコネクションが、8でこちら主導で切断したときに、TIME_WAIT状態になって残るのでしょう
    >(クライアントのFINが先に到着すれば、残らないんですが)
    TIME_WAITの可能性も考えて、netstat -an を使用して監視してみましたが、LISTENが残っているように見えました。

    >対策としては、ListenerSocketにSO_REUSEADDRを立てるか、6をやめて13から4で戻る(要はListenerSocketを開きっぱなしにする)ようにするか、切断にSO_LINGER 使うなどの手があります。
    > 個人的には、ListenerSocketが開いたり閉まったりするのは、あんまり動作として好きではないですね。
    SO_REUSEADDRやSO_LINGERというのは使用したことがないですね。
    検索キーワードにして後学のために調べてみます。

    TcpListenerを開きっぱなしにするについては、試してみようと思います。
    ただ気になるのは、TcpListenerの受信保留キューにはどの程度貯められるのでしょうか?
    実際は受信速度よりも処理速度の方が速いため気にする必要はないのですが、ご存知でしたらご教授下さい。
    自分で調べたところ、TcpListener.Start()の引数に指定しない限りは、無制限の様に感じました。

    以上です、よろしくお願い致します。

    2015年4月6日 6:22