1

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.
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Adiel Yaacov
  • 1,401
  • 3
  • 14
  • 24
  • Does the thread on which the user creates the `QUdpSocket` have an active event loop? What happens in you alter the connection type to `Qt::DirectConnection` (just by way of a quick test)? – G.M. Apr 05 '17 at 08:20
  • @G.M. tried to use direct connetion it didn't help. the thread that creates the udpsocket does not have an event loop, its not a qt thread. – Adiel Yaacov Apr 05 '17 at 08:38
  • If the thread on which the `UdpNetworkStreamer` exists doesn't have an event loop then queued signals can never be delivered to it. I suspect the `readyRead` signal *is* being emitted but the resulting events are sitting in an event queue and will never be processed. – G.M. Apr 05 '17 at 08:46
  • i understand what you are saying, but wouldn't the signal spy counter be updated at least once ? it remains zero ... – Adiel Yaacov Apr 05 '17 at 08:47
  • I'm pretty sure you need an event loop for `readyRead` to be emitted. What would emit it, if there wasn't an event that would trigger it? – thuga Apr 05 '17 at 09:12
  • @thuga - i'd be happy to have an event loop that will process it ? how can you do it, if the calling thread does not have one ? create a new qthread and move the socket to that thread using moveToThread ? – Adiel Yaacov Apr 05 '17 at 09:17
  • You should see [this question](http://stackoverflow.com/questions/27801572/is-it-possible-to-create-local-event-loops-without-calling-qapplicationexec). – thuga Apr 05 '17 at 09:32
  • Are you creating a [`QCoreApplication`](http://doc.qt.io/qt-5/qapplication.html) and calling `exec()` on it? The message loop that Qt dispatches on won't run otherwise. – Paul Belanger Oct 09 '18 at 17:42

2 Answers2

2

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;
};
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Adiel Yaacov
  • 1,401
  • 3
  • 14
  • 24
  • 1
    You should be able to simply connect the thread's `started()` to your `CreateSocket()` - obviously don't start the thread until after the `moveToThread()` and `connect()`. – Toby Speight Oct 09 '18 at 15:51
0

My guess is that you need to add Q_OBJECT macro to the beginning of the class from where you need to emit the signal. Unless you do this, the signal-slot mechanism will not function properly.

Doğukan Tunç
  • 337
  • 2
  • 16
  • thanks but, i have that macro, Obviously since it is working perfectly when i use a qt gui applicationto use the library. – Adiel Yaacov Apr 05 '17 at 07:52