68user's page 掲示板

Prev< No. 715〜731> Next  [最新発言に戻る] [過去ログ一覧]
No. 715 # たまり 2000/02/10 (木) 23:35
はじめまして、たまりと申します。
CGI、特にperlを勉強中の物です。
「ネットワークプログラミングの基礎知識」、滅茶苦茶参考になりました。
ありがとうございます。

現在、68userさんのスクリプトを参考に、FTPクライアントを書いているんですが、
どうもSTORがうまくいきません。
ファイルが送信し終わるまでに接続を切られてしまい、FTPdがファイルハンドルを
解放しないような状態になってしまいます。

当方の環境はというと、Windows98 + ActivePerl build 521 + TinyFTPDaemon Ver 0.51です。
HDの転送速度が追いつかないのかなと思い、以下のようにウェイトを入れてみても
だめでした。

while (<IN>) {
    print DATA $_;
    $i = 10000;
    while ($i) {$i--;}
}

上記のコードはPassiveモードでサーバが開いたDATAポートにINファイルをPUTして
いる部分だとお考え下さい。
結果として転送速度は確かに下がったのですが、ファイルハンドルを解放しない
(=FTPdが「受信が完了しました」と言わない」)という症状は改善されません。

なお、環境の制限により、シングルスレッドで書いています。

これは、環境による原因があるのでしょうか?
それともコーディング上の問題があるのでしょうか?

よろしければご教示下さい。

No. 716 # 68user [URL] [E-mail] 2000/02/11 (金) 20:24
> ファイルが送信し終わるまでに接続を切られてしまい
何バイト送ったら切れる、などという再現性はありますか?
1バイトずつ送ってみるとどうでしょう。また、切れるのは
データコネクションですか? コマンドコネクションですか?

> FTPdがファイルハンドルを解放しないような状態
というのは具体的にはどういう状態ですか?
これもデータコネクションかコマンドコネクションかを教えて下さい。

あと、このコードの前後数行を見せてもらえますか?

> HDの転送速度が追いつかないのかなと思い
これはさすがにないと思いますよ。Windows といえども OS ですから、
そこらへんはきっちりやってくれるでしょう。

ちょっと急いでいるので、この辺で。

No. 717 # 68user [URL] [E-mail] 2000/02/11 (金) 20:28
> これはさすがにないと思いますよ。
仮に HDD がとても遅かったとしても、
>> while (<IN>) {
でブロッキングするだろうから、読めなかったけど処理が
先に進むなんてことはないと思う、ということです。
# ここでブロッキングしてくれないと、OS の意味がない。

No. 718 # メリクリウス 2000/02/12 (土) 00:36
初めまして、セガBBSの過去ログ凄いです、
ご苦労様としか言えません、
また、じっくり見させてもらいにきます、
(今からみるんだけど、、、)

No. 719 # たまり 2000/02/12 (土) 02:10
> 何バイト送ったら切れる、などという再現性はありますか?
再現性はないようです。
接続が切られた時点でのサイズは不定でした。

> 1バイトずつ送ってみるとどうでしょう。
えっと、具体的にどのようにすればいいんでしょうか?
readで1バイトずつ読んでいくのでしょうか。ちょっと試してみます。

> また、切れるのはデータコネクションですか? コマンドコネクションですか?
すいません、説明が足りなかったようで。
どうやらQUITは送信されているようなのでデータコネクションが切られているようです。

>> FTPdがファイルハンドルを解放しないような状態
FTPdが起動中はエクスプローラからもファイルの削除等ができません。
もちろんスクリプトが終了していても、です。
FTPdを終了させると、送信したファイルが元サイズ以下(不定)であるという感じです。
そうなると削除等も可能になります。
「データコネクションかコマンドコネクションか」というのは自分にはわかりません。

> あと、このコードの前後数行を見せてもらえますか?
以下のようになります。
ほとんどftptrans.plを流用させて貰ってます(^^;

if ($mode eq 'put') {
    &send_command(COMMAND, "STOR $target_file\r\n");
} elsif ($target_file =~ m|/$|) {
    &send_command(COMMAND, "LIST $target_file\r\n");
} else {
    &send_command(COMMAND, "RETR $target_file\r\n");
}

&client_work(DATA, $data_connection_host, $data_connection_port);
open(IN, $infile) || die "$infile: $!";
binmode(IN);
while (<IN>) {
    print DATA $_;
}
close(IN) || die "close: $!";
&read_response(COMMAND);
close(DATA) || die "close: $!";

&send_command(COMMAND, "QUIT$CRLF");
&read_response(COMMAND);
close(COMMAND) || die "close: $!";

> # ここでブロッキングしてくれないと、OS の意味がない。
当てがはずれてたようですね(^^;
実際改善されなかったですし。


ファイルが送信し終わる前にQUITが送信されてると言うことはデータコネクションが
切断される前にコマンドコネクションが切断されると言うことでしょうか?
確かFTPってデータコネクションの切断=データの終わりでしたよね?
でもcloseでは死んでないようだし・・・

とりあえず1バイトずつを試してみます。

No. 720 # たまり 2000/02/12 (土) 02:13
あ、すいません、訂正です

> &send_command(COMMAND, "QUIT$CRLF");
&send_command(COMMAND, "QUIT\r\n");

です。

# 普段$CRLF="\r\n";でやってるもので(^^;

No. 721 # 68user [URL] [E-mail] 2000/02/12 (土) 09:19
> メリクリウスさん
メールの返事をここでさせて頂きますが、人の多いところから
リンクしちゃダメなのは、お肉さんのところです。うちは
どこからリンクしても構いませんです。


> FTPdが起動中はエクスプローラからもファイルの削除等ができません。
FTPd が STOR 先のファイルをオープンし続けているということですね。
謎ですね。

こちらには Windows 環境がないので、そのものずばりの答えは
わかりませんが、思いつくままに。

1. まずは小さなファイルでやってみて下さい。
2. sub client_work の最後で、SOCK のバッファリングを OFF にしている
   ($|=1) のは削ってませんよね?
3. QUIT 送信の前に &read_response(COMMAND) が一つ足りてないんじゃない
   ですか? データコネクションが確立したら、
    150 Binary data connection for test/hogemoge (127.0.0.1,33299).
   というレスポンス、さらに転送が完了したら
    226 Transfer complete.
   というレスポンスが返ってきませんか?
   (でもこれが原因じゃないと思う。レスポンスを読まなくても転送はできるはず)
4. $verbose=1 にして、プロトコル眺めてますよね?
5. passive なら、telnet コマンドだけを使って送信してみるのもいいかも。
6. ftp にデバッグオプションを付けて、プロトコルを観察してみては。

>> 1バイトずつ送ってみるとどうでしょう。
> えっと、具体的にどのようにすればいいんでしょうか?
例えばこんな感じです。
    &send_command("STOR $target_file\r\n");
    &client_work(DATA,$data_connection_host,$data_connection_port);
    &read_response(COMMAND);

    use IO::File;
    sysopen(IN,$infile,O_RDONLY);
    binmode(IN); # sysopen なら不要かな?
    while (1){
        $nread = sysread(IN,$buf,1);
        $read_size += $nread;
        last if ( $nread == 0 );
        print "$read_size read ";
        print DATA $buf || die "$!"; # 成功したかどうかチェック
        print "$read_size send.\n";
    }
    close(DATA); close(IN); &read_response(COMMAND);
    &send_command("QUIT$CRLF"); &read_response(COMMAND); close(COMMAND);
とりあえず、何バイト目でこけているのかは調べられるでしょう。
ただし、パケットレベルで 1バイトずつのデータが送られる、という
保証はありません。

念には念を入れて、print DATA の代わりに syswrite(DATA,...) を
使ってみるのもいいかも。
# syswrite = select(DATA);$|=1;binmode(DATA);print DATA と
# 同じはずだと思うんですが、違いましたっけ。


Solaris の ftpd 相手に試していて思ったんだけど、
    http://X68000.startshop.co.jp/~68user/net/ftp-5.html
> 次のアクションは
>  データコネクションの確立なのか
>  コマンドコネクションからのエラーレスポンスなのか
> のどちらなのかが わからないのです。
こんな変な仕様は、もしかして FreeBSD の ftpd だけかもしれない…。
調べておかねば。

No. 722 # アーゴン 2000/02/13 (日) 20:17
はじめまして。ゲームでわからない事があったので、誰か教えて下さい。
「七ツ風の島 物語」というゲームなのですが・・・
(知っている人、いるかなぁ・・・)
第11章 『小さくてキレイな物』・・・で、どうしたらいいのか、わかりません。 あった事といえば、白ガラスのヒューと、カンぞうに会った事です。できれば、わかる人、教えてください。よろしくお願いします。
  

No. 723 # たまり 2000/02/14 (月) 05:02
返事が遅くなって申し訳ないです。
ここ二日ほど色々あって何もできませんでした(^^;

> FTPd が STOR 先のファイルをオープンし続けているということですね。
です。FTPd側の問題という可能性も無くはない気もしますが(但し、普通の
クライアントを使用した場合は当然ながらうまく送信できます)

まず、先程試してみた結果から。

どうやら原因は
> 3. QUIT 送信の前に &read_response(COMMAND) が一つ足りてない
だったようです。
これを入れたらまともに動いてくれるようになりました。

念のため、詳しい報告も
> 1.
小さなファイル(数KB程度)でも同様でした
> 2.
削っていません。
> 4.
とりあえずまともに動くようにしようと即興で作っていたので$verbose=1の部分は
削ってました(^^;
本来はそういうときこそ細かくデバッグすべきなんでしょうけど・・・
> 5. 6.
telnetやftpを使えば問題なく送信できます。

なお、1バイトずつ読んでいく方法ですが、
print DATA $buf || die "$!";
ではデータ的に0x30、つまり'0'のところでなぜかdieするようです。
print DATA $buf;
のようにするとうまくいきました。

また、syswriteに関しては使ったことがないのであまりわかりませんが、
引数はsysreadと同様な形で取って、返り値は書き込んだバイト数のようです。
また、binmodeはsysopenでO_BINARYを指定しておけば不要かと思います。

ありがとうございました。

No. 724 # 68user [URL] [E-mail] 2000/02/14 (月) 18:59
> はじめまして。ゲームでわからない事があったので、誰か教えて下さい。
ここにはゲームに詳しい人はあまりいないような気がします…。


> どうやら原因は
>> 3. QUIT 送信の前に &read_response(COMMAND) が一つ足りてない
> だったようです。
なるほど。ちょっと不親切な ftpd ですね (と僕は思う)。

> ではデータ的に0x30、つまり'0'のところでなぜかdieするようです。
なんででしょう。調べてみよう。

> また、binmodeはsysopenでO_BINARYを指定しておけば不要かと思います。
ああそうか。(Cでいうところの) open でも改行コード変換できるんですね。

> こんな変な仕様は、もしかして FreeBSD の ftpd だけかもしれない…。
> 調べておかねば。
これは Solaris の ftpd でも同じでした。変なの。

No. 725 # ハリー 2000/02/15 (火) 19:43
はじめまして、ハリーと申します。
実は今、Kシェルを作っているんですが、シェル内で
exportコマンドを打って環境変数を変更しようとし
てもできませんでした。普通にexportコマンドを打
ってもダメなんですか?それとも出来ないのですか?
シェル初心者なので、よくわかりません。
よろしく、お願いします。

No. 726 # 68user [URL] [E-mail] 2000/02/15 (火) 19:59
export HOGE=FUGA で できませんか。

No. 727 # ハリー 2000/02/15 (火) 20:09
はい。
シェルの中でenvを打つと変更した環境変数に変わっているのですが
シェルが終ってもう一度、envを叩くと元に戻っています。

No. 728 # 68user [URL] [E-mail] 2000/02/15 (火) 20:35
子プロセスでいくら環境変数をいじっても、親プロセスには
影響しません。これはどんなシェルでも同じです。

具体的に何をしたいのかがわからないのですが、
どうしてもやりたければ、eval を使うしかないです。

No. 729 # ハリー 2000/02/16 (水) 09:04
ありがとうございました。
やってみます。

No. 730 # つる 2000/02/16 (水) 09:39
はじめましてつると申します。
ネットワークプログラムについての質問なのですが、
今、いろいろと事情があって
ここのページを参考にプロキシのようなサーバプログラムを
作成しております。
なにをするサーバかというと、ブラウザからのリクエストを受け取り
そのリクエスト(GET,POST)を他のWebサーバにそのまま送りつけ
そのWebサーバからのレスポンス(HTML)を受け取り、
ブラウザに返してやるサーバです。
このページのソースを参考にしています。
サーバ部分はecho-server.plを
httpリクエスト部分はhttp-client.plを利用しましたが、
Webサーバまではリクエストが行っているようですが
レスポンスがうまく表示されません。
なにか原因がわかりになるようでしたら
教えてください。

No. 731 # つる 2000/02/16 (水) 10:32
ソースはこんな感じです。

#!/usr/local/bin/perl -w

use Socket;

$server_port = 4989;

# ソケット生成
socket(CLIENT_WAITING,PF_INET,SOCK_STREAM,0)
                || die "ソケットを生成できません。$!";

# ソケットオプション設定
setsockopt(CLIENT_WAITING,SOL_SOCKET,SO_REUSEADDR,1)
                || die "setsockopt に失敗しました。$!";

# ソケットにアドレス(=名前)を割り付ける
bind(CLIENT_WAITING,sockaddr_in($server_port,INADDR_ANY))
                || die "bind に失敗しました。$!";

# ポートを見張る
listen(CLIENT_WAITING,SOMAXCONN)
                || die "listen: $!";

print "ポート $server_port を見張ります。\n";

# while(1)することで、1つの接続が終っても次の接続に備える
while (1){
                accept(CLIENT,CLIENT_WAITING);

                # HTTP プロトコルを使う
                $port = getservbyname('http','tcp');

                # ホスト名を、IP アドレスの構造体に変換
                $iaddr = inet_aton("xxx.xxx.xxx.xxx")
                                || die "[xxx.xxx.xxx.xxx]は存在しないホストです。\n";

                # ポート番号と IP アドレスを構造体に変換
                $sock_addr = sockaddr_in($port,$iaddr);

                # ソケット生成
                socket(SERVER,PF_INET,SOCK_STREAM,0)
                                || die "ソケットを生成できません。\n";

                # 指定のホストの指定のポートに接続
                connect(SERVER,$sock_addr)
                                || die "[xxx.xxx.xxx.xxx]のポート$portに接続できません。\n";

                # ファイルハンドル SERVER をバッファリングしない
                select(SERVER); $|=1; select(STDOUT);
                # クライアントに対してバッファリングしない
                select(CLIENT); $|=1; select(STDOUT);

                # WWWサーバにHTTPリクエストを送る
                while (<CLIENT>){
                                print SERVER "$_";
                                print "$_";
                }

                # ヘッダ部分を受け取る
                while (<SERVER>){
                                # 改行のみの行ならループを抜ける
                                m/^\r\n$/ && last;
                }

                # ボディ部分を受け取り、表示
                while (<SERVER>){
                                print CLIENT "$_";
                                print "$_";
                }
                close(CLIENT);
}

とりあえず、サンプルをそのまま利用させていただいております。
おかしいところがあればご指摘下さい。
perl初心者のものですみません。

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