c++multithreadingqtsocketsqudpsocket

How to properly create QUdpSocket on non-gui thread ? Readyread not emitted


I'm writing a network library that wraps the QUdpSocket:

QAbstractSocket *UdpNetworkStreamer::addConnection()
{
    QUdpSocket *udpSocket = new QUdpSocket(this);
    udpSocket->bind(connection.port, QUdpSocket::ShareAddress);
    bool ret = udpSocket->joinMulticastGroup(QHostAddress(connection.ip));
    connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::QueuedConnection);

    return udpSocket;
}
  1. create a new QUdpSocket.
  2. connect to its readyRead signal.
  3. call readDatagram when readyRead is raised.

All is working fine when I use the library from a Qt GUI application.

The problem starts when another user includes the library used outside of a Qt GUI application.

He calls the addConnection (which creates the socket and calls connect on the readyRead)

The thread on which the addConnection is called is non-Qt.

The addConnection seems to end successfully but the readyRead is never emitted.

Calling read (even though no readyRead was emitted) leads to a successful datagram read.

Fixes that did not work :

  1. moving the the UDP socket thread to the this->thread

    QUdpSocket *udpSocket = new QUdpSocket();
    udpSocket->moveToThread(this->thread());
    udpSocket->setParent(this);
    
  2. I tried to simulate the problem by calling:void

    MainWindow::on__btnOpenMulticastReceiver_clicked()
    {
       QFuture<void> future = QtConcurrent::run(this, 
       &MainWindow::CreateMulticastConnection, testHandle);
    }
    

    This also led to same symptoms as the one the user had with my library, meaning the readyRead wasn't emitted.

  3. QSignalSpy - I've activated a spy on the readyRead signal; the counter kept on being zero although I could read data directly from the socket. The spy gave valid results (i.e. progressed) when used the socket was initialized on the main thread.

My Questions:

  1. What am I missing and doing wrong ?
  2. What is the simplest way of having the readyRead emitted even though it is not created on the main GUI thread - I couldn't find any sample that works with no GUI or outside Qt threads.

Solution

  • I ended up solving the problem this way :

    void MainWindow::OpenConnection()
    {
      QThread *t = new QThread();
      t->start();
    
      SocketWrapper *w= new SocketWrapper();
    
      w->moveToThread(t);
    
      w->metaObject()->invokeMethod(w, "CreateSocket", Qt::QueuedConnection);
    }
    

    You must call invokeMethod() with the thread the socket wrapper was movedTo() upon creation of the socket, so that the thread that creates the socket will have a running event loop.

    In addition to that, the CreateSocket() needs to be a slot in the SocketWrapper, something like that :

    class SocketWrapper : public QObject
    {
      Q_OBJECT
    public:
      explicit SocketWrapper(QObject *parent = 0);
    
    
    signals:
    
    public slots:
      void readyRead();
      void CreateSocket();
    private:
      QUdpSocket *_socket;
    };