c++multithreadingqtqtcpsocketqsslsocket

Do QTcpSocket or QSslSocket automatically create thread for reading/writing?


Despite not using std::thread or QThread anywhere, still getting following problems:

  1. Always a runtime debug error log from Qt:

    QObject::connect: Cannot queue arguments of type 'QAbstractSocket::SocketError'
    (Make sure 'QAbstractSocket::SocketError' is registered using qRegisterMetaType().)

  2. Intermittent crash on TcpSocket::flush() method;
    I use this method to make sure that the TCP is written immediately; Now sometimes the app crashes exactly at this method with SIGPIPE

Upon searching internet, found that people suggest that to fix 1st problem (i.e. the meta error), I need to register using qRegisterMetaType(), when we have multiple threads.
Same multithreading is referred as a cause for the 2nd problem as well; see this and this.

But I don't have more than 1 thread!
My socket code looks like below:

struct Socket : public QSslSocket
{
  Q_OBJECT public:

  void ConnectSlots ()
  {
    const auto connectionType = Qt::QueuedConnection;
    connect(this, SIGNAL(readyRead()), this, SLOT(ReceiveData()), connectionType);
    connect(this, SIGNAL(disconnected()), this, SLOT(Disconnected()), connectionType);
    connect(this, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(Error(QAbstractSocket::SocketError)), connectionType);
    //                           ^^^^^^^ error comes whether I comment this or not
  }

  public slots:
  void ReceiveData () { ... }
  void Disconnected () { ... }
  void Error () { ... }
}

Question: Is Qt creating any internal thread by itself for read/write purpose? (I hope not). How to fix above 2 issues?


Solution

  • No, the sockets don't create a separate thread for read/write. Instead the OS raises the event on a given socket descriptor, whenever there is a read/write observed. This event should be queued. Hence for that Qt::QueuedConnection is preferred.

    The QAbstractSocket::SocketError is impromptu and appears OS specific. It cannot be avoided. At the max, the socket can be destroyed when such error happens.

    To avoid crashes, whenever there is a disconnection in the socket, following can be done:

    void Destroy (QWebSocket* const pSocket)
    {
      if(pSocket == nullptr)
        return;
      pSocket->disconnect();  // no further signal/slot
      pSocket->close();  // graceful closure
      pSocket->deleteLater(); // don't delete immediately; let the Qt take care
      pSocket = nullptr; // to avoid further undefined behaviour
    }
    

    Even after doing above, sometimes a socket crash happens due to write() operation. viz. When the socket close()-es, it tries to flush() all the writable data. During then, if the remote connection is already closed, then the OS crashes the program using a SIGPIPE event. Unfortunately it cannot be prevented in C++ using the std::exceptions.

    The solutions mentioned in below post doesn't help:
    How to prevent SIGPIPEs (or handle them properly)
    This can be avoided by following:

    if(pSocket->isValid())
      pSocket->sendBinaryMessage(QByteArray(...));
    

    So isValid() helps in a situation, where a socket is trying to write something into an already disconnected remote socket connection.