Disclaimer: I am relatively new to Qt and any type of programming that revolves around Threads and Networking. I have also adopted a lot of code from Qt Examples, API, and other online examples.
All code can be found on GitHub. This code is relatively as simple as it can get minus striping out GUI. I figure supplying it this way would help as well versus just pasting the code below.
I want to use and believe I need to use Threads as I need multiple clients send a request to the server, the server run some SQL code, then spit out the results back to the client (basically deriving a MySQL Server, but specific to what I am doing). Right now though, I am just working on learning the workings of it all.
With all that being said, as the Title states.. My client can connect to the server, the server sets up the thread, and will receive data (a String) through the readReady. After the data is read in, for right now I am just trying to echo it back to the client. It will do this, but only once. Then it spits out:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x266cca92ea0), parent's thread is serverThread(0x266cca9ed60), current thread is QThread(0x266cac772e0)
I cannot send any further data to the server unless I have the client reconnect, then after the data is sent, it will do its job but then spit out the same error and cease functioning. I have tried quite a bit of different things, but cannot seem to fix the issue. I even tried setting up a SIGNAL/SLOT for this as suggested in API:
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.
Anyway, any help would be greatly appreciated! My Code is below..
// Project
#include "ServerDialog.h"
#include "ServerThread.h"
ServerThread::ServerThread(qintptr _socketDiscriptor, QObject *parent /*= 0*/)
: QThread(parent)
{
socketDiscriptor = _socketDiscriptor;
}
void ServerThread::run()
{
emit threadStarted(socketDiscriptor);
// Start Thread
clientSocket = new QTcpSocket;
// Set SocketDisc
if (!clientSocket->setSocketDescriptor(socketDiscriptor))
{
emit error(clientSocket->error());
return;
}
// Connect Socket and Signal
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(clientSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
//// Loop Thread to Stay Alive for Signals and Slots
exec();
}
void ServerThread::readyRead()
{
QDataStream in(clientSocket);
in.setVersion(QDataStream::Qt_5_7);
in.startTransaction();
QString dataReceived;
in >> dataReceived;
if (!in.commitTransaction())
{
emit readyReadError(socketDiscriptor);
return;
}
emit readyReadMessage(socketDiscriptor, dataReceived);
echoData(dataReceived);
}
void ServerThread::disconnected()
{
emit threadStopped(socketDiscriptor);
clientSocket->disconnect();
clientSocket->deleteLater();
this->exit(0);
}
void ServerThread::echoData(QString &data)
{
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_7);
out << data;
clientSocket->write(block);
}
So in ServerThread.cpp
when echoData is called, that is when the error shows up and the Socket ceases functioning.
Any and all help will be appreciated. I know there are a few other posts regarding "Cannot create children for..." in regards to Threads. But I did not find any of them helpful. The one thing that I did find interesting but did not understand was maybe using moveToThread() but a lot of mixed comments on that.
I learn best through code examples along with explanation versus just an explanation or pointer to API. Thank you!
Most of Qt network functions are asynchronous; they do not block the calling thread. There is no need to mess up with threads if you are using QTcpSocket
s. In fact, creating a thread for every socket is an overkill, since that thread will spend most of its time just waiting for some network operation to finish. Here is how I would implement a single-threaded echo server in Qt:
#include <QtNetwork>
#include <QtCore>
//separate class for the protocol's implementation
class EchoSocket : public QTcpSocket{
Q_OBJECT
public:
explicit EchoSocket(QObject* parent=nullptr):QTcpSocket(parent){
connect(this, &EchoSocket::readyRead, this, &EchoSocket::EchoBack);
connect(this, &EchoSocket::disconnected, this, &EchoSocket::deleteLater);
}
~EchoSocket() = default;
Q_SLOT void EchoBack(){
QByteArray receivedByteArray= readAll();
write(receivedByteArray);
disconnectFromHost();
}
};
class EchoServer : public QTcpServer{
public:
explicit EchoServer(QObject* parent= nullptr):QTcpServer(parent){}
~EchoServer() = default;
//override incomingConnection() and nextPendingConnection()
//to make them deal with EchoSockets instead of QTcpSockets
void incomingConnection(qintptr socketDescriptor){
EchoSocket* socket= new EchoSocket(this);
socket->setSocketDescriptor(socketDescriptor);
addPendingConnection(qobject_cast<QTcpSocket*>(socket));
}
EchoSocket* nextPendingConnection(){
QTcpSocket* ts= QTcpServer::nextPendingConnection();
return qobject_cast<EchoSocket*>(ts);
}
};
int main(int argc, char* argv[]){
QCoreApplication a(argc, argv);
EchoServer echoServer;
echoServer.listen(QHostAddress::Any, 9999);
QObject::connect(&echoServer, &EchoServer::newConnection, [&](){
EchoSocket* socket= echoServer.nextPendingConnection();
qDebug() << "Got new connection from: " << socket->peerAddress().toString();
});
return a.exec();
}
#include "main.moc"
EchoSocket
object to handle it and prints a statement out to qDebug()
, and if that event was receiving something on a previously created socket, the same thread will echo received data back and close the connection. It will never block on a single connection waiting for data to arrive nor it will block waiting for a new connection to arrive.In this section, I am explaining why threading does not work for you the way you are doing it:
You should not be declaring slots in your QThread
subclass, Instead, use worker QObject
s and move them to QThread
s as needed.
The quote you have provided in your question is the exact explanation for why you get this warning. The ServerThread
instance you create will be living in the main thread (or whatever thread that created it). Now let's consider this line from your code:
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
The signal readyRead()
will be emitted from the current ServerThread
instance (since the clientSocket
object that emits it lives there), However, the receiver object is the current ServerThread
instance, But that lives in the main thread. Here is what the documentation says:
If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used.
Now, the main point of Qt::QueuedConnection
is executing the slot in the receiver object's thread. This means that, your slots ServerThread::readyRead()
and ServerThread::disconnected
will get executed in the main thread. This is most likely not what you meant to do, since you'll end up accessing clientSocket
from the main thread. After that, any call on clientSocket
that results in child QObject
s being created will result in the warning you get (you can see that QTcpSocket::write()
does this here).