PDA

View Full Version : Internet Speedometer



dilidpan
6th April 2009, 16:55
Hello,
I'm new in QT. I have to implement internet speedometer. The application must have the following properties:
1) To work synchronous - with threads;
2) To receive parameters: IP address of server, port and interval (ms), in which we make ping to the server;
3) Use Dial or progressBar for ping time connection status;
4) To watch for connection failure with announcement;
5) To gather information for connection and on request to give average results.

I made a review of QtcpSocket class and the examples relevant to it. I've made the .ui file. But I'm hard up for an answer to make the rest of an application. Can someone give me an advice in basic steps how to implement it.
Thank you in advance.

talk2amulya
6th April 2009, 20:13
thats a lot of problems..target them one by one....i cant imagine a coherent reply to that post..so whats the first problem?

wysota
6th April 2009, 21:15
I'd start with "why do want a synchronous solution?". Being synchronous makes your application much more complex without any benefits on its own. Unless that is an outside requirement I would change the design to using asynchronous calls.

dilidpan
7th April 2009, 08:42
I'll try to explain in details what I must do. I have big application, working with DB. When I have some request to this database, I would like to check the connection to the server, to be sure that I have a responce from the server or to understand where is the problem of slow connection.
So if you still think that I should do it asynchronous, I'm waitnig for your suggestions.

wysota
7th April 2009, 09:40
Yes, I think it should be asynchronous :) You start a call, you wait until you have a response (or not) and then you make a call to the database. Just a side note - if you really want to "ping" the server, getting no response won't mean there is no connection - the pings might be blocked. The best option would be to try to connect to the database regardless of the reachability of the remote site. The connection will simply fail (QSqlDatabase::open will return false).

dilidpan
7th April 2009, 10:40
Thanks for the help. While I was waiting for your answer, I tried to make the app synchronous. And now I have a problem while executing it.When I push the Start button in a few seconds later I receive the following message: "The following error occurred: Socket operation timeout."

I will post here the source code:

1.speedometerthread.h

#ifndef SPEEDOMETERTHREAD_H
#define SPEEDOMETERTHREAD_H

#include <QThread>
#include <QMutex>
#include <QWaitCondition>

class SpeedometerThread : public QThread
{
Q_OBJECT

public:
SpeedometerThread(QObject *parent = 0);
~SpeedometerThread();

void requestNewSpeedometer(const QString &hostName, quint16 port); // to request a new speedometer,
//and the result is delivered by the
//newSpeedometer() signal
void run();

signals:
void newSpeedometer(const QString &speedometer); //The reault of invoking the requestNewSpeedometer()
void error(int socketError, const QString &message); //If any error occurs, the error() signal is emitted.

private:
QString hostName; //requestNewSpeedometer() is called from the main, GUI thread, but the host name and port
quint16 port; //values it stores will be accessed from SpeedometerThread's thread.
QMutex mutex; // Because we will be reading and writing FortuneThread's data members from different threads
//concurrently, we use QMutex to synchronize access.
QWaitCondition cond;
bool quit;
};

#endif // SPEEDOMETERTHREAD_H


2. speedometerthread.cpp

#include <QtNetwork>

#include "speedometerthread.h"

SpeedometerThread::SpeedometerThread(QObject *parent)
: QThread(parent), quit(false)
{
}

SpeedometerThread::~SpeedometerThread()
{
mutex.lock();
quit = true; // sets quit to true
cond.wakeOne(); // wakes up the thread
mutex.unlock();
wait(); //waits for the thread to exit before returning. This lets the while loop in run() will finish
// its current iteration. When run() returns, the thread will terminate and be destroyed.
}

void SpeedometerThread::requestNewSpeedometer(const QString &hostName, quint16 port)
{
QMutexLocker locker(&mutex); // stores the host name and port of the fortune server as member data, and we
this->hostName = hostName; // lock the mutex with QMutexLocker to protect this data. We then start the
this->port = port; //thread, unless it is already running.
if (!isRunning())
start();
else
cond.wakeOne(); // because the thread falls asleep waiting for a new request, we needed to wake it up
// again when a new request arrives. QWaitCondition is often used in threads to
// signal a wakeup call like this.
}

void SpeedometerThread::run()
{
mutex.lock(); // acquiring the mutex lock
QString serverName = hostName; // fetching the host name and port from the member data
quint16 serverPort = port;
mutex.unlock(); // releasing the lock again

while (!quit)
{
const int Timeout = 5 * 1000; // (in ms)

QTcpSocket socket; //creating a QTcpSocket on the stack
socket.connectToHost(serverName, serverPort); // starts an asynchronous operation which, after control
// returns to Qt's event loop, will cause QTcpSocket
// to emit connected() or error().
if (!socket.waitForConnected(Timeout)) //since we are running in a non-GUI thread, we do not have to worry
{ // about blocking the user interface. So instead of entering an event loop, we simply call
// QTcpSocket::waitForConnected(). This function will wait, blocking the calling thread,
//until QTcpSocket emits connected() or an error occurs. If connected() is emitted, the function returns
// true; if the connection failed or timed out (which in this example happens after 5 seconds),
// false is returned. QTcpSocket::waitForConnected(), like the other waitFor...() functions, is part of
//QTcpSocket's blocking API.
emit error(socket.error(), socket.errorString());
return;
}

while (socket.bytesAvailable() < (int)sizeof(quint16))
{ // we have a connected socket to work with. Now it's time to see what the fortune server has sent us.
if (!socket.waitForReadyRead(Timeout)) // read the size of the packet. Although we are only reading
{ // two bytes here, and the while loop may seem to overdo it, waiting for data using
// QTcpSocket::waitForReadyRead(). For as long as we still need more data, we call waitForReadyRead().
// If it returns false, we abort the operation. After this statement, we know that we
//have received enough data.
emit error(socket.error(), socket.errorString());
return;
}
}

quint16 blockSize;
QDataStream in(&socket); // create a QDataStream object, passing the socket to QDataStream's constructor,
in.setVersion(QDataStream::Qt_4_0); // and set the stream protocol version to QDataStream::Qt_4_0, and read
in >> blockSize; // the size of the packet.

while (socket.bytesAvailable() < blockSize)
{ //a loop that waits for more data by calling QTcpSocket::waitForReadyRead(). In this loop, we're waiting
// until QTcpSocket::bytesAvailable() returns the full packet size.
if (!socket.waitForReadyRead(Timeout))
{
emit error(socket.error(), socket.errorString());
return;
}
}

mutex.lock();
QString speedometer;
in >> speedometer; //use QDataStream to read the fortune string from the packet. The resulting
emit newSpeedometer(speedometer); // speedometer is delivered by emitting newSpeedometer().

cond.wait(&mutex); // acquire the mutex so that we can safely read from our member data.
serverName = hostName; // We then let the thread go to sleep by calling QWaitCondition::wait().
serverPort = port;
mutex.unlock();
}
}

dilidpan
7th April 2009, 10:41
3. speedometerctrlform.h

#ifndef SPEEDOMETERCTRLFORM_H
#define SPEEDOMETERCTRLFORM_H

#include <QtGui/QWidget>

#include "speedometerthread.h"

namespace Ui
{
class speedometerCtrlFormClass;
}

class speedometerCtrlForm : public QWidget
{
Q_OBJECT

public:
speedometerCtrlForm(QWidget *parent = 0);
~speedometerCtrlForm();

private slots:
void requestNewSpeedometer();
void showSpeedometer(const QString &speedometer);
void displayError(int socketError, const QString &message);
void displayAverageValues(void);

private:
Ui::speedometerCtrlFormClass *ui;
SpeedometerThread thread;
QString currentSpeedometer;
};

#endif // SPEEDOMETERCTRLFORM_H


4. speedometerctrlform.cpp

#include "speedometerctrlform.h"
#include "ui_speedometerctrlform.h"

#include <QtGui>
#include <QtNetwork>

speedometerCtrlForm::speedometerCtrlForm(QWidget *parent)
: QWidget(parent), ui(new Ui::speedometerCtrlFormClass)
{
ui->setupUi(this);

connect(ui->startCtrlFormBtn, SIGNAL(clicked()),
this, SLOT(requestNewSpeedometer()));
connect(ui->CtrlFormAverageValuesBtn, SIGNAL(clicked()),
this, SLOT(displayAverageValues()));

connect(&thread, SIGNAL(newSpeedometer(const QString &)),
this, SLOT(showSpeedometer(const QString &)));
connect(&thread, SIGNAL(error(int, const QString &)),
this, SLOT(displayError(int, const QString &)));

}

speedometerCtrlForm::~speedometerCtrlForm()
{
delete ui;
}

void speedometerCtrlForm::requestNewSpeedometer()
{
ui->startCtrlFormBtn->setEnabled(false); // enables the button
thread.requestNewSpeedometer("www.abv.bg", //hostLineEdit->text(),
8080);//portLineEdit->text().toInt());
}

void speedometerCtrlForm::showSpeedometer(const QString &nextSpeedometer)
{
if (nextSpeedometer == currentSpeedometer)
{
requestNewSpeedometer();
return;
}

currentSpeedometer = nextSpeedometer;
//ui->statusLabel->setText(currentSpeedometer);
ui->startCtrlFormBtn->setEnabled(true);
}

void speedometerCtrlForm::displayError(int socketError, const QString &message)
{
switch (socketError) {
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(this, tr("Speedometer - Control Form"),
tr("The host was not found. Please check the "
"host and port settings."));
break;
case QAbstractSocket::ConnectionRefusedError:
QMessageBox::information(this, tr("Speedometer - Control Form"),
tr("The connection was refused by the peer. "
"Make sure the speedometer server is running, "
"and check that the host name and port "
"settings are correct."));
break;
default:
QMessageBox::information(this, tr("Speedometer - Control Form"),
tr("The following error occurred: %1.")
.arg(message));
}

ui->startCtrlFormBtn->setEnabled(true);
}

void speedometerCtrlForm::displayAverageValues(void)
{
}


5. main.cpp

#include <QtGui/QApplication>
#include "speedometerctrlform.h"

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


Where am I wrong?

wysota
7th April 2009, 10:56
The socket didn't manage to complete the requested operation within the time frame you gave it. By the way, you don't need that mutex there. At least not it its current form. You can use QReadLocker and QWriteLocker or better yet change the way you pass data to the thread and get rid of the mutex.

wysota
7th April 2009, 11:15
Here is something quick, dirty and working (and asynchronous:cool:).

dilidpan
7th April 2009, 15:11
Thanks a lot.

raedbenz
23rd May 2010, 16:50
Here is something quick, dirty and working (and asynchronous:cool:).

hello,

i tried to comoiple your main.cpp app. and i get many errors, and the first one is QtNetwork not found. any hints? how the .pro file should be??

Thanks a lot