简体   繁体   English

如何在QThread上创建QTcpServer,然后从主线程停止它

[英]How to create QTcpServer on a QThread and then stop it from the main thread

I have provided my entire code below. 我在下面提供了我的整个代码。 Please excuse the design as I am new to Qt and this is a quick and dirty example. 请原谅这个设计,因为我是Qt的新手,这是一个快速而肮脏的例子。

What I want to do is create a QThread that manages a QTcpServer listener. 我想做的是创建一个管理QTcpServer侦听器的QThread。 I want it to be able to close listener when stopped and then reopen it when started again. 我希望它能够在停止时关闭监听器,然后在再次启动时重新打开它。 Please ignore the fact that the listener may be getting leaked right now, I will tackle that issue later. 请忽略以下事实:侦听器可能现在正在泄漏,我稍后将解决该问题。

I believe the problem is that when Stop is called it invokes the m_listener->close() method on a different thread (invoked from the main thread) than the thread it was created on (it was created on the TcpServerThread). 我相信问题在于,当调用Stop时,它将在与创建线程(在TcpServerThread上创建)不同的线程(从主线程调用)上调用m_listener-> close()方法。 So the questions is, how to I fix it? 所以问题是,我该如何解决? Or how to I redesign it so I avoid this issue? 或者如何重新设计它以避免出现此问题?

This isn't the full design, I have made a toy example that mimicks the real thing. 这不是完整的设计,我做了一个模仿真实事物的玩具示例。

tcpserverthread.h tcpserverthread.h

#ifndef TCPSERVERTHREAD_H
#define TCPSERVERTHREAD_H

#include <QObject>
#include <QThread>

class QTcpServer;

class TcpServerThread : public QThread
{
    Q_OBJECT
public:
    TcpServerThread();

    void run();

    void Start();
    void Stop();
public slots:
    void newConnection();

private:

    QTcpServer* m_pListener;
};

#endif // TCPSERVERTHREAD_H

tcpserverthread.cpp tcpserverthread.cpp

#include "tcpserverthread.h"
#include "tcpserver.h"
#include <iostream>
#include <QTcpServer>
#include <QTcpSocket>
#include <QCoreApplication>

TcpServerThread::TcpServerThread()
{

}

void TcpServerThread::run()
{
    std::cout << "thread started ..." << std::endl;
    std::cout << "listener starting ..." << std::endl;
    m_pListener = new QTcpServer();
    connect(m_pListener, SIGNAL(newConnection()), this, SLOT(newConnection()));
    if(!m_pListener->listen(QHostAddress::Any, 1234))
    {
        std::cout << "listener could NOT START - " << m_pListener->errorString().toStdString() << std::endl;
    }
    else
    {
        std::cout << "listener SUCCESSFULLY STARTED" << std::endl;
    }

//    std::cout << "thread running..." << std::endl;
//    m_pListener = new TcpServer();
//    m_pListener->Start();

    exec();
}

void TcpServerThread::newConnection()
{
    qDebug() << "in new conn ... ";
    //std::cout << "in new conn.." << std::endl;
    QTcpSocket *soc = m_pListener->nextPendingConnection();
    soc->write("hello client");
}

void TcpServerThread::Start()
{
    start();
}

void TcpServerThread::Stop()
{
    std::cout << "TcpServer Stopping . . . " << std::endl;
    //this close is the line that causes the problem... 
    m_pListener->close();
    this->quit();
}

main.cpp main.cpp

#include <QCoreApplication>
#include <iostream>
#include <QTcpServer>
#include "tcpserver.h"

#include "tcpserverthread.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    TcpServerThread *t = new TcpServerThread();
    t->Start();
    t->Stop();

    return a.exec();
}

If you comment out the call to t->Stop in main you can then run it and use telnet (or putty) to connect to it @ 127.0.0.1:1234. 如果注释掉对main中t-> Stop的调用,则可以运行它并使用telnet(或腻子)以127.0.0.1:1234连接到它。

I want to make it so it can be stopped and then started again and connections will work. 我想这样做,以便可以停止它,然后再次启动它,并且连接将起作用。

Ok, so you are going about using QThread the wrong way here. 好的,所以您将在这里以错误的方式使用QThread。 If you are going to inherit from QThread then this should be because you want to extend the functionality of threading, which is not what you want. 如果要从QThread继承,那应该是因为您想扩展线程功能,而这并不是您想要的。

QThread is a class used to control a thread and for you, should not be a part of your class. QThread是用于控制线程的类,对您而言,它不应成为您的类的一部分。 Instead you should design your class TcpServer (not TcpServerThread ) as you have but remove the thread elements. 相反,您应该按自己的方式设计类TcpServer (而不是TcpServerThread ),但删除线程元素。 Then make run(), start() and stop() slots. 然后创建run(),start()和stop()插槽。 Also you need to inherit from QObject to use signals and slots. 另外,您需要从QObject继承以使用信号和插槽。 Something like this: 像这样:

class TcpServer: public QObject
{
    Q_OBJECT // don't forget this
    :
    :
}

Then in main you can create your thread and your class, and then move your class into the thread: 然后在main中可以创建线程和类,然后将类移入线程:

TcpServer *pTcpServer = new TcpServer();
QThread *pThread = new QThread;
pTcpServer->moveToThread(pThread);
// Connect thread start to your run() function
connect(pThread, &QThread::started, pTcpServer, &TcpServer::run, Qt::QueuedConnection);
// Now run your thread, when it starts your run() slot will be called
thread->start();

As to calling your start/stop - what triggers a start/stop? 至于呼叫您的启动/停止-是什么触发了启动/停止? I assume some other object will start/stop the server, or some event? 我假设某些其他对象将启动/停止服务器,或发生某些事件? In any case you will connect them in the same way: 无论如何,您将以相同的方式连接它们:

connect(pSomeClass, &SomeClass::startTcpServer, pTcpServer, &TcpServer::start, Qt::QueuedConnection);
connect(pSomeClass, &SomeClass::stopTcpServer, pTcpServer, &TcpServer::stop, Qt::QueuedConnection);

note 注意

I think the reason you are seeing some objects created in main thread is because you are calling functions directly into your class ( start() / stop() ). 我认为您看到主线程中创建了一些对象的原因是因为您直接在类中调用函数( start() / stop() )。 Doing this across the thread "boundary" is not safe since any actions taken (or objects allocated) would be done within the calling thread (main thread). 跨线程“边界”执行此操作并不安全,因为将在调用线程(主线程)内执行任何操作(或分配的对象)。 You need to use the slot / signal mechanism (or some other thread-safe mechanism). 您需要使用插槽/信号机制(或其他一些线程安全机制)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM