TIdFTPServerで通信するには?

解決


迷子の子犬  2003-06-30 20:01:19  No: 3963

通常FTPサーバーはファイルをHDDに取り込むのですが
TIdFTPServerを使用してFTPクライアントから受け取ったファイルを
配列メモリに取り込みたいのですが
1.Delphiでのコードはどのように書けばよいのでしょうか?
2.イベントでメッセージを表示するとStoreイベントの後Exceptionが
    発生し(Connection Closed Gracefully)クライアントからの最終コマンド
    「Quit」に対する処理が正常に行われないのですが。
    Exceptionイベントを無視する方法はどうすればよいのでしょうか?
3.Connectイベント発生後、次のコマンド(イベントが発生しない場合)の
    タイムアウト等の設定は変更できるのでしょうか?
質問が多くてすみませんがよろしくお願いします.


にしの  2003-06-30 20:26:31  No: 3964

1.OnRetrieveFileイベントで、メモリに保存すればよいと思います。
2.Connection Closed Gracefullyの意味を間違えていらっしゃる。ヘルプを参照してください。無視する方法もかかれています。

3は調べ切れていません。


迷子の子犬  2003-06-30 20:38:06  No: 3965

回答ありがとうございます。

>1.OnRetrieveFileイベントで、メモリに保存すればよいと思います。
OnRetrieveFileイベントにメッセージを入れて表示しているのですが
表示されません.イベントを発生させるために
何らかの工夫がいるのでしょうか?

>2.Connection Closed Gracefullyの意味を間違えていらっしゃる。
私の認識では正常にクローズ処理がおこなわれたと言う意味だと思うのですが.
つまりエラーではなく、正常に処理がおわったということですが、
以下のように各イベントでメッセージをメモコンポーネントに
表示すると以下のようになり、クライアント側の「Quit」コマンドを
うけとることなく強制的にDisconnectしているのですが。

----------  以下メッセージ(Memoコンポーネントより) ----------
IdFTPServer1BeforeCommandHandler(PORT 192,168,001,002,054,192)
IdFTPServer1AfterCommandHandler
IdFTPServer1BeforeCommandHandler(TYPE I)
IdFTPServer1AfterCommandHandler
IdFTPServer1BeforeCommandHandler(STOR SAMPLE.DAT)
IdFTPServer1StoreFile[SAMPLE.DAT]
IdFTPServer1AfterCommandHandler
IdFTPServer1Exception(Connection Closed Gracefully.)
IdFTPServer1Disconnect


にしの  2003-06-30 21:19:22  No: 3966

STORコマンドでアップロードするならば、OnStoreFileイベントですね。
# RETRコマンドでアップロードした場合、OnRetrieveFileイベントが発生します
手元にあるFTPクライアントで、RETRコマンドを使用していたのでそれしか確認していませんでした。

> 私の認識では正常にクローズ処理がおこなわれたと言う意味だと思うのですが.
> つまりエラーではなく、正常に処理がおわったということですが、
> 以下のように各イベントでメッセージをメモコンポーネントに
> 表示すると以下のようになり、クライアント側の「Quit」コマンドを
> うけとることなく強制的にDisconnectしているのですが。
例外処理はどのようにしていますか?
通常は、例外処理はエラーとして書くので、正常処理の場合を切り分けてやらないとDisconnectされてしまいます。


迷子の子犬  2003-06-30 21:28:29  No: 3967

正常処理させる方法がわかりません。
AException.Message に空('')のデータをいれればよいのかと
思ったのですが処理できないようです。
また、AException.Freeを行うと今度は間違いなくエラーが発生します。

それから、StorイベントにおいてVStreamがNilで
データが渡ってきません。
データを取得するには『イベントハンドラに手続きを割り当てる』と
言うことですが、具体的にどうすればよいのかもわからない状態です。

何分初心者なものでよろしくお願いします.


にしの  2003-06-30 21:43:19  No: 3968

OnStoreFilesのVStreamに、TStream系のオブジェクトを作成してください。

例えば、
  VStream := TFileStream.Create(ExtractFilePath(Application.ExeName) + AFileName, fmCreate);

というように。
OnStoreFilesは、VStreamが変えてくることを期待しています。


にしの  2003-06-30 21:43:48  No: 3969

間違えました。
VStreamが返ってくることを期待しています。


迷子の子犬  2003-07-01 02:57:49  No: 3970

>例えば、
>  VStream := TFileStream.Create(ExtractFilePath(Application.ExeName) + AFileName, fmCreate);

これをそのまま使用すると、カレントディレクトリにファイルが作成され、
『Connection Closed Gracefully.』の例外も発生しなくなりました。

あれからずっと、上記を応用してメモリ配列
GetData[1..100][0..999] of Byte;
に、各クライアント(100台)から1秒ごとに送られてくる
1000バイトの固定データをメモリに取得しようとしていますが、
結局方法がわかりませんでした。
以下の方法でデータの受信ができる場合もあるが
ほとんどが通信エラー状態(パケットモニターでは各コマンドでリトライが多くなっている)になって
データを受け取ることができなくなります.
多分、考え方が間違っているのだと思います。よろしくお願いします.

Type
  TGetStream = class(TMemoryStream)
  private
    McNum: Integer;
  public
    procedure BeforeDestruction; override;
    procedure SetPointer(Ptr: Pointer; Size: Longint);
  end;

var
  GetData: array [0..999] of Byte;
  GetMemory:    array [1..100, 0..999] of Byte;

Stor イベント
      Wk_Stream := TGetStream.Create;
      Wk_Stream.McNum := McNum;
      Wk_Stream.SetPointer(@GetData, SizeOf(GetData));
      VStream := Wk_Stream;

procedure TGetStream.BeforeDestruction;
var
  i:     Integer;
  McNum: Integer;
begin
  // 受け取ったデータの先頭4バイトはクライアントの管理番号が入っている
  // 本来なら、Wk_Stream.McNumを使用したいのだが...
  McNum := GetData[0] shl 24 + GetData[1] shl 16 + GetData[2] shl 8 + GetData[3];
  if (McNum >= 1) and (McNum <= 100) then begin
    for i := 0 to 999 do GetMemory[McNum][i] := GetData[i];
  end;
  inherited;
end;


にしの  2003-07-01 17:30:04  No: 3971

BeforeDestructionは破棄寸前で呼ばれるものなので、AfterCommandHandlerの方がよろしいと思います。
このイベントがファイルを転送した後なのか、前なのかは未確認です。
最悪でも、VStreamを返すときにスレッドを作成し、VStreamに入れたオブジェクトに値が入ったかどうかを確認してやればOKです。


迷子の子犬  2003-07-01 17:58:04  No: 3972

AfterCommandHandlerの後ではVStreamにデータが
入ってこない場合がありました。
どうやら、このイベントの後にデータを受け取ることが
あるようです。データ受信部分は別スレッドなのかな?
と、すればブロッキングモードのようなものはないのでしょうか?

また、ソースコードは前記の BeforeDestruction の中身をそのまま AfterCommandHandler に移動したものです。
(もともとAfterCommandHandlerでデータ受信できない場合があったので
試行錯誤の末 BeforeDestruction に持っていったのですが。。。)

>VStreamを返すときにスレッドを作成し
>VStreamに入れたオブジェクトに値が入ったかどうかを確認
VStreamを返すときにイベント等が発生しないのでスレッドを
作成することができないのですが、また、『VStreamに入れたオブジェクト』
の『VStreamに入れた』というのはどういうことでしょうか?
またオブジェクトというのは前記のソースコードでの
GetData のことでしょうか?

わからないことばかりですみませんがよろしくお願いします.


にしの  2003-07-01 20:02:35  No: 3973

VSream云々のところは忘れてください。
試してみたら、OnStoreFilesイベントで返したTStreamは、使い終わると破棄していました。
BeforeDestructionでOKです。

SetPointerを使わずに、保存場所はそのまま自分自身(TGetStream)でOKです。
つまり、継承したら、BeforeDestructionだけオーバーライドします。
そして、SetPointerを使わずに、そのままTMemoryStreamが用意したバッファに保存させます。

BeforeDestructionで、自分自身のデータを別のところに待避します。

例えば、Form1に
MemStream: TMemoryStream
を用意し、オブジェクトを作成しておいてから、

procedure TGetStream.BeforeDestruction;
begin
  Form1.MemStream.LoadFromStream(Self);
  inherited BeforeDestruction;
end;

とすれば、TGetStreamが破棄されるときにMemStreamに保存されます。
# この場合は、スレッドのことを考えていませんので、複数同時接続には対応していません

Indyでは、各接続を、スレッドとして扱っています。
同時に複数のスレッドができることもあるので、このあたりは同期させるようにした方がよいです。


迷子の子犬  2003-07-03 03:47:28  No: 3974

現在3台のクライアントからデータを取得するようにしています。
数時間動作していますが、
現状ではデータ取得には問題が無いようです.
ただ、IdFTPServerには問題があるのですが
別の議題とします。

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


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加