質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.32%

C# Socketクラスを使用した受信タイムアウトについて

受付中

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 11K+

ryo_se

score 74

お世話になっております。

現在Socket通信を行うプログラムを作成しており、
電文受信時(SocketクラスのReceiveメソッド)にタイムアウト設定を設けたいと思っています。

その場合、SocketクラスのReceiveTimeoutを使えばよいことは調べてわかったのですが、
タイムアウト設定を2秒として使用してみると、
電文受信時から2秒ではなく、電文を受信していない時間が2秒経過した場合に例外処理されてしまうように見受けられます。

電文受信時からの設定を行いたいのですが、方法はございますでしょうか?
(それとも私の何かしらの処理がおかしいのか)

また、タイムアウトで例外扱いになった場合、
再度クライアントから電文を送信すると受信処理が行われません。
その場合、ソケットがクローズされた扱いになっているのでしょうか?
再開方法も教えていただければ幸いです。

・参考サイト
http://note.chiebukuro.yahoo.co.jp/detail/n1656

// 接続待ち開始ボタンのクリックイベント
        private void button1_Click(object sender, EventArgs e)
        {
            if (!SLTAlive)  // まだ接続待ちスレッドを生成していない場合
            {
                // Socket の生成
                ServerSocket = new System.Net.Sockets.Socket(
                    System.Net.Sockets.AddressFamily.InterNetwork,  // IP version 4 のアドレス
                    System.Net.Sockets.SocketType.Stream,   // 通信方式をバイトストリーム
                    System.Net.Sockets.ProtocolType.Tcp);   // プロトコルをTCP
                // ホストのIPアドレスとポート番号の指定
                System.Net.IPEndPoint EndPointHost = new System.Net.IPEndPoint(System.Net.IPAddress.Parse("127.0.0.1"), 9000);
                // *** System.Net.IPEndPoint EndPointHost = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 9000);
                ServerSocket.Bind(EndPointHost);  // ローカル エンドポイント(IPアドレス等の情報)と関連付け
                ServerSocket.Listen(100);         // 電文取り出しの接続がまだ保留中におけるキューの最大長

                // 接続待ち用スレッドを作成
                StartListeningThread = new System.Threading.Thread(StartListening);
                // 接続待ち用スレッドを開始
                StartListeningThread.Start();
                // スレッド終了指示フラグを未終了に設定
                SLTAlive = true;
            }
        }


// 接続待ちスレッド用メソッド
        private void StartListening()
        {
            label1.Text = "サーバー開始";
            try
            {
                // 受信の受付を行なうための無限ループ
                while (SLTAlive)    // スレッド終了指示フラグでの終了指示がある場合はループ終了
                {
                    // クライアントからの接続を受け付ける
                    System.Net.Sockets.Socket ClientSocket = ServerSocket.Accept(); // Socketクライアント

          //*************ここで電文受信時のタイムアウトを2秒間に設定*************
          ClientSocket.ReceiveTimeout = 2000;

                    // クライアントからの電文の受信
                    byte[] ReceiveData = new byte[2000];
                    int ResSize = ClientSocket.Receive(
                            ReceiveData, ReceiveData.Length,
                            System.Net.Sockets.SocketFlags.None);    // 受信
                    string str = System.Text.Encoding.Unicode.GetString(ReceiveData);
                    textBox1.Text = str;    // 受信データ

                    // 返信電文をクライアントへ送信
                    byte[] SendBuffer = Encoding.Unicode.GetBytes("本サーバーの御利用ありがとう御座います。");
                    int i = ClientSocket.Send(SendBuffer);

                    // Socketクライアントをクローズ
                    ClientSocket.Shutdown(System.Net.Sockets.SocketShutdown.Both);
                    ClientSocket.Close();
                }
            }
            catch (Exception ex)
            {
                label1.Text = "サーバー終了";
            }
        }

    }
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

0

電文受信時から2秒ではなく、電文を受信していない時間が2秒経過した場合に例外処理されてしまうように見受けられます。

正しい結果です。このコードは、そのように動きます。
タイムアウトの例外で、StartListening()が終了してしまうので、2回目のReceive()はできません。
ClientSocketはデストラクタが働いてクローズされているかもしれませんし、されていないかもしれません。
ServerSocketはクラス変数になっているのであれば、まだ生きていると思います。

質問者さんの期待するタイムアウトとは、ClientSocket.Receive()でデータが受信できるまでは永遠に待ち、受信が始まってから期待するデータが全部受け取れるまでの時間のことでしょうか?
その場合は、Receive()のやり方を変えなければなりません。
また、データが受信できるまで永遠に待つのはだめで、やはりこののタイムアウトチェックも必要です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/06/13 22:16

    私が想定しているタイムアウトはReceiveメソッドの処理がはじまってから受信するまでの時間です。(つまりは後者です)
    データ自体は数バイトの文字列しか受けない想定なので全部受け取れるかは気にしていません。
    受信のタイムアウトは調べてもReceiveTimeOutしか見つけられず、その方法をご教示していただければ幸いです。

    またタイムアウトした場合は少なくとも再度StartListeningを動かす必要があるのですね。

    キャンセル

  • 2016/06/14 09:07 編集

    数バイトでしたら一回のReceiveで受けられるので気にする必要はありません。
    Receive()からリターンした時には、ReceiveDataにデータが全部入っています。
    大きなサイズのデーターだとtcpのセグメントサイズの単位で分割されるので、Receiveを繰り返す必要がありタイムアウト判定が必要です。コードの受信処理は非同期型に書き換えなければなりません。
    似たようなQAがあったので参考にして下さい。
    http://qa.atmarkit.co.jp/q/280

    タイムアウトしたら、何らかの異常が起きたと判断して、ソケットはクローズしてスレッドを再起動します。

    キャンセル

0

1 byte 受信したところでタイマーを起動し、時間が来たらスレッドを破棄すればいいんじゃないですか?
「私が想定しているタイムアウト」は設計者や私たちの想定とは違います。
想定が普通と違う以上、自分で実装する必要があります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.32%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る