C++ 如何在Qt中创建线程网络服务器?
我在一个线程化的telnet服务器上工作(每个连接一个线程),不知道如何消除valgrind错误。我已将问题缩小到删除tcpsocket的位置 我在QThread的run()方法中创建QTcpSocket:C++ 如何在Qt中创建线程网络服务器?,c++,qt,sockets,destructor,C++,Qt,Sockets,Destructor,我在一个线程化的telnet服务器上工作(每个连接一个线程),不知道如何消除valgrind错误。我已将问题缩小到删除tcpsocket的位置 我在QThread的run()方法中创建QTcpSocket: void TelnetConnection::run() { tcpSocketPtr = new QTcpSocket(); if (!tcpSocketPtr->setSocketDescriptor(socketDescriptor)) { e
void TelnetConnection::run()
{
tcpSocketPtr = new QTcpSocket();
if (!tcpSocketPtr->setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocketPtr->error());
return;
}
....
}
当我的应用程序要断开客户端连接时,我会呼叫:
void TelnetConnection::disconnectClient()
{
tcpSocketPtr->disconnectFromHost();
}
被称为插座断开的插槽为:
void TelnetConnection::clientDisconnected()
所以,我试过了
1.删除clientDisconnected插槽中的QTcpSocket,但这会导致读/写不稳定。(偶尔撞车)
2.稍后在clientDisconnected插槽中删除,但这会导致内存读/写错误
3.在线程的exec循环后删除,但仍会导致内存读/写错误
4.在线程的exec循环之后删除,并且所有错误都消失了
从我所读到的,deletelater如果在exec循环终止后调用,将在线程被删除时运行。因此,虽然这是可行的,但我认为这不是正确的方法
我尝试创建以“this”作为父项的QTcpSocket,但由于父项与此不匹配错误,我的信号连接失败。(这将允许在螺纹破坏时删除QTcpSocket)
解决这个问题的正确方法是什么?您的问题几乎完全源于重新实现
QThread
。不要这样做。将所有功能放入QObject
,然后使用moveToThread()
将其移动到裸QThread
。如果您仅通过信号插槽连接从外部访问对象,那么您将立即完成
首先,我总是将您的TelnetConnection
的一些实例称为telnethread
。这只是为了让大家明白我在说什么
到目前为止,您显示的代码中的错误包括:
run()
方法中调用emit error(tcpSocketPtr->error())
。它是从telnetThread
调用的,与信号所在的QObject
不同的线程:它位于telnetThread->thread()
中
run()
方法正在telnetThread
线程中执行。但是,由moc生成的信号实现预计将在实例化QThread
的任何线程中调用,即telnetThread->thread()
,并且该线程永远不能等于执行run()
的线程。基本上,有点令人困惑的是,以下不变量成立:
QThread * telnetThread ...
Q_ASSERT(telnetThread != telnetThread->thread());
tcpSocketPtr
上的方法,这些方法位于telnetThread
中。以下观点认为:
Q_ASSERT(tcpSocketPtr->thread() == telnetThread);
telnetThread
上声明的所有插槽都在与telnetThread
本身不同的线程中执行!因此,disconnectClient
的主体在GUI线程中执行,但它直接在tcpSocketPtr
上调用方法Q
,然后输入/返回将彻底关闭服务器
导言
注意:此示例已被重构,最后一个服务器现在已正确删除
在一定程度上注意确保物品包装干净。请注意,只需退出(正在运行的QCoreApplication
,就可以了,这样会自动进行总结。因此,所有对象最终都会被破坏和释放,没有任何东西会崩溃。线程和服务器从其析构函数向控制台发出诊断消息。通过这种方式,很明显事情确实会被删除
该代码同时支持Qt4和Qt5
停止线程
将缺少的行为添加到QThread
。通常,当您销毁正在运行的线程时,会收到警告消息和崩溃/未定义的行为。该类在销毁时会通知线程的事件循环退出,并等待线程实际完成。它的使用方式与QThread
一样,只是它不会在销毁时做愚蠢的事情
线程化QoObjectDeleter
当给定的QObject的线程被销毁时,删除该对象。当线程逻辑上拥有其对象时非常有用。此逻辑所有权不是父子所有权,因为线程和逻辑所有权对象位于不同的线程中(!)
构造函数是私有的,并且提供了工厂方法。这是为了强制在空闲存储(也称为堆)上创建删除程序。在堆栈上生成deleter可能是一个错误,因此此模式使用编译器来防止这种情况发生
对象必须尚未移动到指定的线程,否则删除程序的构造将受到竞争条件的约束-对象可能已经在线程中删除了自身。这一先决条件是肯定的
服务器工厂
在调用其newConnection
插槽时生成新的服务器实例。构造函数被传递给客户机QObject
的QMetaObject
来创建。因此,该类可以构造“任意”所需的QObject
,而无需使用模板。对其创建的对象只有一个要求:
它必须有一个Q\u可调用的
构造函数,将qtcsocket*
作为第一个参数,将QObject*parent
作为第二个参数。它生成的对象是在父对象设置为nullptr
的情况下创建的
套接字的所有权转移到服务器
ThreadedServerFactory
为每个服务器创建一个专用的StoppingThread,并将服务器移动到此线程。否则,其行为类似于ServerFactory。这个
Q_ASSERT(tcpSocketPtr->thread() == telnetThread);
#include <QCoreApplication>
#include <QThread>
#include <QTcpServer>
#include <QTcpSocket>
#include <QAbstractEventDispatcher>
#include <QPointer>
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
#define Q_DECL_OVERRIDE override
#endif
// A QThread that quits its event loop upon destruction,
// and waits for the loop to finish.
class StoppingThread : public QThread {
Q_OBJECT
public:
StoppingThread(QObject * parent = 0) : QThread(parent) {}
~StoppingThread() { quit(); wait(); qDebug() << this; }
};
// Deletes an object living in a thread upon thread's termination.
class ThreadedQObjectDeleter : public QObject {
Q_OBJECT
QPointer<QObject> m_object;
ThreadedQObjectDeleter(QObject * object, QThread * thread) :
QObject(thread), m_object(object) {}
~ThreadedQObjectDeleter() {
if (m_object && m_object->thread() == 0) {
delete m_object;
}
}
public:
static void addDeleter(QObject * object, QThread * thread) {
// The object must not be in the thread yet, otherwise we'd have
// a race condition.
Q_ASSERT(thread != object->thread());
new ThreadedQObjectDeleter(object, thread);
}
};
// Creates servers whenever the listening server gets a new connection
class ServerFactory : public QObject {
Q_OBJECT
QMetaObject m_server;
public:
ServerFactory(const QMetaObject & client, QObject * parent = 0) :
QObject(parent), m_server(client) {}
Q_SLOT void newConnection() {
QTcpServer * listeningServer = qobject_cast<QTcpServer*>(sender());
if (!listeningServer) return;
QTcpSocket * socket = listeningServer->nextPendingConnection();
if (!socket) return;
makeServerFor(socket);
}
protected:
virtual QObject * makeServerFor(QTcpSocket * socket) {
QObject * server = m_server.newInstance(Q_ARG(QTcpSocket*, socket), Q_ARG(QObject*, 0));
socket->setParent(server);
return server;
}
};
// A server factory that makes servers in individual threads.
// The threads automatically delete itselves upon finishing.
// Destructing the thread also deletes the server.
class ThreadedServerFactory : public ServerFactory {
Q_OBJECT
public:
ThreadedServerFactory(const QMetaObject & client, QObject * parent = 0) :
ServerFactory(client, parent) {}
protected:
QObject * makeServerFor(QTcpSocket * socket) Q_DECL_OVERRIDE {
QObject * server = ServerFactory::makeServerFor(socket);
QThread * thread = new StoppingThread(this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(server, SIGNAL(destroyed()), thread, SLOT(quit()));
ThreadedQObjectDeleter::addDeleter(server, thread);
server->moveToThread(thread);
thread->start();
return server;
}
};
// A telnet server with following functionality:
// 1. It echoes everything it receives,
// 2. It shows a smiley face upon receiving CR,
// 3. It quits the server upon ^C
// 4. It disconnects upon receiving 'Q'
class TelnetServer : public QObject {
Q_OBJECT
QTcpSocket * m_socket;
bool m_firstInput;
Q_SLOT void readyRead() {
const QByteArray data = m_socket->readAll();
if (m_firstInput) {
QTextStream out(m_socket);
out << "Welcome from thread " << thread() << endl;
m_firstInput = false;
}
for (int i = 0; i < data.length(); ++ i) {
char c = data[i];
if (c == '\004') /* ^D */ { m_socket->close(); break; }
if (c == 'Q') { QCoreApplication::exit(0); break; }
m_socket->putChar(c);
if (c == '\r') m_socket->write("\r\n:)", 4);
}
m_socket->flush();
}
public:
Q_INVOKABLE TelnetServer(QTcpSocket * socket, QObject * parent = 0) :
QObject(parent), m_socket(socket), m_firstInput(true)
{
connect(m_socket, SIGNAL(readyRead()), SLOT(readyRead()));
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
}
~TelnetServer() { qDebug() << this; }
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTcpServer server;
ThreadedServerFactory factory(TelnetServer::staticMetaObject);
factory.connect(&server, SIGNAL(newConnection()), SLOT(newConnection()));
server.listen(QHostAddress::Any, 8023);
return a.exec();
}
#include "main.moc"