68user's page 掲示板

Prev< No. 1615〜1620> Next  [最新発言に戻る] [過去ログ一覧]
No. 1615 # mak(spriggan) [E-mail] 2001/01/19 (金) 19:59:14
>>1591 68user
レスどもです。
ret = select( 64, &work_fdset, NULL, NULL, &time_out );
と指定していたので、ファイルディスクリプタ
0〜64までをチェックしかselectはチェックしていなかったので
selectはタイムアウトを返し、実際取得したファイルディスクリプタは
64を超えた数になっていたのでFD_ISSETは反応を返した。
というふうになっていた模様です。

ちなみに、ファイルディスクリプタの番号を
ここからここまでの番号しか取得しない。とか制限かける方法とか
ないのでしょうか?

No. 1616 # みかん(perlでソケットの質問してた方) 2001/01/19 (金) 22:41:30
レス遅くなってごめんなさい。
>>1525 68user
>2. 相手側がコネクションを切断したときも select は 1 を返します。
> その際、recv すると1バイトも読めず、0を返しているはずなのに

    コネクションが切断したときのselectの返り値、recvの返り値については
    このレスを見て初めて知りました。ありがとうございます。

>>1526 68user
>ついでに言っておくと、状況にもよりますが、select に
><> や read を使うのは不適切です。select で読み込み

    このサイトで紹介されているECHOサーバのように、「クライアントやサーバーとうま
    く接続できたかどうかを確認する」、というような形が正しいselectの使われ方だと
    考えてもいいですか?

    alarmを使う事でブロッキングを強引に回避するという方法がありますが、他に
    比較的OSに依存しない形でブロックを避ける手段はないでしょうか?

No. 1617 # gixs 2001/01/20 (土) 01:15:01
>>1615 mak(spriggan)氏
> 0〜64までをチェックしかselectはチェックしていなかったので
> selectはタイムアウトを返し、実際取得したファイルディスクリプタは
> 64を超えた数になっていたのでFD_ISSETは反応を返した。
> というふうになっていた模様です。

確かになりますね。
知りませんでした。
しかし、fd_setの戻り値をチェックするのは、select(2)が正数を返した時のみにしておいた方が安全でしょう(select(2)に正しい第1引数を渡したとしても、タイムアウト時にfd_setがゼロクリアされるかは分かりません。規格としてゼロクリアが決まっているならO.K.でしょうが、そこまでしてselect(2)の戻り値のチェックを省く理由も見付かりません)。
# 今回はselect(2)の戻り値チェックを省いたおかげで、第1引数のバグに気づいたわけですが。

> ちなみに、ファイルディスクリプタの番号を
> ここからここまでの番号しか取得しない。とか制限かける方法とか
> ないのでしょうか?

select(2)を呼ぶ時に、チェックすべきファイルディスクリプタの部分だけ、fd_setにマスクをかけますが(第1引数は効率の為)。
それとも、効率を気にしていますか?
確かに、非常に大きな番号のファイルディスクリプタ1つだけをチェックするとなると、無駄がありそうなことは否定しません。
それが気になるなら、poll(2)でしょうか。

No. 1618 # gixs 2001/01/20 (土) 01:33:19
>>1612 YAGI氏
# その場に行けば解決できるかもしれませんが、このやりとりでは、助けられる自信はありません。申し訳ないです。

> 当人まだ、知識が乏しく本にsmitとsmittyが書いてあったりもするのですが
> 区別が分からずsmitでやってました^^;

X以外でsmitを起動すると、tty版のsmittyと同じ動きなので、

> なお、smitのコマンド類はWinNTのTera Termより発行しています。

smitでもsmittyでも変わりありません。
# という事は、走る男を見ていないんですねえ。
# もしかして一度も見たことが無い、とかだったら不幸です。

> 私も、その手順で実際に行なってバージョンアップしたClientをもう一度
> NISの再設定を行ってServerのマップをmakeし直すと
> 接続できなくなってしまうのです。

うーむ、あまり他人の文章のケチをつけるのもなんですが、何をどういう順序でやったのか、いまいち不明です。
(改行の位置に読点があると考えてよいのでしょうか。
この手の説明は、時系列に並べた箇条書の方が分かりやすいと思います。)

No. 1619 # 68user 2001/01/20 (土) 04:46:57
>>1607 H.Motoki
> コマンド型ツールでメール層送受信が可能なもの
> かつ、添付ファイルが遅れるもの
> かつ、Solarisで動作するもの
僕は知りませんが、
    http://www.freebsd.org/cgi/ports.cgi?query=mime&stype=all&release=4.1-STABLE%2Fi386
の中を見ると、お望みのものっぽいのがありますので、
Solaris でコンパイルしてみてはどうでしょう。


>>1615 mak(spriggan)
> selectはタイムアウトを返し、実際取得したファイルディスクリプタは
> 64を超えた数になっていたのでFD_ISSETは反応を返した。
なるほど納得です。

> ちなみに、ファイルディスクリプタの番号を
> ここからここまでの番号しか取得しない。
ここから、は指定できません。ここまで、ってのは select の
第一引数ですね。多くの UNIX の実装では select が扱えるのは
1024 までのディスクリプタのようですから、この程度なら
僕はあまり気にしません。あと、FreeBSD 4.2-RELEAE の
select(2) には
    For historical reasons, select() will always examine
    the first 256 descriptors.
とありますので、あまり神経質になるほどのことでもないかも
しれません (し、そうでないかもしれません)。

No. 1620 # 68user 2001/01/20 (土) 04:49:52
繁盛しているのはいいけれど、返事が大変だなぁ。

>>1616 みかん(perlでソケットの質問してた方)
>> select に <> や read を使うのは不適切です。
> 「クライアントやサーバーとうまく接続できたかどうかを確認する」、
> というような形が正しいselectの
> 使われ方だと考えてもいいですか?
いいえ。タイムアウトも select の正しい使い方です。

サンプルプログラムを書いてみました。
    http://X68000.startshop.co.jp/~68user/tmp/select-sysread.pl
echo サーバと echo クライアントです。2つスクリプトを書くのが
面倒だったので、fork して 片方が echo サーバになり、もう片方は
echo クライアントとして動作するようにしました。

echo クライアントは echo サーバに接続し、文字列を送り、
それを受け取るだけです。echo サーバは select でソケットを
監視し、マルチスレッドサーバとして動作します。また、
クライアントが接続してから2秒経過したらタイムアウトとして
切断します。

で、これを動かすと、
    親:5000 でクライアント待ち
    子:localhost:5000 に接続します。
    親:127.0.0.1:1291 からの接続を受け付け
    子:送信メッセージ: HELLO (*1)
    親:127.0.0.1:1291 に反応あり
    親:127.0.0.1:1291 からメッセージ受信:HELLO
    親:127.0.0.1:1291 へメッセージ送信:Received HELLO
    子:受信メッセージ: Received HELLO (*2)
    子:5秒眠ります (*3)
    親:タイムアウトにより 127.0.0.1:1291 を切断 (*4)
    子:新しい接続 (*5)
    親:127.0.0.1:1292 からの接続を受け付け
    子:送信メッセージ: HELLO AGAIN (*6)
    親:127.0.0.1:1292 に反応あり
    子:5秒眠ります (*7)
となります。

最初は子が親に HELLO と送り (*1)、Received HELLO を受け
取ります (*2)。次に、子は5秒 sleep するので (*3)、親は
タイムアウトとして切断します (*4)。

次に、子は新しいソケットを生成し再度親に接続します (*5)。
子は親に HELLO AGAIN と送ります (*6)。ただし、今度は
メッセージの最後に改行コードを付けません。そして子は5秒
sleep します (*7)。するとここで親も子も動作が止まり、
永遠にデッドロックします。

なぜなら、親は子からのメッセージを
      $recv_message = <$sock>;
で読んでいるからです。改行コードが送られてこないと、
ここでブロックしてしまいますので、これでは select を
使う意味がありません。

今回は意図的に改行コードを含まない文字列を送りました。
これと同じことが、改行コード以前のデータが到着している
けれど、改行コードはパケットロスにより再送中、という
状況でも起こります。

というわけで、こういうときは
      sysread($sock, $recv_message, 100);
などとします。これなら、既に到着しているデータのみを
読みます。100バイト分のデータを読もうとしますが、もし
そのとき10バイト分のデータしか届いていなかったら、
そこで sysread から処理が戻り、select まで処理が
進み、正常にタイムアウト処理が行えます。

Prev< No. 1615〜1620> Next  [最新発言に戻る] [過去ログ一覧]