TSG*_*TSG 2 c++ sockets qt destructor
我正在使用线程telnet服务器(每个连接一个线程),并且无法弄清楚如何摆脱valgrind错误.我把问题缩小到了删除tcpsocket的地方.
我在QThread的run()方法中创建了QTcpSocket:
void TelnetConnection::run()
{
tcpSocketPtr = new QTcpSocket();
if (!tcpSocketPtr->setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocketPtr->error());
return;
}
....
}
Run Code Online (Sandbox Code Playgroud)
当我的应用想断开客户端时,我打电话:
void TelnetConnection::disconnectClient()
{
tcpSocketPtr->disconnectFromHost();
}
Run Code Online (Sandbox Code Playgroud)
并且在socket断开连接时调用的插槽是:void TelnetConnection :: clientDisconnected()
{
tcpSocketPtr->deleteLater();
TelnetConnection::s_clientCount--;
QThread::quit(); // Exit ths event loop for this thread
}
Run Code Online (Sandbox Code Playgroud)
所以,我试过1.删除clientDisconnected插槽中的QTcpSocket,但这会导致读/写不稳定.(偶然崩溃)2.在clientDisconnected插槽中删除,但导致内存读/写错误3.在线程的exec循环后删除但仍然导致内存读/写错误4.在线程的exec循环后删除 - 并且全部错误消失了.
从我读到的内容,如果在exec循环终止后调用deletelater,则会在删除线程时运行.因此,虽然这有效,但我认为这不是正确的方法.
我尝试使用"this"作为父级创建QTcpSocket,但由于父级vs这种不匹配错误,我的信号连接失败.(这将允许在线程销毁时删除QTcpSocket).
解决这个问题的正确方法是什么?
你的问题几乎完全来自重新实现QThread.不要这样做.将所有功能放入a中QObject,然后将其移至裸机QThread使用状态moveToThread().如果您只通过信号插槽连接从外部访问您的对象,那么您将立即完成.
首先,我会请务必提交的一些实例TelnetConnection为telnetThread.这只是为了让我明白我在谈论什么线程.
到目前为止,您显示的代码中的错误是:
你是emit error(tcpSocketPtr->error())从run()方法中调用的.它是从所谓的telnetThread,一个不同的线程比QObject信号生活:它生活在telnetThread->thread().
该run()方法在telnetThread线程内执行.但信号的实现,通过MOC生成,预计将在您实例化任何线程中调用QThread-即telnetThread->thread(),与该线程永远不等于到一个地方run()执行.基本上,有点混乱,以下不变量持有:
QThread * telnetThread ...
Q_ASSERT(telnetThread != telnetThread->thread());
Run Code Online (Sandbox Code Playgroud)您正在从另一个线程中执行的槽中调用方法tcpSocketPtr,生活在telnetThread其中.以下是:
Q_ASSERT(tcpSocketPtr->thread() == telnetThread);
Run Code Online (Sandbox Code Playgroud)
您声明的所有插槽telnetThread都在与telnetThread自身不同的线程中执行!因此,在disconnectClientGUI线程中执行的主体,但它直接调用方法tcpSocketPtr.
以下是一种方法.它侦听端口8023. ^ D结束连接.接收大写Q后跟Enter/Return将干净地关闭服务器.
介绍
注意:此示例已重构,现在已正确删除最后一个服务器.
我们需要注意确保事情干净利落.请注意,只需quit()运行就可以了QCoreApplication,总结会自动发生.因此,所有对象最终都被破坏和释放,没有任何东西应该崩溃.线程和服务器从析构函数向控制台发出诊断消息.这种方式显然会被删除.
该代码支持Qt 4和Qt 5.
StoppingThread
添加缺少的行为QThread.通常,当您破坏正在运行的线程时,会收到警告消息和崩溃/未定义的行为.这个类在销毁时告诉线程的事件循环退出并等待线程实际完成.它就像使用它一样QThread,除了它不会在破坏时做愚蠢的事情.
ThreadedQObjectDeleter
QObject在其线程被销毁时删除给定的.当线程在逻辑上拥有其对象时很有用.此逻辑所有权不是父子所有权,因为线程和逻辑拥有的对象存在于不同的线程(!)中.
构造函数是私有的,并提供了工厂方法.这是为了在免费商店(也就是堆)上强制创建删除器.在堆栈上创建删除器可能是一个错误,因此该模式使用编译器来防止它发生.
该对象尚未被移动到指定的线程,否则删除器的构造将受到竞争条件的影响 - 该对象可能已经在线程内自行删除.断言这个先决条件.
ServerFactory
newConnection调用其插槽时生成新的服务器实例.构造函数传递给QMetaObject客户端QObject进行创建.因此,该类可以构造"任何"所需的QObject而无需使用模板.它创建的对象只有一个要求:
它必须有一个Q_INVOKABLE构造函数QTcpSocket*作为第一个参数,并QObject *parent作为第二个参数.它生成的对象是在父设置为的情况下创建的nullptr.
套接字的所有权将传输到服务器.
ThreadedServerFactory
为每个服务器创建一个专用的StoppingThread,并将服务器移动到该线程.否则表现得像ServerFactory.螺纹由工厂拥有,并在工厂被破坏时妥善处理.
服务器的终止退出线程的事件循环,从而完成线程.已完成的线程已删除.在终止服务器之前被破坏的线程将删除现在悬空的服务器.
TelnetServer
实现一个简单的telnet服务器.该接口由一个可调用的构造函数组成.构造函数使用套接字并将套接字连接到内部插槽.功能非常简单,类只对套接字作出反应readyRead并disconnected发出信号.断开连接后,它会自行删除.
这不是真正的telnet服务器,因为telnet协议不是那么简单.碰巧telnet客户端将使用这种愚蠢的服务器.
主要()
main函数创建服务器,服务器工厂,并将它们连接在一起.然后它告诉服务器监听任何地址,端口8023上的连接,并启动主线程的事件循环.监听服务器和工厂位于主线程中,但所有服务器都存在于自己的线程中,您可以在查看欢迎消息时轻松查看.支持任意数量的服务器.
#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"
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2439 次 |
| 最近记录: |