none
Timeoutを使用せずSocket(NetworkStream)の受信ループから抜ける方法 RRS feed

  • 質問

  • Android JavaとWindowsデスクトップ間のソケット通信を行うプログラムをC#作っています。データはAndroid側から一方向で送信し続ける方式なのですが、送信側の急なエラーで通信が切れても、C#側で例外が発生せず、ReadByte()の箇所で永久に停止してしまいます。こちらのサイトで解決案をいただいたのですが、接続が有効なのにfalse判定が返ってきたり、逆に接続が無効なのにtrueになったりと、完全な解決には至っておりません。

    このプログラムでは受信までの時間がかかることもあるため、Timeoutを使うのはあまり好ましくありません。通信が切れたときは強制的に読み込みループから抜け出せるようにしたいのですが、どのように対処したら良いでしょうか。

    TcpClient tc = tl.AcceptTcpClient();
    NetworkStream s tc.GetStream();
    BinaryReader br = new BinaryReader(s);
    while ((type = br.ReadByte()) != 0) {
        switch (type) {
        ...
        }
    }


    2013年9月8日 1:13

回答

  • 外池と申します。

    Azuleanさんも指摘されているところですが、「原理的」に、実時間の経過で判定するしか方法は無いように思います。

    「通信が切れる」には2種類あって、

    まず、サーバー側なりクライアント側なり、どちらかが意図的に(相手の意向にかかわらず)切る場合があります。TCPであれば、切られた側でも、(不本意であっても)わかりますので、タイムアウトを待たずに通信を終了するようにプログラムできます。

    一方、サーバー・クライアント双方が通信を続けたいのに、通信経路上に障害がある場合があります。TCPは、この障害が回復すれば通信を再開する機能、あるいは、障害を迂回してきたデータを使って通信内容を組み立てなおす機能を有しています。TCPは通信回復の可能性があると待ち続けますので、途中で止める判断基準は「所要時間が我慢できない」という主観的なものしか無い、と思います。

    • 回答としてマーク Tank2005 2013年9月9日 1:32
    2013年9月8日 2:25

すべての返信

  • その永久待ちになったというとき、コマンドプロンプトで「netstat」と打ち込んで表示される接続情報はどうなっていますか?
    ずっと ESTABLISHED なのであれば切断されていないということなので、タイムアウト以外の戦略は存在しない可能性があります。

    (基本的に相手先から切断をきちんと伝えていないケースにおいては、TCP/IP 的には時間経過でしか判断できないはずです。回線切断の場合であっても、どこかの層(アプリではなく下位の層だとお考えください)でその接続が無効になったかを確かめるときには時間の経過が決め手になるため)

    2013年9月8日 2:01
    モデレータ
  • 外池と申します。

    Azuleanさんも指摘されているところですが、「原理的」に、実時間の経過で判定するしか方法は無いように思います。

    「通信が切れる」には2種類あって、

    まず、サーバー側なりクライアント側なり、どちらかが意図的に(相手の意向にかかわらず)切る場合があります。TCPであれば、切られた側でも、(不本意であっても)わかりますので、タイムアウトを待たずに通信を終了するようにプログラムできます。

    一方、サーバー・クライアント双方が通信を続けたいのに、通信経路上に障害がある場合があります。TCPは、この障害が回復すれば通信を再開する機能、あるいは、障害を迂回してきたデータを使って通信内容を組み立てなおす機能を有しています。TCPは通信回復の可能性があると待ち続けますので、途中で止める判断基準は「所要時間が我慢できない」という主観的なものしか無い、と思います。

    • 回答としてマーク Tank2005 2013年9月9日 1:32
    2013年9月8日 2:25
  • 規格上難しいとのことなので、タイムアウトを設定し、定期的に空のデータを送信して通信を維持することでチェックしたいと思います。
    • 編集済み Tank2005 2013年9月9日 1:34
    2013年9月9日 1:33