QUdpSocket loses validity - c++

I've a question regarding QUdpSocket:
I'm receiving UDP messages on port 50011 to multicast address 239.0.0.1 about every 0.2 seconds (Proven by wireshark).
The following code is working well (and printing "VALID" on every message) for ~ 1 minute. After that messageHandler is not being called by the QUdpSocket's signal readyRead anymore (even though I've double checked with wireshark, and messages are still being send).
ServiceDiscovery::ServiceDiscovery(QObject *parent) :
QObject(parent),
socket(new QUdpSocket(this))
{
socket->bind(QHostAddress::AnyIPv4, 50011, QUdpSocket::ShareAddress);
socket->joinMulticastGroup(QHostAddress("239.0.0.1"));
connect(socket, &QUdpSocket::readyRead,
this, &ServiceDiscovery::messageHandler,
Qt::DirectConnection);
}
void ServiceDiscovery::messageHandler()
{
if(socket->isOpen()) qDebug("OPEN"); // Does not print, ofc.
if(socket->isReadable()) qDebug("READABLE"); // Does not print.
if(socket->isValid()) qDebug("VALID"); // Suddenly stops.
QByteArray datagram;
while(socket->hasPendingDatagrams()) {
datagram.resize(socket->pendingDatagramSize());
socket->readDatagram(datagram.data(), datagram.size());
Message response(datagram);
if(response.deserialize()) {
if(response.getServiceID() == constants::servicediscovery::SERVICE_ID) {
QByteArray payload = response.getPayload();
if(payload.size() >= 48) {
QString address = QHostAddress(payload.mid(32, 4).toHex().toUInt(nullptr, 16)).toString();
quint16 port = payload.mid(38, 2).toHex().toUInt(nullptr, 16);
emit found(address, port);
}
}
}
}
}
What am I doing wrong here ?
Thanks in advance.
EDIT: Due to comments I pasted the datagram handling, too.

For the Records:
I had the same problem. The reason: A demo library my Application is relying on closes after the evaluation time not only its open ports but all ports of the application.

Related

readAll() from QSerialPort doesn't include the last response sent

I'm using Qt to control a serial device. If I send a command to my serial device, I do something like serial->write("command \r\n"). I made a push button which changes the text inside a plain text widget to the response of the serial port. To get the response of the serial port, I'm using serial->readAll(). The problem is it shows the 2nd to last response rather than the one I was expecting. Does Qt have some sort of buffer which is keeping hold of this response?
EDIT
I botched it by using recursion and compared the strings recieved
You might be calling readAll before the response is available. You should hook your code to the readyRead signal to be notified each time new chunk of data is ready to be read. Keep in mind that readyRead can be emitted with any number of bytes available to read - at a minimum, it'll be just one byte. You can't expect the data to be chunked/blocked in any particular way, since the serial port doesn't act as a message-based communication device. Your receiver code must be able to piece the data together from small chunks and act accordingly when it got all the data it needs.
For example, suppose that the device responses have a fixed, known length. You'd only want to react when a complete response has arrived. E.g.:
class Protocol : public QObject {
Q_OBJECT
QBasicTimer m_timer;
QPointer<QIODevice> m_port;
int m_responseLength = 0;
int m_read = 0;
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() != m_timer.timerId()) return;
m_timer.stop();
emit timedOut();
}
void onData() {
m_read += m_port->bytesAvailable();
if (m_read < m_responseLength)
return;
m_timer.stop();
emit gotResponse(m_port->read(m_responseLength));
m_read -= m_responseLength;
m_responseLength = 0;
}
public:
Q_SIGNAL void gotResponse(const QByteArray &);
Q_SIGNAL void timedOut();
Q_SLOT void sendCommand(const QByteArray & cmd, int responseLength, int cmdTimeout) {
m_responseLength = responseLength;
m_port->write(cmd);
m_timer.start(cmdTimeout, this);
}
explicit Protocol(QIODevice * port, QObject * parent = nullptr) :
QObject(parent), m_port(port) {
connect(m_port, &QIODevice::readyRead, this, &Protocol::onData);
}
};
...
Protocol protocol(0,0);
protocol.sendCommand({"foo"}, 10, 500);
QMetaObject::Connection cmd1;
cmd1 = QObject::connect(&protocol, &Protocol::gotResponse, [&]{
QObject::disconnect(cmd1);
qDebug() << "got response to foo";
});
QObject::connect(&protocol, &Protocol::timedOut, []{ qDebug() << "timed out :("; });

QSerialPort readyread() SIGNAL

I have a problem when receiving bytes from RS232 in QByteArray. I connected readyread() signal to call my serialport method and inside it I am reading bytes with readAll() to an QByteArray. Whenever data is available it rewrites QByteArray, but I want to receive it all, and then use data, but now I cannot because it is in parts. What to do?
Simply append to the array. You'll also need some criterion to determine when you've received all the data you wished. This can be, e.g. a given number of bytes:
class Communicator {
int expect;
QSerialPort port;
QByteArray reply;
void processReply() {
...
}
public:
Communicator() {
QObject::connect(&port, &QIODevice::readyRead, [this]{
reply += port.readAll();
if (expect && reply.size() >= expect) {
processReply();
reply.clear();
expect = 0;
}
});
...
};

QTCPSocket start by timer at the same time

I have problem with synchronize(start at the same time QTCPSocket) in my application I have 10 sockets. I have to read data at the similar time to all sockets. At this moment I have something that:
///...///
if(!socket->waitForConnected(-1))
{
qDebug() << "Server not found";
emit serverNotFound();
}else if(socket->state()==QAbstractSocket::ConnectedState){
qDebug() << "Connected"
connect(timer, SIGNAL(timeout()),this,SLOT(connected()));
timer->start(1000);
}
}
On connected signal:
void SocketsClass::connected()
{
sendRequest(socket, messageToServer);
}
The problem is that when the first socket get connected the timer starts for the one.
You can invert your approach. Don't wait for the sockets to get connected. Instead, check if the socket is connected in the slot activated by the timer. In that slot, you can iterate over all sockets and send the message to each of them.
Finally, you should never use Qt's waitForXxx methods, they lead to a horrible pseudo-synchronous code that is very error prone and hard to extend and maintain. Use the signal-slot mechanism instead.
Example:
SocketManager : public QObject {
Q_OBJECT
QTcpSocket m_sockets[10];
QTimer m_timer;
public:
SocketManager(QObject * parent = 0) : QObject(parent) {
... // connect all sockets here
m_timer.start(1000);
connect(&m_timer, &QTimer::timeout, [this]{
for (auto & socket : m_sockets)
if (socket.state() == QAbstractSocket::ConnectedState)
sendRequest(socket, messageToServer);
});
}
};

How to make blocking tcp socket with Qt?

I work with QTcpSocket. I need any write/read calls to the socket to be synchronous (blocking).
I know there is waitForReadyRead() and waitForBytesWritten(), but those two methods are marked in Qt documentation as they can fail randomly under Windows. I cannot affort this.
The blocking read is the most important (as reading comes always after writting a command to the other peer, so I know that if data reaches the other peer, it will answer).
I have tried 2 approaches.
First:
QByteArray readBytes(qint64 count)
{
int sleepIterations = 0;
QByteArray resultBytes;
while (resultBytes.size() < count && sleepIterations < 100)
{
if (socket->bytesAvailable() == 0)
{
sleepIterations++;
QThread::msleep(100);
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
continue;
}
resultBytes += socket->read(qMin(count, socket->bytesAvailable()));
}
return resultBytes;
}
This should wait for bytes to be available for reading on the socket, processing the event loop in the mean time, so the socket is doing it's necessary internal stuff.
Unfortunately - for unknown to me reason - the bytesAvailable() sometimes returns correct number of bytes, but sometimes it never returns anything greater than 0.
I know in fact that there was data to be read, because it used to work with the second approach (but it has it's own problems).
Second:
I have a kind of signal "blocker", which blocks current context and processes event loop, until certain signal is emitted. This is the "blocker":
SignalWait.h:
class SignalWait : public QObject
{
Q_OBJECT
public:
SignalWait(QObject *object, const char *signal);
bool wait(int msTimeout);
private:
bool called = false;
private slots:
void handleSignal();
};
SignalWait.cpp:
SignalWait::SignalWait(QObject* object, const char* signal) :
QObject()
{
connect(object, signal, this, SLOT(handleSignal()));
}
bool SignalWait::wait(int msTimeout)
{
QTime timer(0, 0, 0, msTimeout);
timer.start();
while (!called && timer.elapsed() < msTimeout)
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return called;
}
void SignalWait::handleSignal()
{
called = true;
}
and then I used it like this:
SignalWait signalWait(socket, SIGNAL(readyRead()));
// ...
// socket->write(...);
// ...
if (!signalWait.wait(30000))
{
// error
return;
}
bytes = socket->read(size);
This approach seems to be working better, but it also fails from time to time. I don't know why. It's like the readyRead() signal was never emitted and the SignalWait keeps waiting, until it times out.
I'm out of ideas. What is the proper way to deal with it?
I would suggest to use the asynchronous approach but if you really want to go with the synchronous way, then a better way is to use a local event loop:
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
loop.connect(socket, SIGNAL(readyRead()), SLOT(quit()));
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
while (resultBytes.size() < count)
{
timer.start(msTimeout);
loop.exec();
if(timer.isActive())
resultBytes += socket->read(qMin(count, socket->bytesAvailable()));
else
break;
}
Here it waits until count bytes are read or the the timeout reaches.

Qt QUdpSocket: readyRead() signal and corresponding slot not working as supposed

I have problems to find why my short QUdpSocket example is not working. I plan to use only one UDP socket to read and write to an embedded device at 192.168.2.66 on port 2007. The device will reply always on port 2007 to the sender. I tested the device with an UDP terminal software and works as I said. So, I designed a simple class to embed the functions needed to manage the device:
class QUdp : public QObject
{
// Q_OBJECT
public:
explicit QUdp(QObject *parent = 0, const char *szHost = 0, uint16_t wPort = 0);
~QUdp();
bool Open();
int64_t Write(QByteArray &data);
int64_t Write(QString strData);
private:
QString m_strHost;
uint16_t m_wPort;
QUdpSocket *OUdp;
private slots:
void received();
};
I suppose that the problem is in the Open method:
bool QUdp::Open()
{
QHostAddress OHost;
connect(OUdp, &QUdpSocket::readyRead, this, &QUdp::received);
bool zRet = OUdp->bind(QHostAddress::AnyIPv4, m_wPort, QUdpSocket::ShareAddress);
OHost.setAddress(m_strHost);
OUdp->connectToHost(OHost, m_wPort, QIODevice::ReadWrite);
return(zRet);
}
//------------------------------------------------------------------------
I used the Qt 5 syntax for the connect(), m_strHost value is "192.168.2.66" and m_wPort is 2007
my Write method is very simple (the part inside #if 0 was added to see if the socket received any data)
int64_t QUdp::Write(QString strData)
{
QByteArray data(strData.toStdString().c_str(), strData.length());
int64_t iCount = OUdp->write(data);
#if 0
bool zRecved = OUdp->waitForReadyRead(3000);
int64_t iRecvCount = OUdp->bytesAvailable();
#endif
return(iCount);
}
//------------------------------------------------------------------------
and this is my test received() method... I wrote it just to see if the signal-slot works or not:
void QUdp::received()
{
int64_t iRecvCount = OUdp->bytesAvailable();
}
//------------------------------------------------------------------------
I don't understand what is wrong.. I found some posts saying that is not possible read and write using only one UDP socket in Qt (Qt uses BSD sockets so it should be possible) but my example looks as the proposed solutions so I really don't understand what is not working.
You can read and write using just one UDP socket in Qt. I have this running in Qt5 on both windows and Linux, so no worries there :)
To establish Rx direct comms in QUdpSocket you should really use the bind() function, something like this:
// Rx connection: check we are not already bound
if (udpSocket->state() != udpSocket->BoundState)
{
// Rx not in bound state, attempt to bind
udpSocket->bind(address, port);
}
Once this has completed you will be able to check that udpSocket->state() == udpSocket->BoundState is true, then you are successfully "bound" to this ip/port. Now your listening can begin if your connection to readready() is correct. I have not used this connection syntax that you are using, so I can't say much about that, but here is the example of how I connect:
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(rxDataEvent()), Qt::QueuedConnection);
Where "this" is the class which contains my QUdpSocket and udpSocket is a QUdpSocket pointer. Then rxDataEvent is defined below:
void CIpComms::rxDataEvent(void)
{
QByteArray rxData;
QHostAddress sender;
quint16 senderPort;
while (udpSocket->hasPendingDatagrams())
{
// Resize and zero byte buffer so we can make way for the new data.
rxData.fill(0, udpSocket->pendingDatagramSize());
// Read data from the UDP buffer.
udpSocket->readDatagram(rxData.data(),
rxData.size(),
&sender,
&senderPort);
// Emit ipDataReceived Signal
emit ipDataReceived(rxData);
}
}
Here we continually check for datagrams until there are none pending (bit easier then doing the whole "bytesAvailable thing") and stick the data into a QByteArray and emit it off elsewhere (which you obviously don't have to do!).
That is all you need to do for connection. Then to send is very easy, you simply have to call writeDatagram(), well there are other options but this is by far the easier to use:
if (-1 == udpSocket->writeDatagram(txData, address, port))
{
// Data write failed, print out warning
qWarning() << "Unable to write data to " << address.toString() << ":" << port << endl;
return false;
}
I have pretty much cut and pasted this from my working code (with a few edits to keep it short-n-simple so it should give you a starting point. In summary where I believe you are going wrong is that you have not "bound" to the IP address/port and are therefore NOT listening to it and will not receive any readReady() events.