ソケットによる複数平行アクセスをするには

[上に] [前に] [次に]
同期 2000/03/05(日) 23:22:43
perlで他のサーバーにあるファイルを読むのにソケットを使っています。
WIN95, ActivePerl version 5.005_03 Build 522
これを複数平行して実行するにはどうしたら良いのでしょうか?
一つだけなら

*前処理
while(<S>){
 print O $_;
}
*後処理

のようにやる訳ですがこれを

*前処理
while(データがある間){
 $s1=<S1>;print O1 $s1;
 $s2=<S2>;print O2 $s2;
 $s3=<S3>;print O3 $s3;
}
*後処理

のようにしたいと思っているのです。
問題となるのがサーバーからのデータ読み込みで
すべてのサーバーに対するアクセスがスムーズなら良いのですが
例えばS2のサーバーが遅いと次のS3サーバーにアクセスしに行かないのです。
そこで直接<S2>のようにデータを読みに行くのでなく
データがあるかを確認してから読めばいいのではと思ったのですが
そこでつまづいてしまいました。


同時平行でなく

*前処理
while(<S1>){
 print O1 $_;
}
*後処理

*前処理
while(<S2>){
 print O2 $_;
}
*後処理

のようにすれば良いという意見もあると思いますが
これでもS2が遅いとS3の処理に進みませんよね。

そこでIE5の同期のように平行して出来たらS2が遅くても
取りあえずS1とS3の処理が出来て良いかなと考えたのですが・・・。

気まぐれ 2000/03/06(月) 00:43:44
作ったこと無いんで確証は無いですが・・・
子供(子プロセス)を作って、親で動作をコントロールするとか。
# でも作った以上は責任を持ってコントロールしないとサーバーが
# 異常動作するかも

↓ここが参考になるかもしれません。
http://X68000.startshop.co.jp/~68user/

ユニオン 2000/03/06(月) 05:25:10
はじめまして、ユニオンといいます。
プロセスやスレッドを新たに作らずにやりたいのなら
select()を使ってみてはいかがでしょうか?
(デフォルトの出力を変更するものではなくてシステムコールのほう)

Perlでselectを使ったことがないので実例は示せませんが、参考までに

B-Cus 2000/03/06(月) 07:52:52
他にもソケットをノンブロッキングモードにするとか、
  use Socket;
  recv(SOCK,$buf,1024,MSG_PEEK);
ってのもあるでよ。

同期 2000/03/09(木) 23:01:25
select()の使用法が良く判らなかったので
fcntl()でノンブロッキングにしてrecv(MSG_PEEK)でデータチェックする
以下のようなプログラムを作って試したのですが
Your vendor has not defined Fcntl macro F_SETFL
となってしまいました。(^^;
引数の指定法が悪いのでしょうか?

test.cgiはN秒スリープしながらデータを出力するプログラムで
これ自体の動作は問題ありません。


use Fcntl;
use Socket;

$server  = "localhost";
$url  = "/cgi/test.cgi";

$addr  = (gethostbyname($server))[4];
$name  = pack("S n a4 x8", 2, 80, $addr);
socket(S, 2, 1, 0);
$ret = connect(S, $name);
print "connect[$ret]\n";
fcntl(S, F_SETFL, O_NONBLOCK);
select(S); $| = 1; select(stdout);
print S "GET $url HTTP/1.0\n\n";

$done = 0;
while (!$done)
{
  recv(S, $buf, 1024, MSG_PEEK);
  if($buf ne "")
  {
    $buf = <S>;
    if($buf eq "")
    {
      $done = 1;
    }
    else
    {
      print "[$buf]\n";
    }
  }
  sleep(1); print ".\n"; # 動作確認用の表示
}
close(S);

B-Cus 2000/03/10(金) 02:52:33
> select()の使用法が良く判らなかったので
use IO::Socket;
use IO::Select;

@servers=qw(www.goo.ne.jp www.yahoo.co.jp www.wakusei.ne.jp);
$selecter=IO::Select->new;
$port = 80;

foreach $server ( @servers ){
    $sock = IO::Socket::INET->new("$server:$port");
    $selecter->add($sock);
    $sock2host{$sock} = "$server:$port";

    $message = "GET / HTTP/1.0\nHost: $server\n\n";
    print $sock "$message";
    $sock->flush();
}

$last_sock = $#servers+1;
while ($last_sock){
    ($active_socks) = IO::Select->select($selecter,undef,undef,undef);
    foreach $sock (@$active_socks){
        $len = read($sock,$buf,1024);
        if ( $len ){
            $buf =~ s/^(.{20}).*/$1/s;
            $buf =~ s/^/      /mg;
            print "read ${len}bytes from $sock2host{$sock} $buf....\n";
        } else {
            print "fin $sock2host{$sock}\n";
            $selecter->remove($sock);
            $last_sock--;
        }
    }
}

同期 2000/03/10(金) 21:30:20
[[解決]]
これです。まさにこういう動作をさせたかったのです。
参考にさせて頂きます。
どうもありがとうございます。(^^)

[上に] [前に] [次に]