Perlのソケット接続のタイムアウト

このQ&Aのポイント
  • Perlのソケット接続でタイムアウトを設定する方法を教えてください。
  • Perlのソケット接続で接続に失敗した場合に別の接続先に接続する方法を教えてください。
  • ソケット接続にタイムアウトを設定できるモジュールがあれば教えてください。
回答を見る
  • ベストアンサー

Perlのソケット接続のタイムアウト

Perlのソケット接続、connectで接続に失敗した時にdieではなく、別の接続先に接続するよう、次のように書きました。 connect(ファイルハンドル, 接続先1) || connect(ファイルハンドル, 接続先2) || die; 上記のコードではundefが帰って来た時にしか次の接続先に接続せず、例えばサーバーが応答せず、パケットをドロップしているような場合だと接続先1への接続でプログラムがハングアップしたような状態になります。 それぞれのconnectにタイムアウト時間を設けたいのですが、どのようにしたらよろしいのでしょうか。 また、ソケット接続にタイムアウトを設定できるモジュールがありましたらそちらを利用したいと思います。 Perl初心者で大変恐縮ですが、何卒宜しくお願い致します。

  • inshin
  • お礼率78% (104/133)
  • Perl
  • 回答数4
  • ありがとう数4

質問者が選んだベストアンサー

  • ベストアンサー
  • kumoz
  • ベストアンサー率64% (120/185)
回答No.3

次のプログラムを使って、自宅でテストしたところ接続に成功しました。プログラムの誤りではなく、connect 試行中に時間切れになっているのではないかと思います。 use strict; use warnings; use Socket; socket(SOCK, PF_INET, SOCK_STREAM, 6) or die $!; eval { local $SIG{ALRM} = sub { die "timeout1"; }; alarm 5; connect(SOCK, sockaddr_in("80", inet_aton("192.168.0.13"))) || die "$!"; alarm 0; }; if ($@) { alarm 0; print $@; die; } close SOCK; print "connect test OK\n"; 参考までに記すと、開いていないポート番号を指定した場合は、瞬時にエラーになります。 > 接続を拒否されました at xxx.pl line 10. > 接続を拒否されました at xxx.pl line 10. > ...propagated at xxx.pl line 17. 誤った IP アドレスに指定すると、数秒後に以下のエラーメッセージが表示されます。 > ホストへの経路がありません at xxx.pl line 10. > ホストへの経路がありません at xxx.pl line 10. > ...propagated at xxx.pl line 17. alarm 2; に変更すると、タイムアウトエラーに変わります。 > timeout1 at xxx.pl line 8. > timeout1 at xxx.pl line 8. > ...propagated at xxx.pl line 17.

inshin
質問者

お礼

丁寧に実証して頂きありがとうございます。 こちらでも同じことは確認できました。 ただ、本来の目的である、つながらない場合は次の接続先につなぐということ(先ほどお礼に書かせて頂いたコード)をしようとすると2つ目以降のconnectが必ずタイムアウトしてしまうのです。 再三お恥ずかしいのですが、何卒よろしくお願い致します。

その他の回答 (3)

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.4

「Perl ネットワークプログラミング」(2002年、ピアソン・エデュケーション刊) の 86 頁に以下のような記述があります。 ----------------------------------------------- $boolean = connect(SOCK, $dest_addr) connect() 関数は、コネクション指向のソケットを指定された送信先アドレスに接続しようとする。ソケットは socket() 関数を使用して、パックされた送信先アドレスは sockaddr_in() 関数などを使用して、あらかじめ作成しておかなければならない。 ソケットのローカルアドレスに使用する短命なポートは、システムによって自動的に選択される。 connect() 関数は、正常終了した場合に true の値を返す。それ以外の場合は false を返し、$! に問題を説明するエラーコードが設定される。コネクション指向のソケットで connect() 関数を複数回呼び出すことは違法である。connect() 関数を2回目に呼び出すと、EISCONN (「トランスポートエンドポイントがすでに接続されている」) エラーが発生する。 ------------------------------------------------ 私も知らなかったのですが、2度目の接続ではソケットの流用はできず、新しく作り直す必要があるようです。テストしてみたところ、それでうまくいくようです。 use strict; use warnings; use Socket; socket(SOCK, PF_INET, SOCK_STREAM, 6) or die $!; eval { local $SIG{ALRM} = sub { die "timeout1"; }; alarm 2; connect(SOCK, sockaddr_in("80", inet_aton("192.168.0.99"))) || die "$!"; alarm 0; }; if ($@) { alarm 0; print $@; # 次の2行を追加 close SOCK; socket(SOCK, PF_INET, SOCK_STREAM, 6) or die $!; eval { local $SIG{ALRM} = sub { die "timeout2"; }; alarm 2; connect(SOCK, sockaddr_in("80", inet_aton("192.168.0.13"))) || die "$!"; alarm 0; }; if ($@) { alarm 0; print $@; die; } } close SOCK;

inshin
質問者

お礼

度々のご回答ありがとうございます。 なるほど。connectの結果を待たずに次のconnectをしようとしたことが原因だったんですね。 connect(ファイルハンドル, 接続先1) || connect(ファイルハンドル, 接続先2) || die;で問題が起こらなかったのはconnect関数がちゃんと終了していたからですね。 新たにソケットを生成するように書いたらこちらでもちゃんと動作しました。 本を読むことも大事ですね。その本買ってみようと思います。 いろいろとありがとうございました。

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.2

eval ブロックでエラーが発生した場合は、特殊変数 $@ にエラーメッセージがセットされます。したがって、直後に $@ をチェックするのが基本になります。 eval { local $SIG{ALRM} = sub { die "timeout1"; }; alarm 1; connect(ファイルハンドル, 接続先1); alarm 0; }; if ($@) { alarm 0; print $@; eval { local $SIG{ALRM} = sub { die "timeout2"; }; alarm 1; connect(ファイルハンドル, 接続先2); alarm 0; }; if ($@) { alarm 0; print $@; die; } } 「プログラミング Perl 第3版」の alarm 関数のリファレンスには、以下の記述があります。 > 古いマシンでは、秒数の数え方の関係から、経過時間が、 > 指定した秒数より最大で 1 秒短くなることがある。 > さらに、負荷の重いシステムでは、プロセスを時間どおり > 即時に起動できないこともある。 eval ブロックが正常に終了した場合は、最後に評価された式の値が返されます。(使っているマシンが古いということはないでしょうが)、alarm の秒数を増やしてみてはどうでしょうか。

inshin
質問者

お礼

再度のご回答いただきありがとうございます。 ご教示のコードの通りにやってもやはり必ずタイムアウトしてしまいます。 コード #!/usr/bin/perl use Socket; #接続先のホスト名 $host = "www.ugtop.com"; #CGIなどへのパス $path = "/spill.shtml"; $uri = "http://".$host.$path; # ソケットの生成 $ipaddr = inet_aton($proxy); socket (SOCK, PF_INET, SOCK_STREAM, 6) || die "$!"; # 接続先プロキシサーバーリスト # 1 timeout # 173.193.200.199:8080 # 2 refuse (undef) # 173.255.142.206:8080 eval { local $SIG{ALRM} = sub { die "timeout1"; }; alarm 10; print "first try\n"; connect(SOCK, sockaddr_in("8080", inet_aton("173.193.200.199"))) || die "$!"; alarm 0; }; if ($@) { alarm 0; print $@; eval { local $SIG{ALRM} = sub { die "timeout2"; }; alarm 10; print "second try\n"; connect(SOCK, sockaddr_in("80", inet_aton($host))) || die "$!"; alarm 0; }; if ($@) { alarm 0; print $@; die; } } select(SOCK); $|=1; select(STDOUT); $|=1; # HTTP要求の送信 print SOCK <<EOF; GET $uri HTTP/1.1 Host: $host Connection: close EOF # HTTP応答を受信 while (<SOCK>) { print; } close (SOCK); 出力 > ./socket.pl first try timeout1 at ./spill.pl line 23. second try timeout2 at ./spill.pl line 34. timeout2 at ./spill.pl line 34. ...propagated at ./spill.pl line 44. たびたびすみませんが、よろしくお願いいたします。

inshin
質問者

補足

すみません、出力正しくはこちらです。 > ./socket.pl first try timeout1 at ./socket.pl line 23. second try timeout2 at ./socket.pl line 34. timeout2 at ./socket.pl line 34. ...propagated at ./socket.pl line 44.

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.1

OS でサポートされていれば、シグナルを利用してタイムアウトさせることができます。(以下のコード例は、Unix 系であれば問題なく実行できます。Windows 系については知らないので悪しからず。) use strict; my $i; eval { local $SIG{ALRM} = sub { die "timeout" }; alarm 3; ++$i while 1; # ここにタイムアウトさせたい処理を書く alarm 0; }; alarm 0; print "$i, $@" if $@;

inshin
質問者

お礼

ご回答いただきありがとうございます。 ご教示の方法を元のソースに組み込もうと、次のようにしてみたのですが、必ずタイムアウトしてしまいます。 eval { local $SIG{ALRM} = sub { die "timeout" }; alarm 1; connect(ファイルハンドル, 接続先1); alarm 0; } || eval { local $SIG{ALRM} = sub { die "timeout" }; alarm 1; connect(ファイルハンドル, 接続先2); alarm 0; } || die; orでつないでいるのがいけないのかとも思いますが、それ以外の書き方を知らないので… ちなみに環境はこちらになります。 > perl -v This is perl, v5.8.8 built for i686-linux よろしくお願いいたします。

関連するQ&A

  • Perlのorのソケット接続での利用法について

    一般的なソケットの接続は次のような書き方になっていると思いますが、connectでundefが帰って来た時にdieではなく、別の接続先に接続するような書き方を探っています。 connect(ファイルハンドル, 接続先) || die; 上記のコードを次のように書き換えてみましたが、接続先1で失敗した場合、接続先2に接続されるかと思いきや、そのままdieしてしまいます。 connect(ファイルハンドル, 接続先1) || connect(ファイルハンドル, 接続先2) || die; この書き方はどこがまずかったのでしょうか。 また、想定通りに動作させるにはどのように書いたら良いのでしょうか。 Perl初心者で大変恐縮ですが、何卒宜しくお願い致します。

    • ベストアンサー
    • Perl
  • Winsockで接続待ちタイムアウトを設定する方法

    現在、WinsockAPIを使ってソケット通信プログラムを作っています。  このプログラムの仕様の中で、接続待ちタイムアウトというものがあります。これはつまり、connect関数を実行してから応答が返ってくる(関数が値を返す)までの時間監視のことなのですが、そのような値をソケットに対して設定する手段というのはあるのでしょうか?

  • alarmのタイムアウト後の処理でスクリプトを止めない方法をご教授ください

    はじめまして。 現在perlで簡単なRSSリーダーを作っています。 簡単に書けば下記のようなスクリプトです。 use LWP::Simple; use XML::RSS; foreach(@hoge) { # @hogeはrssファイルのURL $SIG{ALRM} = sub{ die("alarm_error"); }; alarm 10; $rss->parse($_); alarm 0; } rssファイルをとりにいって10秒たって取得できなければnextで次のrssファイルへという形にしたいのですがどうもやり方がわかりません。 いろんなところで調べてもタイムアウト後はdie等によって強制終了するやり方しか見つかりませんでした。 タイムアウト後nextを使えるやり方をご存知のかたおられたらお教えください。 よろしくお願いします。

    • ベストアンサー
    • Perl
  • PerlからのCSV出力

    Perl初心者です。 PerlでOracleに接続し、結果をCSVを出力したいのですが 下記プログラムだとなにも出力されません。 Oracle10g OS:Linux *** #!/user/bin/perl # # # DBI モジュールの読み込み use DBI; #接続 my $hDb = DBI->connect("dbi:Oracle:dbname", "user/pass") or die "CONNECT ERROR $DBI::errstr"; #SELECT # $hSt = $hDb->prepare("SELECT ID,NAME FROM MASTER"); open(OUT, ">data.dat"); #実行 $nRes = $hSt->execute; #データの取得 while($raRes = $hSt->fetchrow_arrayref) { print OUT join(",", @$raRes), "\n"; } # ファイルクローズ close(OUT); $hSt->finish; $hDb->disconnect;

  • Firefoxで接続タイムアウト

    いつもお世話になります。 Firefox1.0.6をを使っております。 リンクやブックマークによるブラウジングで早い時は早いのですが、接続のタイムアウトになるときが結構あります。 一応、about:configのnetwork.http.connect.timeoutを120に変更してみましたがさほど効果はありませんでした、 とにかくタイミングが悪い時は駄目で、タイムアウトが出たすぐクリックすると表示される事が多いです。 何か他に試す事はありますでしょうか? 当方、WindowsXP HOME sp2 メモリは2Gあるのでそれほど問題があるとも思えません。 以上 よろしくお願いします。

  • Firefoxで「接続がタイムアウトしました」のエラー

    Firefoxで「接続がタイムアウトしました」のエラー このところ、Firefoxで「接続がタイムアウトしました」という 警告メッセージが頻繁に表示されます。 一部のサイトが開けない状況です。 現在のfirefoxのバージョンは、3.5.8です。 3.0の頃から、この現象は頻発しています。 firefoxのインストールし直すなどもやってみましたが 状況は変わらずです。 ちなみに、IEの切り替えアドオンも入れており エラー表示されたサイトもIEに切り替えると問題なく表示されます。 色々と検索し、下記のような記事を見つけ、 試したりもしましたが、変化なしでした。 http://okwave.jp/qa/q1367110.html ※上記記事内の「network.http.connect.timeout」というのはなく、 「network.http.max-connections」を変更してみました。。。 どなたかご存知の方がいらっしゃいましたら、 お力添えをいただけると幸いです。 よろしくお願いします。

  • connect タイムアウトのデフォルト

     Perl を使って HTTP 経由で HTML ファイルを取得するロジックを組んでいます。  通常、コネクト時のタイムアウト処理にはシグナルを使用しますが、これだとルーチンの呼び出し元に戻れません。  タイムアウトしたら終了せずに次の処理へ進む、というロジックを組みたいのですが、シグナルを設定しなかった場合、処理はちゃんと戻ってくるでしょうか。  本当ならこのくらい自分で確認すりゃいいんですが、なんせ「戻ってこないホスト」に心当たりがないので確認できないのです(^_^;  もしくは、シグナルのコールバック関数から元のルーチンへ戻る方法でもいいのです。  何かご存知の方がいらっしゃいましたら、よろしくお願いします。

    • ベストアンサー
    • Perl
  • Socketの接続のタイムアウトを設定するには

    JavaでSocket通信をしようとおもっています。 Socketはnewするときにconnectもしてしまいまうらしいので 妙なIPアドレスを指定したりして、応答がもどってこないと すべてそこで止まってしまうとおもうのですが、 Timeoutをかける方法はないのでしょうか。 setSoTimeoutというメソッドはreadのときとかStreamに関してしか かけられないということが分かりました。 Socketのコンストラクタの引数にSocketImplを指定して、 Socket.setSoTimeoutしてから SocketImpl.connectすれば大丈夫かな、とおもっていたのですが、 setSoTimeoutはreadにしか効かない、と明記してあったので、無駄ですよね。 どこかに情報がないかと検索エンジンでも探してみたのですが、 見つかりません。もしかしてタイムアウトを設定するのは 一般的な方法ではないのかとも思えてきました。 ずーーーーっと接続にしにいくのを待ちつづけるしかないのでしょうか。 どのような設計(というか使うクラス?)をするべきなのか、 思い当たるかたがいらっしゃいましたら教えていただけないでしょうか。 よろしくお願い致します(__)

  • Perl 教えてください。

    下記のような簡単なスクリプトなんですが、なぜかサーバーにアップすると動きません。 ローカルでコマンドプロンプトから直接実行するとまともに動作します。 パーミッションは「755」と「705」でやってみましたが。 perlのパスは合っていますし、実際同じ場所に置いた他のCGIは動作します。 #!/usr/bin/perl open (DT, "<./data/***/***.csv") or die "File '***.csv' Open Error."; @data = <DT>; $data[0] = ",,,,,,\n"; open (OUT,">./data/***/***1.csv") or die "File '***1.csv' Open Error."; print OUT @data; close (DT); close (OUT); 原因が分からず困っています。 解決策でなく、原因と思えるだけの回答で結構ですので何卒よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • タイムアウト時間の設定

    http://www32.ocn.ne.jp/~chi/FreeBSD/HowTo/mail/viaisp.html の下のほうに、「メールサーバ自身がタイムアウト等で接続状態を解除するまで 待ちましょう」 とあるのですが、タイムアウトと判断する時間を設定するには、sendmailではどのファイルのどこをいじったらいいのでしょうか?