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

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

新規登録して質問してみよう
ただいま回答率
85.48%
.NET Framework

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

Q&A

解決済

2回答

9453閲覧

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

hsk

総合スコア728

.NET Framework

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

0グッド

0クリップ

投稿2016/09/27 04:32

編集2016/09/27 05:04

お世話になっております。
.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パケットを受信できます。

C#

1public class CustomSocket :System.Net.Sockets.Socket 2{ 3 public CustomSocket () : base(System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.IP) { } 4 5 public void ReceiveAny() 6 { 7 while (base.Poll(100000, System.Net.Sockets.SelectMode.SelectRead)) // <- ここのPollメソッド 8 { 9 var val1 = base.Available; 10 if (val1 == 0) 11 break; // <- ここにブレイクポイントを置いてもヒットしないサーバ側の挙動(サーバ側から接続終了しない)で動かしてみています。netstat コマンドでも ESTABLISHED のままです。 12 var pkt = new byte[val1]; 13 base.Receive(pkt); 14 15 // (略) 16 } 17 System.Threading.Thread.Sleep(200); // <- ここで少し待機させてみる(300ms以上ではほぼ受信され、100ms以下ではうしろのbase.Availableは0となることが多いです) 18 var val2 = base.Available; 19 if(val2 > 0) { 20 var pkt2 = new byte[val2]; 21 base.Receive(pkt2); 22 } 23 // (略) 24 } 25} 26 27public static class MyProgram 28{ 29 public static void Start() 30 { 31 // address, port などをここで指定 32 var epRemote = new System.Net.IPEndPoint(address, port); 33 using(var soc = new CustomSocket ()) 34 { 35 soc.Connect(epRemote); 36 soc.ReceiveAny(); 37 // (略) 38 } 39 } 40}

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

自己解決

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

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

C#

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

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

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

投稿2016/09/27 05:37

hsk

総合スコア728

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

投稿2016/09/27 04:44

htsign

総合スコア870

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hsk

2016/09/27 05:33

どうも有難う御座います!本当ですね。。 英語の原文でも "Set the microSeconds parameter to a negative integer if you would like to wait indefinitely for a response." なので、誤訳でもなく、もとから食い違っているのですね。 お恥ずかしいことに、ソースを読んでいて、私の間違いに気づきました...
hsk

2016/09/27 05:50

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

2016/09/27 06:23 編集

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秒ちょっとは行けそうです。
htsign

2016/09/27 06:31

https://msdn.microsoft.com/ja-jp/library/3b2e7499.aspx int, longのビット数に関してはここに書いてありました。 Selectメソッドの第一引数でint型を取るのはこれが関係しているのでは? longを受け付けると仮定すると、MicrosecondsToTimeValueメソッド内の処理でマイクロ(1000000)で割っても、int.MaxValue(WinAPIのlong最大値)より遥かに大きく、予期しない問題が発生する可能性があります。 たとえuncheckedにしたとしてもそれはそれでユーザーの予期しない処理となっていろいろ問題になりそうです。 だからあえてintという制限をかけたと考えられます。 というところまで妄想しました。
hsk

2016/09/27 23:56

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問