QT client doesn't download file when action is placed on button - c++

Code in main.cpp
Client c;
c.start("ip-address", port, "final folder/file");
It works and client downloads file I need.
But if I place same code in mainwindow.cpp on button click action
void MainWindow::on_btn_connect_clicked()
{
Client c;
c.start("ip-address", port, "final folder/file");
}
it doesn't work. Why is that? I'm newbie at Qt and network coding. Some source code
void Client::start(QString address, qint16 port, QString file)
{
qDebug() << "client started";
QHostAddress addr(address);
filename = file;
client->connectToHost(addr, port);
qDebug() << client->socketDescriptor();
}
I have readyRead() but programm doesn't get there.
Client::Client(QObject *parent) :
QObject(parent)
{
client = new QTcpSocket(this);
client->abort();
connect(client, SIGNAL(readyRead()), this, SLOT(ReadData()));
connect(client, SIGNAL(disconnected()), this, SLOT(Completed()));
}

Did you connect the buttons clicked signal to the on_btn_connect slot?

When you create the Client like this:
void MainWindow::on_btn_connect_clicked()
{
Client c;
c.start("ip-address", port, "final folder/file");
}
The client object is created, start is called, then it is immediately destroyed one execution leaves the on_btn_conenct_clicked() function. This will never work unless the "start" function is completely finished doing whatever you want it to do by the time you leave it, which does not appear to be the case by the code you posted.
For this to work, you'd have to make client a member of the MainWindow class so it sticks around after the slot is called. Another alternative would be to make it a pointer, create it in the on_btn_connect_clicked() slot, then connect whatever signal gets emitted when its all done with its work to its own deleteLater slot. Like so:
void MainWindow::on_btn_connect_clicked()
{
Client* c = new Client(this);
connect(c, &Client::downloadComplete, c, &Client::deleteLater);
c->start("ip-address", port, "final folder/file");
}
The downloadComplete signal is something that you have to make sure you emit yourself whenever the action you are trying to accomplish is completed, otherwise you'll have a memory leak and the client object will not get deleted until the entire MainWindow is deleted.

Related

How to properly create QUdpSocket on non-gui thread ? Readyread not emitted

I'm writing a network library that wraps the QUdpSocket:
QAbstractSocket *UdpNetworkStreamer::addConnection()
{
QUdpSocket *udpSocket = new QUdpSocket(this);
udpSocket->bind(connection.port, QUdpSocket::ShareAddress);
bool ret = udpSocket->joinMulticastGroup(QHostAddress(connection.ip));
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::QueuedConnection);
return udpSocket;
}
create a new QUdpSocket.
connect to its readyRead signal.
call readDatagram when readyRead is raised.
All is working fine when I use the library from a Qt GUI application.
The problem starts when another user includes the library used outside of a Qt GUI application.
He calls the addConnection (which creates the socket and calls connect on the readyRead)
The thread on which the addConnection is called is non-Qt.
The addConnection seems to end successfully but the readyRead is never emitted.
Calling read (even though no readyRead was emitted) leads to a successful datagram read.
Fixes that did not work :
moving the the UDP socket thread to the this->thread
QUdpSocket *udpSocket = new QUdpSocket();
udpSocket->moveToThread(this->thread());
udpSocket->setParent(this);
I tried to simulate the problem by calling:void
MainWindow::on__btnOpenMulticastReceiver_clicked()
{
QFuture<void> future = QtConcurrent::run(this,
&MainWindow::CreateMulticastConnection, testHandle);
}
This also led to same symptoms as the one the user had with my library, meaning the readyRead wasn't emitted.
QSignalSpy - I've activated a spy on the readyRead signal; the counter kept on being zero although I could read data directly from the socket. The spy gave valid results (i.e. progressed) when used the socket was initialized on the main thread.
My Questions:
What am I missing and doing wrong ?
What is the simplest way of having the readyRead emitted even though it is not created on the main GUI thread - I couldn't find any sample that works with no GUI or outside Qt threads.
I ended up solving the problem this way :
void MainWindow::OpenConnection()
{
QThread *t = new QThread();
t->start();
SocketWrapper *w= new SocketWrapper();
w->moveToThread(t);
w->metaObject()->invokeMethod(w, "CreateSocket", Qt::QueuedConnection);
}
You must call invokeMethod() with the thread the socket wrapper was movedTo() upon creation of the socket, so that the thread that creates the socket will have a running event loop.
In addition to that, the CreateSocket() needs to be a slot in the SocketWrapper, something like that :
class SocketWrapper : public QObject
{
Q_OBJECT
public:
explicit SocketWrapper(QObject *parent = 0);
signals:
public slots:
void readyRead();
void CreateSocket();
private:
QUdpSocket *_socket;
};
My guess is that you need to add Q_OBJECT macro to the beginning of the class from where you need to emit the signal. Unless you do this, the signal-slot mechanism will not function properly.

QThread with slots and signals does not seem to create a new thread

I'm having some issues with Qt threading to allow the threaded part to update the GUI of my program. Seems like it's a known "problem" with Qt, so I found multiple tutorials, but I don't understand why my example here is not working.
I inherited from QThread as follow:
class CaptureThread: public QThread {
Q_OBJECT
public:
CaptureThread(const QObject *handler, QPushButton *start) {
CaptureThread::connect(start, SIGNAL(clicked()), this, SLOT(capture_loop()));
CaptureThread::connect(this, SIGNAL(sendPacket(Packet*)),
this, SLOT(receivePacket(Packet*)));
}
signals:
void sendPacket(Packet*);
public slots:
void capture_loop() {
Packet *packet;
while (my_condition) {
packet = Somewhere::getPacket();
//getPacket is define somewhere and is working fine
emit(sendPacket(packet));
std::cout << "Sending packet!" << std::endl;
}
}
};
And here is the CaptureHandler:
class CaptureHandler: public QWidget {
Q_OBJECT
public:
CaptureHandler() {
start = new QPushButton("Capture", this);
thread = new CaptureThread(this, start);
thread->start();
}
public slots:
void receivePacket(Packet *packet) {
std::cout << "Packet received!" << std::endl;
/*
Here playing with layout etc...
*/
}
private:
QPushButton *start;
CaptureThread *thread;
};
I think the signals and slots are ok, because it displays on the terminal
Sending packet!
Packet received!
Sending packet!
Packet received!
Sending packet!
Packet received!
But in the receivePacket slot, i'm trying to modify my GUI, and it does not work. The GUI just freeze, and all I can do is CTRL+C on terminal.
So i think my capture_loop, which is an infinite loop for the moment, is blocking the program, which means my thread has not started.
But I called thread->start().
I even tried to start the thread in CaptureThread constructor, by calling this->start(), but the result is the same.
Any idea?
Your using QThread wrong. By just creating the thread, it will not execute on the thread. you will need to do it inside the QThread::run function (by overriding it), since thats the only one that will run on the new thread. Please notice, that as soon as you return from this function, the thread will exit.
If you want to use your own loop inside the QThread::run function (instead using Qts default event loop), the thread won't be able to receive signals inside the run-function!
Here an example on how to use QThread:
class CaptureThread: public QThread {
Q_OBJECT
public:
CaptureThread(const QObject *handler, QPushButton *start) {
//calling "start" will automatically run the `run` function on the new thread
CaptureThread::connect(start, SIGNAL(clicked()), this, SLOT(start()));
//use queued connection, this way the slot will be executed on the handlers thread
CaptureThread::connect(this, SIGNAL(sendPacket(Packet*)),
handler, SLOT(receivePacket(Packet*)), Qt::QueuedConnection);
}
signals:
void sendPacket(Packet*);
protected:
void run() {
Packet *packet;
while (my_condition) {
packet = Somewhere::getPacket();
//getPacket is define somewhere and is working fine
emit sendPacket(packet) ;//emit is not a function
qDebug() << "Sending packet!";//you can use qDebug
}
}
};

How to deal with signals emitted before slots connected

I have a somewhat more complicated version of the following code:
void Foo::makeConnection(...) {
QTcpSocket * socket = new QTcpSocket(this);
// ...
socket->disconnect(this);
emit connectionAppeared(socket);
}
void Bar::baz() {
// ...
connect(foo, SIGNAL(connectionAppeared(QTcpSocket*)), this, SLOT(onConnectionAppeared(QTcpSocket*)));
foo->makeConnection(...);
}
void Bar::onConnectionAppeared(QTcpSocket * socket) {
if (!socket) { std::terminate(); }
socket->setParent(this);
connect(socket, SIGNAL(readyRead()), this, SLOT(readData()));
connect(socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
}
The Bar::baz asks Foo to make it a connected QTcpSocket, and after it is made, the socket's signals are connected to Bar's slots. But there is a problem with timing: the readyRead signal may very well be emitted befor it is connected to any slots, and thus is effectively lost. We "fixed" it like this:
void Bar::onConnectionAppeared(QTcpSocket * socket) {
// ... same code as before plus the following line:
emit socket->readyRead(); // Kick it to get started!
}
void Bar::readData() {
while (mSocket->bytesAvailable()) {
// ...
}
}
The problem of losing disconnected() signals is still there, but it doesn't manifests very often. Technically, I could emit disconnected() manually, and check socket->state() in the socketClosed() slot... but I just feel it's not the right way. I effectively just manually examine the current state instead of reacting to state transitions which I was the whole point of signal/slot machinery.
Are there nice ways to fix this kludge? I thought of passing signal/slot pairs to Foo::makeConnection(...) somehow, so they can be connected before socket->connectToHost() is called, but I don't know how, and I am not sure it's the good idea anyway.

Access QTcpSocket from another thread (child thread)

I have created a thread contains a QTcpServer which accepts any incoming connections:
void Service::run() //Service class is a subclass of QThread
{
server->listen(QHostAddress::LocalHost, ServicePortNo);
// server is a private member of Service
while(server->waitForNewConnection(-1)){
QTcpSocket *socket = server->nextPendingConnection();
handle(socket); // This is a pure virtual function
}
}
in handle(QTcpSocket *socket) :
// TimeDateService is subclass of Service
// implementation of pure virtual function handle()
void TimeDateService::handle(QTcpSocket *socket)
{
(new TimeDateSocketHandler(socket))->Start();
}
Note: TimeDateSocketHandler is a subclass of SocketHandler and SocketHandler itself is a subclass of QThread as shown in the following:
void SocketHandler::run()
{
if(!socket->waitForReadyRead(WAIT_TIMEOUT))
{
socket->disconnectFromHost();
socket->close();
return;
}
QByteArray request = socket->readAll();
QByteArray response = Serve(request); // Serve is a pure virtual function
socket->write(response);
socket->waitForBytesWritten(WAIT_TIMEOUT);
socket->disconnectFromHost();
socket->close();
}
And finally here is the TimeDateSocketHandler
QByteArray TimeDateSocketHandler::Serve(QByteArray request)
{
QByteArray response;
response.append(QTime::currentTime().toString().toUtf8());
response.append(QString(SEPARATOR).toUtf8());
response.append(QDate::currentDate().toString().toUtf8());
return response;
}
main function:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
TimeDateService timedateService;
//CalculatorService calculatorService;
//RemoteCMDService remoteCMDService;
timedateService.StartService();
//calculatorService.StartService();
//remoteCMDService.StartService();
return a.exec();
}
In the main function I started the TimeDateService. But when I connect to the server for retrieving time and date, server sends time and date but when the TimeDateSocketHandler wants to close the socket the program crashes:
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to
objects ow ned by a different thread. Current thread 3998779bf0.
Receiver '' (of type 'QNat iveSocketEngine') was created in thread
39985efcd0", file kernel\qcoreapplicatio n.cpp, line 494
Can anyone help me out please how can I fix this problem, Many Thanks
Your problem is this line:
(new TimeDateSocketHandler(socket))->Start();
The parent "socket" lives in the TimeDateService thread, but the child will be in the TimeDateocketHandler thread. Parent and children should be in the same thread when using the Qt event loop.
Here is the relevant part of the documentation:
Event filters are supported in all threads, with the restriction that the monitoring object must live in the same thread as the monitored object. Similarly, QCoreApplication::sendEvent() (unlike postEvent()) can only be used to dispatch events to objects living in the thread from which the function is called. Here is an example of that:
The solution is relatively straight-forward:
You could call the method directly with the invokeMethod method of QMetaObject. You would need to use queued connection to get the slot triggered in the separate thread.
QMetaObject::invokeMethod(new TimeDateSocketHandler(socket),
SLOT(Start()),
Qt::QueuedConnection);
or
Use signal and slots. This means emit a signal instead of direct invokation and then connect the other thread's corresponding slot.
TimeDateSocketHandler *timeDateSocketHandler = new TimeDateSocketHandler(socket);
connect(this, SIGNAL(socketHandled()), timeDateSocketHandler, SLOT(Start()));
emit socketHandled();
or
Use a smart pointer (like QSharedPointer) instead of raw pointer
Move to the socket handling into the other thread.

Why would deleting a Qt (QSslSocket) object cause a crash

I am really stumped and hoping somebody out there knows something about my problem.
I have a very simple SSL client and server. The connection is fine. Communication is fine. The problem arises when the client disconnects from the server. This fires a signal on the server which is handled in the SLOT error_handler(QAbstractSocket::SocketError in_error). In that function is where the sslSocket object has to be deleted, I would imagine.
However doing this causes the server to seg fault. I don’t understand what’s going on. I expected this to be really straightforward but apparently I am missing some Qt (or other) concept.
Can anybody help out?
Essential server code:
void SSLServer::incomingConnection(int sd)
{
sslSocket = new SSLSocket(this);
if( sslSocket->setSocketDescriptor(sd))
{
QFile sslkeyfile(privKey_);
sslSocket->setPrivateKey(QSslKey(sslkeyfile.readAll(),QSsl::Rsa));
QFile cliCertFile(serverCert_);
sslSocket->setLocalCertificate(QSslCertificate(cliCertFile.readAll()));
QFile certFile(caCert_);
sslSocket->addCaCertificate(QSslCertificate(certFile.readAll()));
sslSocket->setPeerVerifyMode(QSslSocket::VerifyPeer);
sslSocket->setProtocol(QSsl::SslV3);
connect(sslSocket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(error_handler(QAbstractSocket::SocketError)));
connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(ssl_error_handler(QList<QSslError>)));
connect(sslSocket, SIGNAL(encrypted()), this,
SLOT(ready()));
connect(sslSocket, SIGNAL(readyRead()), this,
SLOT(read_data_from_client()));
sslSocket->startServerEncryption();
if(!sslSocket->waitForEncrypted())
{
qDebug() << "failed to perform SSL handshake with client";
return;
}
}
}
void SSLServer::read_data_from_client()
{
QByteArray qstrbytes = sslSocket->readAll();
qDebug() << Q_FUNC_INFO << qstrbytes;
}
void SSLServer::ready()
{
QSslCertificate clientCert = sslSocket->peerCertificate();
qDebug() << clientCert.isValid();
}
void SSLServer::error_handler(QAbstractSocket::SocketError in_error)
{
qDebug() << Q_FUNC_INFO << in_error;
if(in_error == QAbstractSocket::RemoteHostClosedError)
{
delete sslSocket; //// line causes crash !!!!!!
}
}
Use QObject::deleteLater() instead of delete since QSslSocket inherits QObject. You may still receive messages on the socket which is causing the crash when you just delete the object.
sslSocket->deleteLater();
When you call deleteLater(), Qt automatically disconnects all slots and signals and calls the object destructor after there are no pending events being delivered to the object. See QObject::~QObject() for more information.
If you think how a QObject class such as SSLSocket class may be written, it could be something like this:-
class SSLSocket : public QObject
{
signals:
void sslErrors(QList<QSslError>);
void SomeFunction()
{
// something went wrong, emit error
emit sslErrors(errorList);
Cleanup(); // If a slot connected to sslErrors deleted this, what happens now?!
}
}
When the signal sslErrors is triggered, your slot function is called. As you can see, after emitting the signal, the class may have more work to do. If you immediately delete the object in your slot, this is going to crash, which is why you should always use deleteLater() for deleting QObject instances in slot functions.
The deleteLater function will ensure that the slot function has finished executing and the call stack restored, so it will be deleted at the appropriate time.
Note that the code above is not actually what SSLSocket does, but just an example.
QSslSocket is a QObject. Never just delete a QObject. For sure don't do this in a slot. Always use deleteLater().
Here's Qt example code using QSslSocket:
http://qt-project.org/doc/qt-4.8/network-securesocketclient-sslclient-cpp.html
As the other posters mentioned, use deleteLater(), and the error notification is not the only place to do so.