Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Call for Presentations - Qt World Summit

    Solved Threaded TCP server for persistent connection

    General and Desktop
    2
    2
    943
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • S
      Sentret_C last edited by Sentret_C

      We are writing an app to receive and store data from IoT devices, which acts as a tcp server. We found some examples including the official threaded fortune server one, but different from those examples, in our case the tcp connection would last forever unless:

      • the tcp client is not our device (no valid response when expected),
      • the device is shut down (maybe out of power),
      • the app is closed (on server maintenance), or
      • the connection is simply lost due to network problem.

      We want to make sure that:

      • the resources are released as soon as possible, and
      • when the connection is closed by the app, it is closed properly (with FIN not RST).

      What's more, the communication class need to interact with the ui to show information to user and respond to change in configuration like request interval.

      We subclassed QTcpServer and:

      void MyServer::incomingConnection(qintptr socketDescriptor)
      {
          auto *worker = new MyWorker(socketDescriptor);
          auto *thread = new MyThread(this);
          worker->moveToThread(thread);
      
          connect(thread , &MyThread::started, worker, &MyWorker::init);
          connect(thread , &MyThread::finished, worker, &MyWorker::deleteLater);
          connect(worker, &MyWorker::showMsg, this, &MyServer::showMsg);
          connect(this, &MyServer::setReqInterval, worker, &MyWorker::setReqInterval);
      
          thread->start();
          emit setReqInterval(reqInterval);
      }
      

      QThread is subclassed to stop the thread on destruction:

      MyThread::~MyThread()
      {
          qDebug("in ~MyThread()");
          quit();
          wait();
      }
      

      Finally, in MyWorker:

      MyWorker::MyWorker(qintptr socketDescriptor, QObject *parent) :
          QObject(parent),
          socket(new QTcpSocket(this))
          connectionName(QString::number(socketDescriptor))
      {
          socket->setSocketDescriptor(socketDescriptor);
          connect(socket, &QTcpSocket::disconnected, this->thread(), &QThread::deleteLater);
      }
      
      void MyWorker::init()
      {
          // make sure the sql connection is created in the correct thread with a unique name
          auto db = QSqlDatabase::addDatabase("QMYSQL", connectionName);
          /*
              set database connection parameters
          */
          connect(socket, &QTcpSocket::readyRead, this, &MyWorker::dataReceived);
          connect(&sendTimer, &QTimer::timeout, this, &MyWorker::sendRequest);
          // sendTimer will be started in setReqInterval()
          recvTimer.setSingleShot(true);
          connect(&recvTimer, &QTimer::timeout, this, &MyWorker::recvTimeout);
          // recvTimer will be started in sendRequest() and stopped in dataReceived() if the data is valid
          sendRequest();
      }
      
      void MyWorker::recvTimeout()
      {
          QString message;
          /*
              construct message
          */
          emit showMsg(message);
          socket->disconnectFromHost();
      }
      

      The code above (modified for brevity so there may be typo) seems to work, but we are not sure if it is done correctly, especially:

      • the QTcpSocket objects are created and initialized (setSocketDescriptor called) in MyWorker constructor, which runs in the ui thread,
      • the worker object is deleted after the thread quits, probably when the thread object is deleted, and
      • we connected the socket's disconnected signal to &QThread::deleteLater of this->thread() in MyWorker constructor (though the debug output shows that ~MyThread() is called as expected), maybe we should change it to the following?
      // in MyServer::incomingConnection
          connect(worker, &MyWorker::destroyed, thread, &MyThread::deleteLater);
      
      // in MyWorker::MyWorker
          connect(socket, &QTcpSocket::disconnected, this, &MyWorker::deleteLater);
      
      1 Reply Last reply Reply Quote 0
      • V
        VRonin last edited by

        the QTcpSocket objects are created and initialized (setSocketDescriptor called) in MyWorker constructor, which runs in the ui thread

        That's fine, setting the socket descriptor is not an intensive task

        the worker object is deleted after the thread quits, probably when the thread object is deleted

        It's deleted when the thread finishes, not when it is deleted. Qt internals take care of this case, even if technically the thread finished already, Qt will still run any delayed deletes

        we connected the socket's disconnected signal to &QThread::deleteLater of this->thread() in MyWorker constructor

        You normally connect(thread, &MyThread::finished, thread, &MyThread::deleteLater); and then connect a signal to &MyThread::quit. Putting connect(socket, &QTcpSocket::disconnected, this->thread(), &QThread::deleteLater); violates OOD as makes the worker dependent on living in a QThread. The worker should just emit a signal when the sochet is disconnected and you should connect that signal to &MyThread::quit.

        P.S.
        Using 1 thread per client will quickly add too much overhead to your server. If you don't use that many clients you can even not use threads at all. We [users of the forum] recognise Qt examples for TCP are sub-optimal and we are working on a better example: You can have a look at what we have done so far, we have a basic version and a more advanced one.

        "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
        ~Napoleon Bonaparte

        On a crusade to banish setIndexWidget() from the holy land of Qt

        1 Reply Last reply Reply Quote 5
        • First post
          Last post