PDA

View Full Version : UdpSockets, QTimer and QThread errors going from Qt4.8 to Qt5



taraj
7th December 2023, 06:14
Hello,

I have a console application that talks to another (main) application using udp sockets and waits for a reply. I wrote the initial code (which didn't have these problems) in Qt4.8 but I am now on Qt5.5 and it is not working as it was.
I am getting 3 errors:
1. "QObject::killTimer: Timers cannot be stopped from another thread" and QObject::startTimer: Timers cannot be stopped from another thread"
2. QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
(this can be 'corrected' by the line : udpReceiverThread->moveToThread(udpReceiverThread); not sure if it is the right thing to do though based on what i have read
3. replyNotReceived SLOT is not being called (replyTimer).


main.cpp starts ArcConsoleInput and ArcConsoleInput starts the UdpThread. There are 2 timers in the code

1. timeoutTimer - if the user has not typed in any commands in 1 minute the ArcConsoleApp quits
2. replyTimer - the thread waits 5 seconds for a reply, if no reply received (e.g. main app not running) then message goes to std::cout.

How do I get rid of error #1 and get the replyNotReceived SLOT to run..

I have being trying for a few days to get this working using various qt websites, pages, forums but not having any luck.

Thank you for your help


ArcConsoleInput.h


private:
/*! Timer for when there has been no user interaction for userTimeout seconds */
QTimer* timeoutTimer;

/*! A SocketNotifier to read from Standard Input */
QSocketNotifier* notifier;

/*! A file to read standard input from */
QFile* stdin;

/*! A UdpThread that will receive replies from the ARC */
UdpThread* udpReceiverThread;

/*! A UDP socket to send requests to the ARC software */
QUdpSocket *udpSocketArcSender;

ArcConsoleInput.cpp



void ArcConsoleInput::networkSetup()
{
//create a sender UDP socket
udpSocketArcSender = new QUdpSocket(this);

//create a thread for the a udp receiver sockets
udpReceiverThread = new UdpThread();
udpReceiverThread->start();
//to stop error:QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
udpReceiverThread->moveToThread(udpReceiverThread);

//Create a SocketNotifier to read from Standard Input
stdin = new QFile();
stdin->open(0, QIODevice::ReadOnly);
notifier = new QSocketNotifier(0, QSocketNotifier::Read);
connect(notifier, SIGNAL(activated(int)), this, SLOT(readUserLine()));

//Create a timer for when there has been no user interaction for userTimeout seconds
timeoutTimer = new QTimer(0);
connect(timeoutTimer, SIGNAL(timeout()), this, SLOT(timeExpired()));
timeoutTimer->setInterval(userTimeout);
timeoutTimer->start();
}

/************************************************** ***************
* Name: sendUserInput
* Description: Send the line from stdin to the socket for sending to ARC
************************************************** ****************/
void ArcConsoleInput::sendUserInput(QString line)
{
QByteArray datagram = line.toUtf8();
udpSocketArcSender->writeDatagram(datagram.data(), datagram.size(), QHostAddress::LocalHost, quint16(port));
}

/************************************************** ***************
* Name: readLine
* Description: Read the line from stdin and process
************************************************** ****************/
void ArcConsoleInput::readUserLine()
{
QString line;
char buf[1024];
QRegExp rx("[a-z]*[0-9]{1,2}$");

stdin->readLine(buf, sizeof(buf));
line = QString::fromStdString(buf).trimmed().toLower();

if(line != NULL)
{
std::cout << "\n" << std::flush;

if((QString::compare(line, "quit", Qt::CaseInsensitive) == 0) ||
(QString::compare(line, "exit", Qt::CaseInsensitive) == 0))
{
qDebug("Exiting Console Application");
exit(1);
}
else if (QString::compare(line, "status", Qt::CaseInsensitive) == 0)
{
udpReceiverThread->setReplyReceived(false);
sendUserInput(line);
}
//more stuff here
else
{
qDebug() << "Unknown console input! " << line << "\nType help for assistance";
std::cout << "\n>" << std::flush;
}

restartTimer();

}
else //empty line - user interaction has occurred
{
std::cout << ">" << std::flush;
restartTimer();
}
}

/************************************************** ***************
* Name: restartTimer
* Description: User interaction has occurred restart the timer
************************************************** ****************/
void ArcConsoleInput::restartTimer()
{
if(timeoutTimer->isActive() == true)
timeoutTimer->stop();

if(timeoutTimer->isActive() == false)
timeoutTimer->start();
}

/************************************************** ***************
* Name: timeExpired
* Description: ArcConsoleApp will automatically close if no user interaction for "userTimeout" seconds
************************************************** ****************/
void ArcConsoleInput::timeExpired()
{
qDebug("\nTimeout: ArcConsoleApp is closing!");
delete udpSocketArcSender;
QCoreApplication::quit();
}


UdpThread.h

class UdpThread : public QThread
{
Q_OBJECT

public:

/*! Constructor */
UdpThread();

/*! Destructor */
~UdpThread();

/*! Wait for pending datagrams from the socket to read */
void run() override;

/*! set the value of the reply */
void setReplyReceived(bool r);

/*! get the value of replyReceived */
bool getReplyReceived();

private slots:

/*! Read all pending datagrams from the socket and print to screen */
void readPendingDatagrams();

/*! Slot for when a reply from ARC has not been received for replyTimeout */
void replyNotReceived();

private:
/*! 5 second timer for the receipt of a reply from ARC */
QTimer *replyTimer;

/*! UDP Socket to receive replies from the ARC */
QUdpSocket *udpSocketReceiver;

/*! Has a reply been received */
bool replyReceived;

Q_Log *logging;
};


UdpThread.cpp


#include <signal.h>
#include <iostream>
#include <QtNetwork>
#include <QString>

#include "UdpThread.h"

static const int replyTimeout = 1 * 5000; //5 seconds
#define RECEIVING_PORT 34568

/************************************************** ***************
* Name: UdpThread
* Description: Constructor.
************************************************** ****************/
UdpThread::UdpThread() : replyTimer(), udpSocketReceiver(), replyReceived(false), logging()
{
udpSocketReceiver = new QUdpSocket(this);
udpSocketReceiver->bind(QHostAddress::LocalHost, RECEIVING_PORT);


replyTimer = new QTimer(0);
connect(replyTimer, SIGNAL(timeout()), this, SLOT(replyNotReceived()));
replyTimer->setInterval(replyTimeout);
}

/************************************************** ***************
* Name: ~UdpThread
* Description: Destructor
************************************************** ****************/
UdpThread::~UdpThread()
{
delete udpSocketReceiver;
setTerminationEnabled(true);
qDebug("Closing UDP Receiver Thread in ARC Console App");
}

/************************************************** ***************
* Name: run
* Description: Wait for pending datagrams from the socket to read
************************************************** ****************/
[[noreturn]] void UdpThread::run(void)
{
QString msg;

for(;;)
{
if(udpSocketReceiver->hasPendingDatagrams())
{
readPendingDatagrams();
}
}
}

/************************************************** ***************
* Name: readPendingDatagrams
* Description: Read all pending datagrams from the socket and print to screen
************************************************** ****************/
void UdpThread::readPendingDatagrams()
{
QString msg;

while (udpSocketReceiver->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(static_cast<int>(udpSocketReceiver->pendingDatagramSize()));
QHostAddress senderAddr = QHostAddress::LocalHost;
quint16 senderPort = RECEIVING_PORT;

udpSocketReceiver->readDatagram(datagram.data(), datagram.size(),
&senderAddr, &senderPort);

//qDebug("%s", datagram.data());
qDebug() << datagram.data();
msleep(50); //to get all datagrams in the same request together without exiting while loop

if(replyTimer->isActive() == true)
replyTimer->stop();

setReplyReceived(true);
}
std::cout << "\n>" << std::flush;
}
/************************************************** ***************
* Name: setReplyReceived
* Description: set the value of the reply
************************************************** ****************/
void UdpThread::setReplyReceived(bool r)
{
replyReceived = r;

if(r == false) {
replyTimer->start();
}
}

/************************************************** ***************
* Name: getReplyReceived
* Description: get the value of replyReceived
************************************************** ****************/
bool UdpThread::getReplyReceived(void)
{
return replyReceived;
}

/************************************************** ***************
* Name: replyNotReceived
* Description: Slot for when a reply from ARC has not been received for replyTimeout
************************************************** ****************/
void UdpThread::replyNotReceived()
{
std::cout << "No reply received, try again or check that ARC is running!\n\n>" << std::flush;

if(replyTimer->isActive() == true)
replyTimer->stop();
}

taraj
11th December 2023, 00:52
If i change the line:
udpReceiverThread->moveToThread(udpReceiverThread);
to
udpReceiverThread->moveToThread(this->thread());

the replyNotReceived() works correctly. but i get both these errors:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
QObject::killTimer: Timers cannot be stopped from another thread

The QSocketNotifier error only happens once but the but the killTimer error happens every time there is a reply. Where there is no reply because the other application that is to response is not running, I get neither error.

Not sure if this helps?

Whage
4th January 2024, 21:27
Considering the transition from Qt4.8 to Qt5, it seems the issues are related to threading complexities, specifically with QSocketNotifier and killTimer. Have you considered other approaches or modifications to the threading mechanism that might resolve these errors while maintaining the correct functionality of replyNotReceived() in your application?