PDA

View Full Version : Networking problems while off-line



mtrpoland
26th September 2007, 17:28
Gentlemen,
I posted a huge problem some time ago and I assume it was too long.
I made some examinations and I am pretty sure that it has the same roots as the situation I describe below.

PROBLEM
Let us have a program that uses QFtp to download some file.
This applies to the program below:

#ifndef FTPGET_H
#define FTPGET_H

#include <QFile>
#include <QFtp>

class QUrl;

class FtpGet : public QObject
{
Q_OBJECT

public:
FtpGet(QObject *parent = 0);

bool getFile(const QUrl &url);

signals:
void done();

private slots:
void ftpDone(bool error);

private:
QFtp ftp;
QFile file;
};

#endif



#include <QtCore>
#include <QtNetwork>
#include <iostream>

#include "ftpget.h"

using namespace std;

FtpGet::FtpGet(QObject *parent)
: QObject(parent)
{
connect(&ftp, SIGNAL(done(bool)), this, SLOT(ftpDone(bool)));
}

bool FtpGet::getFile(const QUrl &url)
{
if (!url.isValid()) {
cerr << "Error: Invalid URL" << endl;
return false;
}

if (url.scheme() != "ftp") {
cerr << "Error: URL must start with 'ftp:'" << endl;
return false;
}

if (url.path().isEmpty()) {
cerr << "Error: URL has no path" << endl;
return false;
}

QString localFileName = QFileInfo(url.path()).fileName();
if (localFileName.isEmpty())
localFileName = "ftpget.out";

file.setFileName(localFileName);
if (!file.open(QIODevice::WriteOnly)) {
cerr << "Error: Cannot open " << qPrintable(file.fileName())
<< " for writing: " << qPrintable(file.errorString())
<< endl;
return false;
}

ftp.connectToHost(url.host(), url.port(21));
ftp.login();
ftp.get(url.path(), &file);
ftp.close();
return true;
}

void FtpGet::ftpDone(bool error)
{
if (error) {
cerr << "Error: " << qPrintable(ftp.errorString()) << endl;
} else {
cerr << "File downloaded as " << qPrintable(file.fileName())
<< endl;
}
file.close();
emit done();
}



#include <QtCore>
#include <iostream>

#include "ftpget.h"

using namespace std;

int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QStringList args = app.arguments();

if (args.count() != 2) {
cerr << "Usage: ftpget url" << endl
<< "Example:" << endl
<< " ftpget ftp://ftp.trolltech.com/mirrors" << endl;
return 1;
}

FtpGet getter;
if (!getter.getFile(QUrl(args[1])))
return 1;

QObject::connect(&getter, SIGNAL(done()), &app, SLOT(quit()));

return app.exec();
}


This is an example application from one of the QT devoted books.

It runs smoothly when the user is on-line.

The problem arises when the user has some connection problems.
Try to run the application while off-line.
The console becomes blocked and it will be until we connect ourselves to web or kill the process.

My question:
How to make our application quit with a certain warning for example after 10 seconds of unsuccessful trials to connect to the web?

PS. I tried to use QFtp::abort() with QTimer but it did not work! :(

marcel
26th September 2007, 17:55
You're not using QFtp properly. I assume the application blocks in QFtp::get.
The states of QFtp::connectToHost are HostLookup, Connecting and Connected. When offline the state will remain HostLookup. But you do not test for this(the stateChanged signal), you just go ahead and login(what I said about connectToHost also applies here) and then download the file.

The correct way is to examine all states and connect to all relevant signals in order to track possible connection errors.

wysota
26th September 2007, 17:56
Try connecting to the stateChanged() signal and reporting any changes through qDebug(). This will at least tell you what happens with the ftp object.

mtrpoland
26th September 2007, 19:34
You're not using QFtp properly. I assume the application blocks in QFtp::get.
No. The application blocks on HostLookup as I am offline!
The problem is that I want to break host lookup after 15 seconds of that state.

I tried something like that:

void MyWidget::connectionManager(int state) {
if(state == QFtp::HostLookup) {
qDebug("QFtp::HostLookup");
timer.setInterval(1000);
connect(&timer,SIGNAL(timeout()),this,SLOT(timerTimeOut())) ;
seconds = 0;
timer.start();
}
}
void MyWidget::timerTimeOut() {
++seconds;
if(seconds > 6) {
qDebug("Connecting stopped.");
ftp->abort();
ftp->close();
timer.stop();
}
}


where

connect(ftp,SIGNAL(stateChanged(int)),this,SLOT(co nnectionManager(int)));

but still I have a problem with quiting my application.
It freezes after clicking exit button and quits properly only if I plug the internet cable and thus revive connection.
This is why I suspect networking as a reason of the problem.

marcel
26th September 2007, 19:41
It freezes after clicking exit button and quits properly only if I plug the internet cable and thus revive connection.

Do you also abort the ftp in closeEvent?

Also, how many times do you get the HostLookup state?

mtrpoland
26th September 2007, 19:56
Do you also abort the ftp in closeEvent?

void MyWidget::closeEvent(QCloseEvent *event) {
ftp->abort();
}

Also, how many times do you get the HostLookup state?
Once

pdolbey
26th September 2007, 20:30
Just a thought but have tried using QNetworkInterface to determine if you have a network connection before trying to connect. Requires Qt 4.2 though.

Pete

mtrpoland
26th September 2007, 20:49
How to do this?
I looked at the documentation but couldn't sketch the code.
http://doc.trolltech.com/4.2/qnetworkinterface.html

I assume that QFtp::abort() is designed to work well as a tool for stopping the download. The whole issue is that I want to know how to order QFtp to stop looking for host as this blocks destructor of main application and I am frozen.

Moreover as far as I understand if QFtp fails in finding host it should signal done(true) and set error as QFtp::HostNotFound...

Is it a bug?

wysota
26th September 2007, 23:56
I don't understand one thing... Do you say that anything works incorrectly when you call abort()? Because I haven't seen you point out anything incorrect in QFtp behaviour... You call abort() and then what? It seems that you immediately call close() which schedules a disconnection and prevents done() from firing (see QFtp::done() docs for details) which prevents your application from closing. Close() will it turn do nothing as there is no live connection and again done() will probably not get emitted. Is it in any way different from what you expected?

mtrpoland
27th September 2007, 09:42
I have written a tiny program that presents solely the area of my concern.
Compile it and run it twice.
First with connection.
Second time disconnect yourself from internet before running the application and plug the cable after the application freezes.
main.cpp

#include "test.hpp"
#include <QApplication>
#include <QWidget>

int main(int argc, char * argv[]) {
QApplication app(argc, argv);
TestDialog widget;
widget.show();
return app.exec();
}


test.hpp

#ifndef TEST_H
#define TEST_H
#include <QDialog>
class QPushButton;
class QFtp;

class TestDialog : public QDialog {
Q_OBJECT
public:
TestDialog(QWidget *parent = 0);
~TestDialog();
public slots:
void done(bool);
private:
QPushButton *pbClose;
QFtp *ftp;
};
#endif


test.cpp

#include "test.hpp"
#include <QApplication>
#include <QtDebug>
#include <QPushButton>
#include <QFtp>
#include <QVBoxLayout>

TestDialog::TestDialog(QWidget *parent) : QDialog(parent) {
QVBoxLayout *qvb = new QVBoxLayout();
pbClose = new QPushButton("Close");
ftp = new QFtp(this);
qvb->addWidget(pbClose);
setLayout(qvb);
connect(pbClose,SIGNAL(clicked()),qApp,SLOT(quit() ));
connect(ftp,SIGNAL(done(bool)),this,SLOT(done(bool )));
ftp->connectToHost("ftp://ftp.mjakobczyk.pl");
}

TestDialog::~TestDialog() {
ftp->abort();
qDebug("Destructor");
}

void TestDialog::done(bool bl) {
qDebug() << "Done" << bl;
}


Thanks for support.
Mike

pdolbey
27th September 2007, 19:44
I tried your code, but never got any blocking behaviour unplugging the cable. However, here's a method I tried that did detect if I had any IPV4 addresses not equal to 127.0.0.1. I'm sure its not the most elegant solution, but its the type of thing I was alluding to earlier. I'm also sure the Qt gurus may have other comments, but its the best I can do for a 50 year old FORTRAN programmer.

This was using Qt4 4.3.1 under VS2005.

test.hpp


#ifndef TEST_H
#define TEST_H
#include <QDialog>
class QPushButton;
class QFtp;

class TestDialog : public QDialog {
Q_OBJECT
public:
TestDialog(QWidget *parent = 0);
~TestDialog();
bool onLine();
public slots:
void done(bool);
private:
QPushButton *pbClose;
QFtp *ftp;
};
#endif


test.cpp


#include "test.hpp"
#include <QApplication>
#include <QtDebug>
#include <QPushButton>
#include <QFtp>
#include <QVBoxLayout>
#include <QNetworkInterface>
#include <QHostAddress>

TestDialog::TestDialog(QWidget *parent) : QDialog(parent), ftp(0) {
QVBoxLayout *qvb = new QVBoxLayout();
pbClose = new QPushButton("Close");
qvb->addWidget(pbClose);
setLayout(qvb);
connect(pbClose,SIGNAL(clicked()),qApp,SLOT(quit() ));
if (onLine()) {
ftp = new QFtp(this);
connect(ftp,SIGNAL(done(bool)),this,SLOT(done(bool )));
ftp->connectToHost("ftp://ftp.mjakobczyk.pl");
} else {
pbClose->setText("No IP Address");
}

}

TestDialog::~TestDialog() {
if (ftp) {
ftp->abort();
}
qDebug("Destructor");
}

void TestDialog::done(bool bl) {
qDebug() << "Done" << bl;
}

bool TestDialog::onLine() {
QList<QHostAddress> ads = QNetworkInterface::allAddresses();
if (ads.size() > 0) {
for (int i = 0; i < ads.size(); i++) {
quint32 ipv4 = ads.at(i).toIPv4Address();
qDebug() << ipv4;
//Any address != loopback
if (ipv4 > 0 && ipv4 != 2130706433 /* == 127.0.0.1*/) {
return true;
}
}
}
return false;
}


Pete

mtrpoland
28th September 2007, 08:42
Thanks for the code.
I have discovered that my code works perfectly on windows xp, so the behavior I had described before occurs only on my ubuntu linux.

josepvr
19th November 2008, 23:00
I've the same problem in Kubuntu... have you tested in debian? occurs the same problem?

Thanks!