我遇到了一个问题,这里有几次类似情况,但是我找不到这些主题的解决方案。
我有一个主要课程,我想通过qt-network支持扩展它,这是一个额外的课程。让我打破源代码,找到相关部分:
主要课程
·H
class MainClass: public QObject{
Q_OBJECT
public:
[...]
private:
NetworkSupport * netSupport;
};
的.cpp
MainClass::MainClass()
{
[...]
netSupport
netSupport = new NetworkSupport(this->thread());
netSupport->start();
[...]
}
网络课程
·H
class NetworkSupport : public QThread
{
Q_OBJECT
public:
NetworkSupport(QThread *mainThread);
~NetworkSupport();
QByteArray readData();
void sendData(QByteArray *msg);
public slots:
void acceptConnection();
void receive();
void run();
private:
QByteArray curMessage;
QThread* libThread;
QEventLoop initWlan;
protected:
QTcpServer server;
QTcpSocket* client;
};
的.cpp
NetworkSupport::NetworkSupport(QThread* mainThread)
{
libThread = mainThread;
server.moveToThreaD(libThread);
server.listen(QHostAddress::Any, 5200);
QMetaObject::invokeMethode(&initWlan, "quit", Qt:QueuedConnection);
initWlan.exec();
}
void NetworkSupport::run(){
connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}
void NetworkSupport::acceptConnection()
{
client = server.nextPendingConnection();
client->moveToThread(libThread);
if (client->state() == QAbstractSocket::ConnectedState)
connect(client, SIGNAL(readyRead()), this, SLOT(receive()));
}
void NetworkSupport::receive()
{
curMessage = client->readAll();
}
void NetworkSupport::sendData(QByteArray* msg)
{
if(client->state() == QAbstractSocket::ConnectedState)
{
client->moveToThread(libThread);
printf("Sending a message %s\n",msg->data());
client->write(*msg);
client->waitForBytesWritten();
}
}
我知道我通常不需要这么详细地指定moveToThread()
,但如果它们全部存在或被移除,它最终不会改变它。
然而,在执行时,我在client->write(*msg)
的{{1}}收到错误消息,即:
sendData()
它看起来像一个警告,因为程序之后仍在执行,但我没有收到来自客户端的任何消息(即[...]
Sending a message ?r
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QnativeSocketEngine(0x2afc660), parent's thread is QThread(0x1b59210), current thread is QThread(0x7f6a70026b60)
QSocketNotifier: Can only be used with threads started with QThread
[...]
永远不会被执行),这是因为我猜错误消息的最后一行。
可能是这个错误信息只是误导,如果是这样,它的实际意义是什么,还是我做了一些完全错误的事情?
答案 0 :(得分:1)
您的代码中存在一些误解,如Qt网络和多线程如何工作。
首先,在Qt文档中,他们在QTcpServer::nextPendingConnection()
上说:
无法从其他线程使用返回的QTcpSocket对象。如果 你想要使用另一个线程的传入连接 覆盖incomingConnection()。
因此,您必须创建自己的Server
类,该类继承自QTcpServer,并在那里覆盖incomingConnection()
。在该重写方法中,您必须将套接字描述符发布到TCP客户端将处理该连接的其他线程。在这个简单的例子中,我们只发出一个信号。
signals:
void myNewConnectionSignal(DescriptorType);
protected:
void incomingConnection(DescriptorType descriptor)
{
emit myNewConnectionSignal(descriptor);
}
其次,NetworkSupport
线程是什么?我想,您希望您的服务器对象能够在那里生活和工作吗?如果是这样,那么你必须以其他方式重写它。以下是仅服务器部分。注意,QThread已经有一个QEventLoop,您可以通过run()中的exec()来使用它。
...
protected:
MyServer *server;
...
NetworkSupport::NetworkSupport()
{
// this is called in the main thread, so "this" lives in main thread
server=0;
client=0;
// say thread to start, but it will be actually started within run()
start();
// sometimes it is recommended to wait here for the thread started
}
NetworkSupport::~NetworkSupport()
{
// this is called in the main thread, say this thread to stop running
quit();
// wait for completion
wait();
}
void NetworkSupport::run()
{
// this is called in server thread
server=new MyServer();
if (server->listen(QHostAddress::Any, 5200))
{
// connect our new signal to slot where we create and init the TCP client, note that Qt::QueuedConnection is used because we want acceptConnection to run in the main thread
connect(server, SIGNAL(myNewConnectionSignal(DescriptorType)), MyClientsPool, SLOT(acceptConnection(DescriptorType)), Qt::QueuedConnection);
// go for infinite event loop
exec();
}
else
{
// do you always ignore errors?
}
// the thread is stopped, let's destroy the server in the thread where it was born
delete server;
server=0;
}
客户在您的主要线程中生活和工作。您不能直接从其他线程调用其方法。 NetworkSupport
也存在于主线程中:是的,它封装并管理其他一些线程,但作为一个QObject本身,它存在于我们创建它的线程中。下面的方法总是在主线程中执行因为我们连接服务器使用Qt :: QueuedConnection向NetworkSupport :: acceptConnection()发出信号,该Qt :: QueuedConnection告诉Qt我们希望在其QObject所在的线程中调用该插槽。
private slots:
void socketDisconnected();
...
void NetworkSupport::socketDisconnected()
{
if (client)
{
client->deleteLater();
client=0;
}
}
void NetworkSupport::acceptConnection(DescriptorType descriptor)
{
QTcpSocket* client=new QTcpSocket(this);
client->setSocketDescriptor(descriptor);
connect(client,SIGNAL(disconnected(),this,SLOT(socketDisconnected());
connect(client,SIGNAL(readyRead(),this,SLOT(receive());
}
void NetworkSupport::receive()
{
if (client)
curMessage = client->readAll();
}
void NetworkSupport::sendData(QByteArray* msg)
{
if (client)
{
client->write(*msg);
client->waitForBytesWritten();
}
}
<强>更新强>
如果我们只想隐藏线程内的所有网络工作。请注意,在下面的示例中,几乎没有错误处理和大量消息数据副本。您可能希望对其进行优化以进行生产。
// private container class incapsulated and working in the thread
class NetworkThreadContainer : public QObject
{
Q_OBJECT
public:
NetworkThreadContainer(QObject* parent=0):QObject(parent),client(0)
{
if (server.listen(QHostAddress::Any, 5200))
{
connect(&server, SIGNAL(newConnection()), this, acceptConnection());
}
else
{
// don't you want to throw exception here?
}
}
public slots:
void sendDataToClient(const QByteArray& barr)
{
if (client)
{
client->write(msg);
client->waitForBytesWritten();
}
}
void acceptConnection()
{
if (!client)
{
client = server.nextPendingConnection();
connect(client, SIGNAL(readyRead()), this, SLOT(receive()));
}
else
{
// what will you do with more than one client connections or reconnections?
}
}
void NetworkSupport::receive()
{
if (client)
{
QByteArray curMessage = client->readAll();
emit messageReceived(curMessage);
}
}
signals:
void messageReceived(const QByteArray&);
public:
QTcpClient* client;
QTcpServer server;
};
// thread class visible to outer program
class NetworkThread : public QThread
{
Q_OBJECT
public:
NetworkThread(QObject* parent=0):QObject(parent)
{
start();
}
~NetworkThread()
{
quit();
wait();
}
bool sendDataToClient(QByteArray* barr)
{
bool ok=false;
// async send data to container's thread
mutex.lock();
if (container)
{
ok=true;
QMetaObject::invokeMethod(
container,
"sendDataToClient",
Qt::QueuedConnection,
Q_ARG(QByteArray, *barr)
);
}
else
{
// container either is not ready yet or already destroyed
}
mutex.unlock();
return ok;
}
signals:
void messageReceived(const QByteArray&);
protected:
void run()
{
mutex.lock();
// I would suggest to catch exception here and assign the error result to some variable for further processing
container=new NetworkThreadContainer();
mutex.unlock();
connect(container,SIGNAL(messageReceived(QByteArray),this,SIGNAL(messageReceived(QByteArray)),Qt::QueuedConnection);
exec();
mutex.lock();
delete container;
mutex.unlock();
}
private:
QMutex mutex;
QPointer<NetworkThreadContainer> container;
};
答案 1 :(得分:1)
你在这段代码中做了很多错事,我不知道该从什么开始。
NetworkSupport::NetworkSupport(QThread* mainThread)
{
libThread = mainThread;
server.moveToThreaD(libThread);
这无济于事。 server
已与MainClass实例
server.listen(QHostAddress::Any, 5200);
服务器将开始侦听与MainClass相同的线程
QMetaObject::invokeMethode(&initWlan, "quit", Qt:QueuedConnection);
initWlan.exec();
这让我嘲笑了很多。这将简单地启动事件循环并几乎立即退出。
}
void NetworkSupport::run(){
connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}
这将简单地在新线程中运行,在connect语句后立即调用connect并退出线程 - 您的线程将不再在connect
之后运行。插槽acceptConnection
也可能在与MainClass相同的线程中调用。我想知道MainClass创建的时间和地点。
对我来说似乎就像你在同时挣扎于很多事情一样。您可能读过somwhere,您应该使用单独的线程进行网络通信,因此您不会阻止其他线程。首先尝试单线程方法。当你开始工作时,你应该考虑如何利用其他线程来满足你的需求。
Bonus问题:这个代码是否是某种可能没有Qt eventloop的应用程序的插件?或者它是全栈Qt应用程序的一部分?