QFtp Get never emits commandFinished()

1.5k Views Asked by At

I am currently facing a weird problem concerning QFtp. I want to download a bunch of files from a FTP server but when I come to some point, after downloading x files on y, the ftp->get() command is done, the file is filled, but there is no emission of the SIGNAL commandFinished() and thus it does not download the other files.

Here is my code :

void Ftp::commandFinished(int i, bool error)
{

    if(ftp->currentCommand() == QFtp::Get)
    {
        if(error)
        {
            //blablabla-ERROR-blablabla
        }
        currentFile->close();
        filesToDownload.pop_front();
        processFileList();
    }

    /**Gestion de la commande Login (authentification de l'utilisateur)
    */
    if(ftp->currentCommand() == QFtp::Login)
    {//not utile here}

    /**Gestion de la commande ConnectToHost (connexion au serveur)
    */
    if (ftp->currentCommand() == QFtp::ConnectToHost) 
    {//not utile here}

    /**Gestion de la commande List (téléchargement d'un fichier)
    */
    if(ftp->currentCommand() == QFtp::List)
    {
        if(error)
        {
            //Nananana-FAIL-nanana
        }

        //!Tri des fichiers à télécharger en fonction de leur dernière date de modification
        if (!filesToDownload.isEmpty())
        {
            currentPeripheral->setLastDownloadDate(newLastModifiedDate) ;
            std::sort(filesToDownload.begin(),filesToDownload.end(),compareQUrlInfos);
            processFileList();
        }



    }
}

void Ftp::processFileList()
{

QUrlInfo info;

if (filesToDownload.isEmpty())
{
    //!Suicide de l'instance de Ftp
    ftp->close();
    disconnect(this,0,0,0);
    this->deleteLater();
    return ;
}

info = filesToDownload.first();
QDir dlDir(QString::number(currentPeripheral->getId()));

//!Si un fichier a été téléchargé, on déclenche son traitement
if (currentFile != nullptr)
{
    emit(oneDownloadFinished(currentFile->fileName(),currentPeripheral));
    delete currentFile;
    currentFile = nullptr;
}

//!On crée un répertoire de téléchargement si nécessaire
if (!dlDir.exists())
{
    dlDir.mkdir(".");
}

//!on crée le fichier qui contiendra le téléchargement
currentFile = new QFile(dlDir.filePath(info.name()));

if(!currentFile->open(QIODevice::WriteOnly))
{
    delete currentFile;
    currentFile = nullptr;
    emit(writeToMonitoringConsole(QString("Erreur lors de la creation du fichier "+info.name()),"Error"));
    return;
}


//Here I start (sometimes) a never ending fail
ftp->get(info.name(), currentFile);
}

At first I thought it was because I was making too much request and that I was rejected because of that, but even with a Sleep(2000) it blocks. The blocking appears even more quickly. I usually can download around 30 files (when lucky 70, once I managed to have 200 !). With Sleep(2000) I barely succed to download 2-3 files.

Is it a mistake from me ? Is there a limitation in QFtp I didn't found ? Or something else ?

EDIT : I tested somes things since I posted it, and what was striking, when monitoring the dataTransferProgress() signal, is that the problematic file is fully downloaded (qDebug says "88928/88928") but I never enter commandFinished().

My slot commandFinished() is linked to my QFtp::commandFinished SIGNAL this way :

connect(ftp, SIGNAL(commandFinished(int,bool)), this, SLOT(commandFinished(int,bool)));
2

There are 2 best solutions below

1
On

I'm seeing pretty much the same thing, with an FTPWidget class I use never receiving a commandFinished for a GET request. The dataTransferProgress() signal reliably reports 100% downloaded and its all there.

I've hacked around using a setting a timeout as follows:

// Set up a single shot QTimer and connect it to a function to fix things

connect(m_lostFinishedTimer, SIGNAL(timeout()), this, SLOT(lostFinishedHack()));

// Start the timeout when the download data progress hits 100%

void FtpWidget::updateXferProgress(qint64 readBytes, qint64 totalBytes)
{
    m_progressBar->setValue(progress_value(readBytes, totalBytes));

    if (m_downloading && readBytes == totalBytes)
        m_lostFinishedTimer->start();
}

// And don't forget to stop the timer if you do get a finish:

void FtpWidget::commandFinished(int id, bool error)
{
    QFtp::Command cmd = m_ftp->currentCommand();
    // ...
    if (cmd == QFtp::Get) 
    {
        m_lostFinishedTimer->stop();
        //....
    }
    // ...
}

where the timer is connected to a fixup routine basically close the download file, reconnect the server, and move on to the next file

void FtpWidget::lostFinishedHack()
{
    if (!m_downloading)
        return;

    if (m_downloadFile)
    {
        DOUT("FtpWidget::lostFinshedHack() -- file: " << DS(m_downloadFile->fileName()) << "\n");
        m_downloadFile->close();
        delete m_downloadFile;
        m_downloadFile = 0;
    }

    // just reconnect the thing
    Connect(m_ftpServerName->text());

    downloadNextFile();
}

This seems to work well enough, though I'm using a rather larger timeout.

So a while after I got that work around in place, I went back and looked at what's going on inside the QFtp itself. Using the debugger to add a print command on a breakpoint I can see the raw replies from the. What seems to be happening is that the ftp server I'm using is sending its ftp replies in a different order sometime.

When it works I my trace looks like:

FtpWidget::commandStarted(id: 265) --  Get
Function: QFtpPrivate::_q_piFtpReply(int, const QString &), code: 200, text "Operation successful"
Function: QFtpPrivate::_q_piFtpReply(int, const QString &), code: 213, text "135980"
Function: QFtpPrivate::_q_piFtpReply(int, const QString &), code: 200, text "Operation successful"
Function: QFtpPrivate::_q_piFtpReply(int, const QString &), code: 150, text "Opening BINARY connection for 000-23Sep2014_103527.jpg (135980 bytes)"
Function: QFtpPrivate::_q_piFtpReply(int, const QString &), code: 226, text "Operation successful"
FtpWidget::commandFinished(id: 265, error: 0) -- Get

Where the FtpWidget::commandStarted and commandFinished are my slots, and the _q_piFtpReply() is a private part of the QFtp object where I've hook my tracing breakpoint.

When it fails I get:

FtpWidget::commandStarted(id: 256) --  Get
Function: QFtpPrivate::_q_piFtpReply(int, const QString &), code: 200, text "Operation successful"
Function: QFtpPrivate::_q_piFtpReply(int, const QString &), code: 213, text "135896"
Function: QFtpPrivate::_q_piFtpReply(int, const QString &), code: 200, text "Operation successful"
Function: QFtpPrivate::_q_piFtpReply(int, const QString &), code: 150, text "Opening BINARY connection for 000-23Sep2014_103525.jpg (135896 bytes)"
FtpWidget::lostFinshedHack()
FtpWidget::lostFinshedHack() -- file: C:/Users/sean/Temp/Snapshots5/000-23Sep2014_103525.jpg
FtpWidget::connect("ftp://[email protected]/")

Getting the 150 reply code after the 200 reply code seems to be the problem. So if you go look up the FTP reply codes and look through the qftp.cpp it looks having the 150 arrive after the 150 is causing the qftp implementation state machine to go into a perpetual wait. As far as I can tell its my ftp server that is messing things up not the qftp.cpp (maybe I should try wire shark to make sure).

For now I'm sticking to my timeout workaround for the problem.

1
On

I am too late to reply, but it still may help others.

In void Ftp::commandFinished(int i, bool error) it is always better to check for QFtp::None option also, since if your last command is get and when commandFinished() slot gets called then if (ftp->currentCommand() == QFtp::Get) will not be able to identify the last command, since your current command may not be get because it is already finished.

You can also check with id as if(ftp->currentCommand() == QFtp::Get || 1 == id)