通常FTPサーバーはファイルをHDDに取り込むのですが
TIdFTPServerを使用してFTPクライアントから受け取ったファイルを
配列メモリに取り込みたいのですが
1.Delphiでのコードはどのように書けばよいのでしょうか?
2.イベントでメッセージを表示するとStoreイベントの後Exceptionが
発生し(Connection Closed Gracefully)クライアントからの最終コマンド
「Quit」に対する処理が正常に行われないのですが。
Exceptionイベントを無視する方法はどうすればよいのでしょうか?
3.Connectイベント発生後、次のコマンド(イベントが発生しない場合)の
タイムアウト等の設定は変更できるのでしょうか?
質問が多くてすみませんがよろしくお願いします.
1.OnRetrieveFileイベントで、メモリに保存すればよいと思います。
2.Connection Closed Gracefullyの意味を間違えていらっしゃる。ヘルプを参照してください。無視する方法もかかれています。
3は調べ切れていません。
回答ありがとうございます。
>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
STORコマンドでアップロードするならば、OnStoreFileイベントですね。
# RETRコマンドでアップロードした場合、OnRetrieveFileイベントが発生します
手元にあるFTPクライアントで、RETRコマンドを使用していたのでそれしか確認していませんでした。
> 私の認識では正常にクローズ処理がおこなわれたと言う意味だと思うのですが.
> つまりエラーではなく、正常に処理がおわったということですが、
> 以下のように各イベントでメッセージをメモコンポーネントに
> 表示すると以下のようになり、クライアント側の「Quit」コマンドを
> うけとることなく強制的にDisconnectしているのですが。
例外処理はどのようにしていますか?
通常は、例外処理はエラーとして書くので、正常処理の場合を切り分けてやらないとDisconnectされてしまいます。
正常処理させる方法がわかりません。
AException.Message に空('')のデータをいれればよいのかと
思ったのですが処理できないようです。
また、AException.Freeを行うと今度は間違いなくエラーが発生します。
それから、StorイベントにおいてVStreamがNilで
データが渡ってきません。
データを取得するには『イベントハンドラに手続きを割り当てる』と
言うことですが、具体的にどうすればよいのかもわからない状態です。
何分初心者なものでよろしくお願いします.
OnStoreFilesのVStreamに、TStream系のオブジェクトを作成してください。
例えば、
VStream := TFileStream.Create(ExtractFilePath(Application.ExeName) + AFileName, fmCreate);
というように。
OnStoreFilesは、VStreamが変えてくることを期待しています。
間違えました。
VStreamが返ってくることを期待しています。
>例えば、
> 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;
BeforeDestructionは破棄寸前で呼ばれるものなので、AfterCommandHandlerの方がよろしいと思います。
このイベントがファイルを転送した後なのか、前なのかは未確認です。
最悪でも、VStreamを返すときにスレッドを作成し、VStreamに入れたオブジェクトに値が入ったかどうかを確認してやればOKです。
AfterCommandHandlerの後ではVStreamにデータが
入ってこない場合がありました。
どうやら、このイベントの後にデータを受け取ることが
あるようです。データ受信部分は別スレッドなのかな?
と、すればブロッキングモードのようなものはないのでしょうか?
また、ソースコードは前記の BeforeDestruction の中身をそのまま AfterCommandHandler に移動したものです。
(もともとAfterCommandHandlerでデータ受信できない場合があったので
試行錯誤の末 BeforeDestruction に持っていったのですが。。。)
>VStreamを返すときにスレッドを作成し
>VStreamに入れたオブジェクトに値が入ったかどうかを確認
VStreamを返すときにイベント等が発生しないのでスレッドを
作成することができないのですが、また、『VStreamに入れたオブジェクト』
の『VStreamに入れた』というのはどういうことでしょうか?
またオブジェクトというのは前記のソースコードでの
GetData のことでしょうか?
わからないことばかりですみませんがよろしくお願いします.
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では、各接続を、スレッドとして扱っています。
同時に複数のスレッドができることもあるので、このあたりは同期させるようにした方がよいです。
現在3台のクライアントからデータを取得するようにしています。
数時間動作していますが、
現状ではデータ取得には問題が無いようです.
ただ、IdFTPServerには問題があるのですが
別の議題とします。
ありがとうございました.
ツイート | ![]() |