What I'm having is a strange problem in typical scenario: QTcpServer's method incomingConnection is overrided in custom class, and any received connection is planned for processing in separate thread on QThreadPool.
Server:
void FooS::incomingConnection(qintptr socketDescriptor)
{
QThreadPool *thread_pool = QThreadPool::globalInstance();
FooSocket *fs = new FooSocket();
fs->setSocket(socketDescriptor);
thread_pool->start(fs);
}
Task:
class FooSocket: public QObject, public QRunnable;
...
private slots:
void connectionIncomingData();
...
void FooSocket::run() {
QTcpSocket *socket = new QTcpSocket();
qDebug() << "SD: " << socketDescriptor; // is correct
if (!socket->setSocketDescriptor(socketDescriptor)) {
qDebug() << "Can't set socket descriptor";
emit error(socket->error());
return;
}
// -- had no effect here
// socket->moveToThread(QThread::currentThread());
connect(socket, SIGNAL(readyRead()), this, SLOT(connectionIncomingData()));
connect(socket, SIGNAL(disconnected()), this, SLOT(connectionClosed()));
}
readyRead signal doesn't gets triggered, but socket client is confirmed (tcpdump) to send data..
After making QRunnable to spawn a QThread object with socket logics inside, and toying with setAutoDelete, moveToThread - still no effect.
In order to process events in a QRunnable, a thread needs to have its own event loop, it mustn't rely on the one from the main thread. From what you've shown in your code, your thread quickly starts, then exits without running a loop.
Try adding
QEventLoop loop;
// connect a signal to the event loop's quit() slot
loop.exec();
Related
I have a class with a serial interface which transmit some data and emit a MessageReceived signal when some valid data are received:
Serial.c
void Serial::on_serialPort_readData()
{
... Other stuff...
else
{
qDebug() << "Received in serial";
emit MessageReceived(_mReceivedData);
}
}
}
And I have my main window which opens the serial connection and start the message transaction. But I have to receive a lot of data and the transmission will take a while, so I decided to use a QThread for the processing of the received data.
I created a thread and a worker in my mainwindow.cpp (code from here):
mainwindow.cpp
_mThread = new QThread(this);
_mThreadWorker = new Worker();
_mThreadWorker->moveToThread(_mThread);
// Like in the example
connect(_mThread, &QThread::started, _mThreadWorker, &Worker::Progress);
connect(_mThreadWorker, &Worker::Finished, _mThread, &QThread::quit);
connect(_mThreadWorker, &Worker::Finished, _mThreadWorker, &Worker::deleteLater);
connect(_mThread, &QThread::finished, _mThread, &QObject::deleteLater);
connect(_mProtocolInterface, &Serial::MessageReceived, _mThreadWorker, &Worker::UpdateData);
_mThread->start();
// Send the first packet so the receiver will answer
The serial interface will send the data in different packages and I read here that it is a bad idea to use the serial port in a seperate thread, so I decided to use the serial port in the main thread and connect the MessageReceived signal with an UpdateData slot of my worker:
worker.h
class Worker : public QObject
{
Q_OBJECT
...
public slots:
void Progress(void);
void UpdateData(QByteArray Data);
public:
Worker(QObject* parent = nullptr);
~Worker();
...
};
worker.cpp
void Worker::UpdateData(QByteArray Data)
{
qDebug() << "Received in Worker";
_mData = Data;
}
void Worker::Progress(void)
{
while(_mActive)
{
switch(_mState)
{
case STATE_INIT:
{
_mState = STATE_INIT;
// Just a idle to simulate the data transfer during debugging
break;
}
}
}
}
But only the slot on_serialPort_readData gets called after I receive the data from the receiver:
>> Received in serial
So why the slot UpdateData of the class Worker doesn´t get called?
Trying to implement a multi thread server in Qt, just consider this:
in SocketThread class:
SocketThread::SocketThread(qintptr descriptor, QObject *parent)
: QThread(parent), socketDescriptor(descriptor)
{
socket = new QTcpSocket();
socket->setSocketDescriptor(socketDescriptor);
socket->moveToThread(this);
connect(socket, &QTcpSocket::readyRead, [this]() { qDebug() << socket->readAll(); }); //trying to read messages from clients
}
in Connection class: creating thread in incomingConnection()
void Connection::incomingConnection(qintptr socketDescriptor)
{
SocketThread *socketThread = new SocketThread(socketDescriptor);
socketThread->start();
connect(socketThread, &SocketThread::started, [&]() { socketThread->socket->write("Hello!"); }); //write a message to client when thread is created
}
Strange thing is, if I add this line:
connect(socketThread, &SocketThread::started, & { socketThread->socket->write("Hello!"); }); //write a message to client when thread is created
Then the socket will not read message from client. If I remove that line, the socket will read message from client.
I want the server to send message to client when a thread is created and to read message from client as well. How can do solve the problem?
Edit:
SocketThread header file:
class SocketThread : public QThread
{
Q_OBJECT
public:
SocketThread(qintptr descriptor, QObject *parent = 0);
~SocketThread();
QTcpSocket *socket;
qintptr socketDescriptor;
};
in the Connection constructor:
Connection::Connection(QObject *parent) : QTcpServer(parent)
{
this->listen(QHostAddress::Any, 6666);
}
And creating a connection object in the main.cpp file. That's really like all the code.
I am having trouble to connect a Signal to a Slot in the following code:
#include "myserver.h"
MyServer::MyServer(QObject *parent) :
QTcpServer(parent)
{
}
void MyServer::StartServer()
{
if(listen(QHostAddress::Any, 45451))
{
qDebug() << "Server: started";
emit servComando("Server: started");
}
else
{
qDebug() << "Server: not started!";
emit servComando("Server: not started!");
}
}
void MyServer::incomingConnection(int handle)
{
emit servComando("server: incoming connection, make a client...");
// at the incoming connection, make a client
MyClient *client = new MyClient(this);
client->SetSocket(handle);
//clientes.append(client);
//clientes << client;
connect(client, SIGNAL(cliComando(const QString&)),this, SLOT(servProcesarComando(const QString&)));
// para probar
emit client->cliComando("prueba");
}
void MyServer::servProcesarComando(const QString& texto)
{
emit servComando(texto);
}
The emit client->cliComando("prueba"); works, but the real "emits" don't.
The console does not show any connection error, and the QDebug texts shows everything works well.
Original code was copied from http://www.bogotobogo.com/cplusplus/sockets_server_client_QT.php
I found the problem, Im sending a signal BEFORE connecting:
client->SetSocket(handle);
sends the signal, and Im CONNECTing after it... Now it is:
// at the incoming connection, make a client
MyClient *client = new MyClient(this);
connect(client, SIGNAL(cliComando(const QString&)),this, SLOT(servProcesarComando(const QString&)));
client->SetSocket(handle);
And it works. I noticed it after read the following:
13. Put all connect statements before functions calls that may fire their signals, to ensure that the connections are made before the signals are fired. For example:
_myObj = new MyClass();
connect(_myObj, SIGNAL(somethingHappend()), SLOT(doSomething()));
_myObj->init();
not
_myObj = new MyClass();
_myObj->init();
connect(_myObj, SIGNAL(somethingHappend()), SLOT(doSomething()));
I found it at https://samdutton.wordpress.com/2008/10/03/debugging-signals-and-slots-in-qt/
Anyway, thanks for your answers!
I know that this is next question about connect signal/slot mechanism between threads. I wrote working Worker application.
Main problem
I have worker class that has been moved to another thread. Second part of application is GUI interface with button. When I click button thread starts:
void MainWindow::startStopThreadA()
{
...
else
{
threadA = new QThread;
workerA = new WorkerObject;
workerA->setMessage("Thread A running");
workerA->moveToThread(threadA);
connect(threadA, SIGNAL(started()), workerA, SLOT(process()), Qt::QueuedConnection);
connect(workerA, SIGNAL(finished()), threadA, SLOT(quit()));
connect(workerA, SIGNAL(finished()), workerA, SLOT(deleteLater()));
connect(threadA, SIGNAL(finished()), threadA, SLOT(deleteLater()));
//Connect signal from thread with slot from MainWindow
connect(workerA, SIGNAL(printMessage(QString)), this, SLOT(printMessage(QString)), Qt::QueuedConnection);
threadA->start();
ui->threadAButton->setText("Stop A");
}
}
When thread starts then emits signal:
void WorkerObject::process(void)
{
//Infinity thread loop
forever
{
//Exit loop part
mutex.lock();
if(m_stop)
{
m_stop = false;
mutex.unlock();
break;
}
mutex.unlock();
//Hold/unhold loop part
mutex.lock();
if(!m_hold)
{
mutex.unlock();
//Here signal is emited
emit printMessage(messageStr); //That not works
//qDebug() << "Thread A test message."; //That works properly
}
mutex.unlock();
}
emit finished();
}
In main GUI thread I have timer for show that GUI thread works. So qDebug() works fine and prints messages from my thread. Also timer from GUI thread works fine and prints message inside textEdit GUI field.
Now when printMessage signal is emited, GUI thread executes slot method:
void MainWindow::printMessage(QString str)
{
ui->textEdit->append(str);
}
And this is most important part of my problem:
When signal printMessage from workerA object is connected with GUI slot printMessage with Qt::QueuedConnection my application hangs up. There is no possible to click something button or even exit app.
When signal/slot are connected with Qt::BlockingQueuedConnection everything works fine. Messages are emitted and received between threads and also GUI timer works fine.
So my question is why connection Qt::QueuedConnection causes that app freezes ?
I figured out this problem with #m7913d help.
You can try to perform a QThread::sleep inside your forever loop (f.e.
1 second) to check if it solves your problem.
So worker's thread emitted signal too frequently. Adding QThread::msleep(5) after emitted signal really helped. There ` also need to be included.
i have something really strange i have this code :
i think i know what is wrong but i dont know how to fix it .
this is what i have :
when i put break point in int test = 0;
it getting there before it gets to httpFinished() slot in the HttpClient , mybe this is the problem ?
in the main.cpp
---------------------------------------------------------------------------------------------------------
#while (i.hasNext())
{
i.next();
ThreadWorker* pThreadWorker = new ThreadWorker();
pThreadWorker->setUrl(sUrl);
QThreadPool::globalInstance()->start(pThreadWorker);
}
QThreadPool::globalInstance()->waitForDone();
---------------------------------------------------------------------------------------------------------
void ThreadWorker::run()
{
startWork();
}
void ThreadWorker::startWork()
{
m_pHttpClient = new HttpClient();
m_pHttpClient->startRequest(m_url);
int test = 0;
}
--------------------------------- HttpClient based on the http example from Qt -----------------------------------
HttpClient::HttpClient()
{
m_networkManager = new QNetworkAccessManager(this);
connect(m_networkManager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this, SLOT(slotAuthenticationRequired(QNetworkReply*,QAuthenticator*)));
#ifndef QT_NO_OPENSSL
connect(m_networkManager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
#endif
}
void HttpClient::startRequest(QUrl url)
{
m_url.setUrl("http://qt.nokia.com/");
QNetworkRequest request;
request.setUrl(m_url);
reply = m_networkManager->get(request);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
connect(reply,SIGNAL(finished()),
this, SLOT(httpFinished()));
connect(reply, SIGNAL(readyRead()),
this, SLOT(httpReadyRead()));
connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
this, SLOT(updateDataReadProgress(qint64,qint64)));
}
the httpFinished() function that is under private slots: never triggered , why ?
UPDATED THE QUESTION
Since the HttpClient and QNetworkAccessManager objects are created within the thread, they automatically belongs to that thread (see QObject::moveToThread), and they both needs an event loop running in that thread, for QNAM to do any work at all, and for your QObject derived class to be able to execute the slots.
You could add a call to QThread::exec() in run() to run that event loop (if you were using QThread):
void Thread::run()
{
startWork();
exec();
}
or create and start a QEventLoop whose quit() slot has to be connected somewhere to stop the loop (for example a finished() signal in the class HttpClient that you would emit when the work is done):
void ThreadWorker::run()
{
startWork();
QEventLoop loop;
QObject::connect(m_pHttpClient, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
}
Also, since Qt 4.8, QNetworkAccessManager is multithreaded, so you might not need to use threads yourself.