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

11.1k views Asked by At

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.

1

There are 1 answers

0
code_fodder On

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.