FTP Hanging

Giganews Newsgroups
Subject: FTP Hanging
Posted by:  Pierre Roux (pier…@openwater.co.za)
Date: Thu, 13 Jul 2006

I have an application that retrieves remote files on a six-hourly base.
Every once in a while (up to two days), it would just hang during the FTP
download process.  I have managed to track the hang down to this section of
code in IdSimpleServer.pas:

    with Binding do begin
      if FAbortedRequested = False then begin
        while (FAbortedRequested = False) and (Result = False) do begin
          Result := Readable(AcceptWait);
        end;
      end;

The while loop never exits.

As it is exceptionally difficult to replicate this problem, I have not
managed to find out where in the FTP object the problem originates, but I
suspect its the 'GET'.  This problem does not occur when FTP.Passive :=
True, but then I have issues with my firewall.

This is my actual code below.  It is a recursive fuction that downloads a
directory structure establishing an FTP connection per directory.

function TMain.FetchDirNoLclTree(sLocalDir, sRemoteDir: String;
  FTP: TIdFTP): Boolean;
var
  iRetries,
iCount  : Integer;
  RemoteFTP : TIdFTP;

  bDownload,
  bDelete  : Boolean;
begin
RemoteFTP    := TIdFTP.Create;

  RemoteFTP.Username  := FTP.Username;
  RemoteFTP.Password  := FTP.Password;
  RemoteFTP.Host    := FTP.Host;
RemoteFTP.TransferTimeout := FTP.TransferTimeout;

  iRetries := 0;
  while not RemoteFTP.Connected and (iRetries < FTPRETRIES) do
  begin
    try
    RemoteFTP.Connect;
    except
        On E:Exception do
        begin
          Log(mtError, 'FTP WST Connect Failed: ' + FTP.Username + ' - '
+E.Message + ' Remote:' + sRemoteDir);
          Inc(iRetries);
          end;
    end;
  end;

  try
  if RemoteFTP.Connected then
  begin
    Result := True;

    RemoteFTP.Passive := FTP.Passive;

    iRetries := 1;

    While (RemoteFTP.RetrieveCurrentDir <> sRemoteDir) and (iRetries <
FTPRETRIES) do
      begin

        try
        RemoteFTP.ChangeDir(sRemoteDir);
        except
        On E:Exception do
            Log (mtWarning, 'FTP Change Dir to ' + sRemoteDir + ' failed.');
        end; // List

        if not DirectoryExists(sLocalDir) then
            CreateDir(sLocalDir);

        try
        RemoteFTP.List;
        except
        On E:Exception do
            Log (mtWarning, 'FTP List ' + sRemoteDir + ' failed.');
        end; // List

      Inc(iRetries);
      end;

    if RemoteFTP.RetrieveCurrentDir = sRemoteDir then
      begin

        for iCount := 0 to RemoteFTP.DirectoryListing.Count - 1 do
          begin
          pbProgress.Min  := 0;
          pbProgress.Max  := RemoteFTP.DirectoryListing.Count - 1;
          pbProgress.Position  := iCount;
          pbProgress.Update;

          if RemoteFTP.DirectoryListing.Items[iCount].ItemType =
ditDirectory then
              Result := FetchDirNoLclTree (sLocalDir,
sRemoteDir+'/'+RemoteFTP.DirectoryListing.Items[iCount].FileName, RemoteFTP)
          else
              begin
              bDownload  := False;
              bDelete  := False;

              if
LowerCase(ExtractFileExt(RemoteFTP.DirectoryListing.Items[iCount].FileName))
= '.log' then
                begin
                // Download if it doesn't exist
                if FileExists(sLocalDir + '\' +
RemoteFTP.DirectoryListing.Items[iCount].FileName) then
                    begin
                    // Download and Don't Delete if File Size Changed
                    if RemoteFTP.DirectoryListing.Items[iCount].Size <>
GetFileSize(sLocalDir + '\' +
RemoteFTP.DirectoryListing.Items[iCount].FileName) then
                      bDownload := True
                    else
                      begin
                      // Delete if old Remote Log File
                      if
RemoteFTP.DirectoryListing.Items[iCount].ModifiedDate <
IncDay(dtTrWSTStats, -7) then
                          bDelete := True;
                      end;

                    end
                else
                    bDownload := True;

                if bDownload then
                    begin
                    iRetries := 1;

                    while (iRetries < FTPRETRIES) and (iRetries <> -1) do
                      begin
                      try
                      Log (mtInformation, 'Downloading: ' + sRemoteDir +
'/' + RemoteFTP.DirectoryListing.Items[iCount].FileName);
                      RemoteFTP.Get(sRemoteDir + '/' +
RemoteFTP.DirectoryListing.Items[iCount].FileName, sLocalDir + '\' +
RemoteFTP.DirectoryListing.Items[iCount].FileName, True, False);
                      iRetries := -1;
                      except
                          On E:Exception do
                            begin
                            if iRetries < FTPRETRIES then
                                Inc(iRetries)
                            else
                                Result := False;
                            Log(mtWarning, 'FTP WST Get - Try
'+IntToStr(iRetries)+' : (' +sLocalDir+ '), ('+ sRemoteDir + ') '
+E.Message);
                            end;
                      end; // except
                      end;
                    end;

                if bDelete then
                    begin
                    try
                    Log (mtInformation, 'Removing: ' + sRemoteDir + '/' +
RemoteFTP.DirectoryListing.Items[iCount].FileName);
                    RemoteFTP.Delete(sRemoteDir + '/' +
RemoteFTP.DirectoryListing.Items[iCount].FileName);
                    except
                      On E:Exception do
                          Log(mtWarning, 'FTP WST Could not remove remote
file: '+ sRemoteDir + ' - ' +E.Message);
                    end; // except
                    end;

                // Keep 'Parent' Alive
                try
                FTP.Noop;

                except
                    On E:Exception do
                      Log(mtWarning, 'FTP NOOP : (' +sLocalDir+ '), ('+
sRemoteDir + ') ' +E.Message);
                end;

                // Keep Application Responding
                Application.ProcessMessages;

                end;
              end;
          Update;

          end;
        end; // Remote = Actual Changed Dir
    pbProgress.Position  := pbProgress.Min;
    if RemoteFTP.Connected then
      RemoteFTP.Disconnect
    else
        Log(mtWarning, 'FTP Prematurely Disconnected: (' +sLocalDir+ ')');

  end
  else
  Result := False;
  except
  On E:Exception do
      begin
        Log(mtError, 'FTP Error:' + E.Message);
        Result := False;
        end;
  end;
  FreeAndNil(RemoteFTP);
end;

Replies