Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Keep connection opened for a QThreaded QTcpSocket
Qt 6.12 LTS is out! See what's new in the release blog

Keep connection opened for a QThreaded QTcpSocket

Scheduled Pinned Locked Moved Unsolved General and Desktop
3 Posts 2 Posters 495 Views
  • 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.
  • O Offline
    O Offline
    odelaune
    wrote on last edited by
    #1

    Hello,
    I have adapted the Blocking Fortune Client Example. Here is the code:

    connectionthread.h

    #ifndef CONNECTIONTHREAD_H
    #define CONNECTIONTHREAD_H
    
    #include <QThread>
    #include <QMutex>
    #include <QWaitCondition>
    
    class ConnectionThread : public QThread
    {
        Q_OBJECT
    
    public:
        ConnectionThread(QObject *parent = nullptr);
        ~ConnectionThread();
    
        void requestNewData(const QString &hostName, quint16 port, const QString &message);
        void run() override;
    
    signals:
        void newData(const QByteArray &data);
        void error(int socketError, const QString &message);
    
    private:
        QString m_hostName;
        quint16 m_port;
        QString m_message;
        QMutex m_mutex;
        QWaitCondition m_cond;
        bool m_quit;
    };
    
    #endif // CONNECTIONTHREAD_H
    

    connectionthread.cpp

    #include <QtNetwork>
    
    #include "connectionthread.h"
    
    ConnectionThread::ConnectionThread(QObject *parent)
        : QThread(parent), m_quit(false)
    {
    }
    
    ConnectionThread::~ConnectionThread()
    {
        m_mutex.lock();
        m_quit = true;
        m_cond.wakeOne();
        m_mutex.unlock();
        wait();
    }
    
    void ConnectionThread::requestNewData(const QString &hostName, quint16 port, const QString &message)
    {
        QMutexLocker locker(&m_mutex);
        m_hostName = hostName;
        m_port = port;
        m_message = message;
    
        if (!isRunning())
            start();
        else
            m_cond.wakeOne();
    }
    void ConnectionThread::run()
    {
        m_mutex.lock();
    
        QString serverName = m_hostName;
        quint16 serverPort = m_port;
        m_mutex.unlock();
    
        while (!m_quit) {
            const int Timeout = 3 * 1000; // 3 sec.
    
            QTcpSocket socket;
            socket.connectToHost(serverName, serverPort);
    
            if (!socket.waitForConnected(Timeout)) {
                emit error(socket.error(), socket.errorString());
                return;
            }
    
            qDebug() << tr("Sending %1 to the server").arg(m_message);
            socket.write(m_message.toUtf8());
    
    
            QByteArray data;
            if (!socket.waitForReadyRead(Timeout)) {
                emit error(socket.error(), socket.errorString());
                return;
            }
            data = socket.readAll();
    
            m_mutex.lock();
            emit newData(data);
    
            m_cond.wait(&m_mutex);
            serverName = m_hostName;
            serverPort = m_port;
            m_mutex.unlock();
        }
    }
    

    In the main code, I connect the thread with

    ConnectionThread m_thread;
    
    connect(&m_thread, &ConnectionThread::newData,
            this, &Client::readData);
    
    connect(&m_thread, &ConnectionThread::error,
            this, &Client::displayError);
    
    m_thread.requestNewData(m_ip_address, m_port, m_messages[m_msgidC2]);
    
    ...
    
    

    It works well so far. Yet, now I need to keep the connection opened, i.e. not reconnect, each time I send a new message (with requestNewData())

    I do not know how to modify my code to achieve that.

    What I tried is to define the socket as a member of ConnectionThread. Then in the run() function, I call socket.connectToHost() only if socket.state() is not equal QTcpSocket::connected.

    But doing so, I get message like this one
    QObject: Cannot create children for a parent that is in a different thread.
    (Parent is QTcpSocket(0x559b0576ae30), parent's thread is QThread(0x559b04f23660), current thread is ConnectionThread(0x559b05582b40)

    Any idea of design?

    jsulmJ 1 Reply Last reply
    0
    • O odelaune

      Hello,
      I have adapted the Blocking Fortune Client Example. Here is the code:

      connectionthread.h

      #ifndef CONNECTIONTHREAD_H
      #define CONNECTIONTHREAD_H
      
      #include <QThread>
      #include <QMutex>
      #include <QWaitCondition>
      
      class ConnectionThread : public QThread
      {
          Q_OBJECT
      
      public:
          ConnectionThread(QObject *parent = nullptr);
          ~ConnectionThread();
      
          void requestNewData(const QString &hostName, quint16 port, const QString &message);
          void run() override;
      
      signals:
          void newData(const QByteArray &data);
          void error(int socketError, const QString &message);
      
      private:
          QString m_hostName;
          quint16 m_port;
          QString m_message;
          QMutex m_mutex;
          QWaitCondition m_cond;
          bool m_quit;
      };
      
      #endif // CONNECTIONTHREAD_H
      

      connectionthread.cpp

      #include <QtNetwork>
      
      #include "connectionthread.h"
      
      ConnectionThread::ConnectionThread(QObject *parent)
          : QThread(parent), m_quit(false)
      {
      }
      
      ConnectionThread::~ConnectionThread()
      {
          m_mutex.lock();
          m_quit = true;
          m_cond.wakeOne();
          m_mutex.unlock();
          wait();
      }
      
      void ConnectionThread::requestNewData(const QString &hostName, quint16 port, const QString &message)
      {
          QMutexLocker locker(&m_mutex);
          m_hostName = hostName;
          m_port = port;
          m_message = message;
      
          if (!isRunning())
              start();
          else
              m_cond.wakeOne();
      }
      void ConnectionThread::run()
      {
          m_mutex.lock();
      
          QString serverName = m_hostName;
          quint16 serverPort = m_port;
          m_mutex.unlock();
      
          while (!m_quit) {
              const int Timeout = 3 * 1000; // 3 sec.
      
              QTcpSocket socket;
              socket.connectToHost(serverName, serverPort);
      
              if (!socket.waitForConnected(Timeout)) {
                  emit error(socket.error(), socket.errorString());
                  return;
              }
      
              qDebug() << tr("Sending %1 to the server").arg(m_message);
              socket.write(m_message.toUtf8());
      
      
              QByteArray data;
              if (!socket.waitForReadyRead(Timeout)) {
                  emit error(socket.error(), socket.errorString());
                  return;
              }
              data = socket.readAll();
      
              m_mutex.lock();
              emit newData(data);
      
              m_cond.wait(&m_mutex);
              serverName = m_hostName;
              serverPort = m_port;
              m_mutex.unlock();
          }
      }
      

      In the main code, I connect the thread with

      ConnectionThread m_thread;
      
      connect(&m_thread, &ConnectionThread::newData,
              this, &Client::readData);
      
      connect(&m_thread, &ConnectionThread::error,
              this, &Client::displayError);
      
      m_thread.requestNewData(m_ip_address, m_port, m_messages[m_msgidC2]);
      
      ...
      
      

      It works well so far. Yet, now I need to keep the connection opened, i.e. not reconnect, each time I send a new message (with requestNewData())

      I do not know how to modify my code to achieve that.

      What I tried is to define the socket as a member of ConnectionThread. Then in the run() function, I call socket.connectToHost() only if socket.state() is not equal QTcpSocket::connected.

      But doing so, I get message like this one
      QObject: Cannot create children for a parent that is in a different thread.
      (Parent is QTcpSocket(0x559b0576ae30), parent's thread is QThread(0x559b04f23660), current thread is ConnectionThread(0x559b05582b40)

      Any idea of design?

      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @odelaune said in Keep connection opened for a QThreaded QTcpSocket:

      while (!m_quit) {
      const int Timeout = 3 * 1000; // 3 sec.

          QTcpSocket socket;
          socket.connectToHost(serverName, serverPort);
      

      Why do you create a new socket on each loop iteration?
      Create it once before the loop and also connect before you enter the loop.
      Also, all variables needed in the thread should be created inside run() (not as class member). Because only what is created inside run() is inside the thread.

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      O 1 Reply Last reply
      1
      • jsulmJ jsulm

        @odelaune said in Keep connection opened for a QThreaded QTcpSocket:

        while (!m_quit) {
        const int Timeout = 3 * 1000; // 3 sec.

            QTcpSocket socket;
            socket.connectToHost(serverName, serverPort);
        

        Why do you create a new socket on each loop iteration?
        Create it once before the loop and also connect before you enter the loop.
        Also, all variables needed in the thread should be created inside run() (not as class member). Because only what is created inside run() is inside the thread.

        O Offline
        O Offline
        odelaune
        wrote on last edited by
        #3

        @jsulm said in Keep connection opened for a QThreaded QTcpSocket:

        @odelaune said in Keep connection opened for a QThreaded QTcpSocket:

        while (!m_quit) {
        const int Timeout = 3 * 1000; // 3 sec.

            QTcpSocket socket;
            socket.connectToHost(serverName, serverPort);
        

        Why do you create a new socket on each loop iteration?
        Create it once before the loop and also connect before you enter the loop.
        Also, all variables needed in the thread should be created inside run() (not as class member). Because only what is created inside run() is inside the thread.

        For now, I tried to move this code snippet

            const int Timeout = 3 * 1000; // 3 sec.
        
            QTcpSocket socket;
            socket.connectToHost(serverName, serverPort);
        
            if (!socket.waitForConnected(Timeout)) {
                emit error(socket.error(), socket.errorString());
                return;
            }
        

        just before the while loop. Yet, doing so, the first message is well sent (it is received by the server), but when I want to send another message (after the first one), I always exit the while loop because I always enter into if (!socket.waitForReadyRead(Timeout)). Actually, the server never receive the second request.
        So something is probably still missing in the code.

        For now, I do not understand why the second message is not sent. I have checked what the write functions return and it corresponds to the correct number of bytes that are sent. But the server does not receive anything...

        If I place the code snippet inside the while loop, all messages are correctly sent...

        1 Reply Last reply
        0

        • Login

        • Login or register to search.
        • First post
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • Users
        • Groups
        • Search
        • Get Qt
        • Unsolved