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

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

ただいまの
回答率

90.52%

  • .NET Framework

    456questions

    .NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

System.Net.Sockets.Socket.Poll メソッドの待機挙動について

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,745

hsk

score 690

お世話になっております。
.NET Framework 4.5.2 を使用してTCP通信クライアントアプリケーションを作成しています。

質問

System.Net.Sockets.Socket.Poll メソッドの挙動について質問があります。

このメソッドは winsock.dll の select関数のような挙動をし、第一引数には「待機時間」を指定する(負の値の場合は無制限に待機)よう MSDNライブラリのヘルプに記載されているよう解釈しています。受信(または接続終了)待ちに利用してみているのですが、「待機時間」の値を負の値(永久待ち)にすると常にtrueが返され受信できる環境において「待機時間」に正の数を指定すると、受信状態がどうであれまったく(1秒ですら)待機しません。
このメソッドの「待機時間」とは、一体何なのでしょうか?

コード

次のようなコードを記述してみています。

MyProgram.Start メソッドから起動させ、TCP 接続後 Poll メソッドで100秒のタイムアウトを指定して受信または接続終了の待機をさせるつもりです。サーバ側は、Accept後すぐに数十バイトのパケットを送信して受信待ちとなるものです。

下記コードの Poll メソッドから true が返され whileループ内のReceived メソッドで受信できる場合が多いのですが、たまにPollメソッドが(100秒どころか1秒もせずに)falseですぐに復帰してしまう場合があります。そのとき、val2 には 0ではない値(受信したであろうパケットの長さ)が入っていたりいなかったりし、0 でない場合はReceiveメソッドで受信できます。

ちなみにPollメソッドの第一引数を負の値にすると、Pollメソッドでは(サーバを止めたりしていなければ)常にtrueですぐに復帰し、Receiveパケットを受信できます。

public class CustomSocket :System.Net.Sockets.Socket
{
  public CustomSocket () : base(System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.IP) { }

    public void ReceiveAny()
    {
        while (base.Poll(100000, System.Net.Sockets.SelectMode.SelectRead)) // <- ここのPollメソッド
        {
            var val1 = base.Available;
            if (val1 == 0)
                break; // <- ここにブレイクポイントを置いてもヒットしないサーバ側の挙動(サーバ側から接続終了しない)で動かしてみています。netstat コマンドでも ESTABLISHED のままです。
            var pkt = new byte[val1];
            base.Receive(pkt);

            // (略)
        }
        System.Threading.Thread.Sleep(200); // <- ここで少し待機させてみる(300ms以上ではほぼ受信され、100ms以下ではうしろのbase.Availableは0となることが多いです)
        var val2 = base.Available;
        if(val2 > 0) {
            var pkt2 = new byte[val2];
            base.Receive(pkt2);
        }
        // (略)
    }
}

public static class MyProgram 
{
    public static void Start()
    {
        // address, port などをここで指定
        var epRemote = new System.Net.IPEndPoint(address, port);
        using(var soc = new CustomSocket ())
        {
           soc.Connect(epRemote);
           soc.ReceiveAny();
           // (略)
        }
    }
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

check解決した方法

+1

.NET Framework のソースを読んでいて、間違いに気づきました。。

第一引数はマイクロ秒単位で指定するもので、私は勘違いしてミリ秒単位で指定してしまっていました。
サンプルコードでは、100秒ではなくたったの100ミリ秒しか待機していないことになります。

while (this.Poll(100000000, System.Net.Sockets.SelectMode.SelectRead))

と修正することで、受信までしっかり待つようになりました。

htsignさん、有難う御座いました。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

回答とは違いますが、MSDNの応答に対して無制限に待機する場合は、microSeconds パラメーターを負の整数に設定しますというこの解説文も怪しいですね。
ソースを読むと、負の数ではなく-1(=System.Threading.Timeout.Infinite)ですね。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/09/27 14:33

    どうも有難う御座います!本当ですね。。
    英語の原文でも
    "Set the microSeconds parameter to a negative integer if you would like to wait indefinitely for a response."
    なので、誤訳でもなく、もとから食い違っているのですね。

    お恥ずかしいことに、ソースを読んでいて、私の間違いに気づきました...

    キャンセル

  • 2016/09/27 14:50

    Poll 関数の第一引数は Int32 型なので、マイクロ秒単位で指定するとなれば、2秒ちょっとまでしか待機指定できないことになりますね。
    ただ、ソースを拝見したところ
    if (microSeconds != -1) {
    MicrosecondsToTimeValue((long)(uint)microSeconds, ref IOwait);
    ...
    }
    とあるので、実際には4秒少々までは指定できるのですね。にしてもこの仕様は何とも...なぜ long 型などにしなかったのだろう Microsoft さんは。

    キャンセル

  • 2016/09/27 15:04 編集

    Windows APIのレベルではlong秒指定できるっぽいですね。(このlongは.NETのintと同bitみたいですが)
    https://msdn.microsoft.com/en-us/library/windows/desktop/ms740560%28v=vs.85%29.aspx
    P/Invokeで直接叩けば長大な時間待機することもできそうです。する価値があるかは別として。

    それから、
    (double)int.MaxValue / 1000000 // => 2147.483647
    なので、少なくとも4000秒ちょっとは行けそうです。

    キャンセル

  • 2016/09/27 15:31

    https://msdn.microsoft.com/ja-jp/library/3b2e7499.aspx
    int, longのビット数に関してはここに書いてありました。
    Selectメソッドの第一引数でint型を取るのはこれが関係しているのでは?

    longを受け付けると仮定すると、MicrosecondsToTimeValueメソッド内の処理でマイクロ(1000000)で割っても、int.MaxValue(WinAPIのlong最大値)より遥かに大きく、予期しない問題が発生する可能性があります。
    たとえuncheckedにしたとしてもそれはそれでユーザーの予期しない処理となっていろいろ問題になりそうです。
    だからあえてintという制限をかけたと考えられます。

    というところまで妄想しました。

    キャンセル

  • 2016/09/28 08:56

    htsignさん
    ご指摘、有難う御座います。単位をまた間違えてしまいました...10^(3*3)ではナノ秒単位になってしまい、マイクロ秒の場合は10^(3*2)でした。
    最大4000秒も待機できるのであれば、十分な長さですね。int型の範囲でたしかに十分実用的です。

    キャンセル

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

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

関連した質問

  • 解決済

    System.out.printからSystem.out.writeへの書き換え

    単純な足し算プログラムの書き換えができません。 System.out.printではなく、System.out.writeを使用するプログラムに書き換えたいのですが、 うま

  • 解決済

    NodejsとGooglemapsを利用したWEBアプリについて

    概要 node.jsのWebSocketとgoogleapiのmapを利用して 待ち合わせWEBアプリを作成しています 位置情報取得->マップ表示->自分のMarker表示-

  • 解決済

    Webサイトのxmlコードを読み取るプログラム開発

    windows form applicationで、webサイトのxmlコードを読み込むプログラムを作りたいと考えていますが、どのように作ればよいか大まかにでも教えていただきたいと

  • 受付中

    C# ソケット通信におけるクライアントの切断確認

    ソケット通信を利用したサーバープログラムを作成しています。 別のサーバープログラムと接続している既存のクライアントシステム(PCではなく専用のハードウェアで動いています)と繋げる

  • 受付中

    Watson の speech to text を HTTP REST で呼び出す方法

    前提・実現したいこと 【IBM Watson音声認識のAPI呼び出し】 IBM Watson の HTTP REST API で speech to text の recogn

  • 解決済

    C# CoincheckのAPIを実行したい。

    お世話になります。 C#の勉強をしたいと思い 下記のサイトより https://kokenji.net/coincheck-api/ コードを拝借してAPIを実行してみまし

  • 解決済

    c# html~情報取得~表示

    私は、プログラミング言語のc#を学び始めた初心者です。 関東でIT関連の仕事をしています。 以前に、少し趣味でJavaを少しかじってましたが、自主的に少しやっていた程度です

  • 解決済

    C# ネットワーク間のシリアライズ

    今TCP/IPでクライアントとサーバーの通信をしています。 クライアントのClassをシリアライズし、サーバーに送りたいのですがやり方がイマイチわかりません。 クライアント

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

  • .NET Framework

    456questions

    .NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。