PDA

View Full Version : QUdpSocket running in QThread - change port



sanji
3rd March 2020, 11:32
Hi everyone!
A simple question, but cannot find any solution.

I have a class:

class Test: public QThread
{
Q_OBJECT
public:
Test();
int port; //An UDP port to bind to
QUdpSocket *so;
void run() override; // creates, assigns an QUdpSocket object to the so variable, and binds it to some initial port.

public slots:
void reconnect(); // Should bind socket to some other port, than the one bound while initializing.
void receive(); // Called to receive datagram. Works fine.
};
The run() function looks like (simplyfied):

void Test::run()
{
so = new QUdpSocket();
so->bind(port);
this->exec();
so->deleteLater();
}
As a result, the QUdpSocket *so lives in its own thread.
Is there some way to run reconnect() slot inside this same thread, where the run() function and UdpSocket *so live in? Otherwise the bind to another port isn't allowed:

QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
QObject: Cannot create children for a parent that is in a different thread.

I've tried a lot. Always ending with running the SLOT reconnect() inside the SIGNALs thread.

Please, give me any suggestion, because I stuck out of ideas - since days now.

^NyAw^
3rd March 2020, 15:40
Hi,

You have to use the SIGNAL/SLOT mechanism. Just connect a SIGNAL to "reconnect" method and then use the "emit mySignal()".
You also can use the "QMetaObject::invokeMethod" function.

sanji
3rd March 2020, 17:18
First, thank you, for your time and attention!

Using of SIGNAL/SLOT pair works for the receive() function, as the signal is emitted by an object QUdpSocket *so created inside QThread's run() function.

But it definitely doesn't apply to a SIGNAL from any other source. The SLOT reconnect() inherits in this cases affinity to the sender's thread - no matter if used Qt::QueuedConnection (which should leve it to the Qthread's event loop started by exec()), or Qt::DirectConnection. This totally ruins my whole experience with the QThreads.
I can provide a sample of code, if someone had time and patience to look at ma "headache" it on his own.

QMetaObject::invokeMethod is something, I've never used and now have obviously to delve into.

Thanks!

^NyAw^
3rd March 2020, 17:31
Hi,

Where dou you call "reconnect()" ?

sanji
3rd March 2020, 17:59
From the main Widget if you mean the SIGNAL source. Please, let me prepare and upload the simple example, so you can see all clearly.

^NyAw^
3rd March 2020, 18:04
Hi,

And how do you call it?

sanji
3rd March 2020, 19:43
In the main Widget, I've connected a SIGNAL from QPushButton on the GUI to the SLOT reconnect() from the QThread based class.
Just as below:

connect(ui->pushButton, SIGNAL(released()), udpTest, SLOT(reconnect()), Qt::QueuedConnection);
I made all so simple, as it only was be possible, leaving just the bare minimum, to still work and get the error/warnings.
You can build and run this program.
I added a qDebug() to every operation, so you can read the thread's IDs of any called function.

13349

Using netcat one can send some data to the UDP port 1111. Then the debug output should look something like:


Main Loop() thread ID: 0x888
run() thread ID: 0x140c
receive() thread ID: 0x140c
reconnect() thread ID: 0x888
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QUdpSocket(0x6441a0), parent's thread is UDP(0x6448a0), current thread is QThread(0x3193c0)

^NyAw^
4th March 2020, 10:25
Hi,

Finally I found the problem. That is that you are creating the UDP Socket when the thread is not really runnig yet.
Check the modifications.

sanji
4th March 2020, 11:55
A huuuge thanks to you, for your rapid help!

But to understand and not just copy your solution, I need to ask, what did you mean with:

That is that you are creating the UDP Socket when the thread is not really runnig yet.
I'm creating the UDP socket inside the run() method, after running of start() method on the QThread object. And this method has already other thread ID than the main thread.
So how can it be possible, that the thread is not really running yet?

^NyAw^
4th March 2020, 12:28
Hi,

I don't remeber where I readed it but basically the OS will start the thread after calling "start". Maye into Qt Threads documentation.

So, a good way of doing it is waiting the "started" SIGNAL emited by the thread.

You get a different thread ID because it is assigned on "start()" call but as I noticed, the thread is not really running. Sometimes people are confused about the QThread inherited object and the thread where its execution is done. One thing is an object, and the other is a OS thread.

Hope it helps.

sanji
4th March 2020, 20:10
Sad news.
Your program works, in fact, without any errors. But after some tests, looking at the qDebug output I noticed:


20:52:42: Starte z:\build-UDPTest-Desktop_Qt_5_14_1_MinGW_64_bit-Debug\UDPTest.exe ...
Main Loop() thread ID: 0x73c
run() thread ID: 0x2fc
reconnect() thread ID: 0x73c
receive() thread ID: 0x73c
reconnect() thread ID: 0x73c

So all the SLOTS are running in the main thread 0x73c, while the run() method runs inside of 0x2fc.

I've reorganized the reconnect() SLOT a little, to run bind() only once at first run, but it has no effect on the thread assignment. It's just, how the method should finally look like. (And the previous debug output comes from your "RAW" version.)


void UDP::reconnect()
{
//If socket exists do not create again
if (so == 0)
{
so = new QUdpSocket(this);
connect(so, SIGNAL(readyRead()), this, SLOT(receive()), Qt::DirectConnection);
so->bind(1111);
} else {
so->disconnectFromHost();
so->bind(1112);
}

qDebug() << "reconnect() thread ID: " << QThread::currentThreadId();
Qt::HANDLE qThreadId = QThread::currentThreadId();
}

^NyAw^
5th March 2020, 09:28
Hi,

Sorry for my words ... "Fucking deleteLater !!!".

I have tryied another approach on the way I use to to this tasks. It now works but you cannot call "deleteLater" as you are telling the EvenLoop to delete the object as soon as possible.

sanji
6th March 2020, 07:14
For the moment I managed only to see if all is in the same thread. Had no time to delve into code, so have no questions yet. Just big thanks, as it, indeed works!


08:11:19: Starte z:\build-UDPTest-Desktop_Qt_5_14_1_MinGW_64_bit-Debug\UDPTest.exe ...
Main Loop() thread ID: 0xe38
run() thread ID: 0x15e4
receive() thread ID: 0x15e4
reconnect() thread ID: 0x15e4
08:11:22: z:\build-UDPTest-Desktop