简体   繁体   中英

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.

What I want to do is create a QThread that manages a QTcpServer listener. 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). 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

#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

#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

#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.

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. 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 is a class used to control a thread and for you, should not be a part of your class. Instead you should design your class TcpServer (not TcpServerThread ) as you have but remove the thread elements. Then make run(), start() and stop() slots. Also you need to inherit from QObject to use signals and slots. 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:

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() ). 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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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