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 */
/*! A SocketNotifier to read from Standard Input */
/*! A file to read standard input from */
/*! A UdpThread that will receive replies from the ARC */
UdpThread* udpReceiverThread;
/*! A UDP socket to send requests to the ARC software */
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;
To copy to clipboard, switch view to plain text mode
ArcConsoleInput.cpp
void ArcConsoleInput::networkSetup()
{
//create a sender UDP socket
//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
connect(notifier, SIGNAL(activated(int)), this, SLOT(readUserLine()));
//Create a timer for when there has been no user interaction for userTimeout seconds
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
) {
udpSocketArcSender
->writeDatagram
(datagram.
data(), datagram.
size(),
QHostAddress::LocalHost, quint16
(port
));
}
/*****************************************************************
* Name: readLine
* Description: Read the line from stdin and process
******************************************************************/
void ArcConsoleInput::readUserLine()
{
char buf[1024];
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;
}
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();
}
To copy to clipboard, switch view to plain text mode
UdpThread.h
{
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 */
/*! UDP Socket to receive replies from the ARC */
/*! Has a reply been received */
bool replyReceived;
Q_Log *logging;
};
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;
};
To copy to clipboard, switch view to plain text mode
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
->bind
(QHostAddress::LocalHost, RECEIVING_PORT
);
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)
{
for(;;)
{
if(udpSocketReceiver->hasPendingDatagrams())
{
readPendingDatagrams();
}
}
}
/*****************************************************************
* Name: readPendingDatagrams
* Description: Read all pending datagrams from the socket and print to screen
******************************************************************/
void UdpThread::readPendingDatagrams()
{
while (udpSocketReceiver->hasPendingDatagrams())
{
datagram.resize(static_cast<int>(udpSocketReceiver->pendingDatagramSize()));
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();
}
#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();
}
To copy to clipboard, switch view to plain text mode
Bookmarks