How to constantly read UDP packets in QT?

Viewed 1533

I am sending myself UDP packets generally using this method here:

How to receive proper UDP packet in QT?

I am setting up modes so that in Write mode, it sends UDP packets every second, and in Read mode, it just receives packets. To do this I set up a simple boolean variable called readmode when readmode = true it reads and writes when = false. I left the connect(socket, SIGNAL(readyRead()), this,SLOT(readyRead())); and the call to the send function in the MainWindow constructor and put the if statements, testing the state of readMode to actually do something, in the send function and readyRead() function themselves.

My problem is that I only receive packets whenever I start the program in Read mode first before opening the window for the program to write. If I start sending packets before I open the Read program, I get nothing.

For clarification I am opening two instances of this program and I set one to Read and the other to Write, but the Read has to be started first for it to work, but once states are changed, it stops working.

How can I get it to read all the time?

Code sample:

bool readMode = true; //Write mode = false, Read Mode = true

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    timer = new QTimer(this);
    timer->start(1000);

    ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Read");
    ui->buttonBox->button(QDialogButtonBox::Cancel)->setText("Write");

    out_socket = new QUdpSocket(this);
    out_socket->bind(60500);

    in_socket = new QUdpSocket(this);
    in_socket->bind(60510);

    connect(timer, SIGNAL(timeout()),this,SLOT(UDP_test()));

    connect(in_socket,SIGNAL(readyRead()),this,SLOT(readyRead()));
}    

void MainWindow::UDP_test()                                             //Write Mode
{    
if (readMode == false) //Write Mode                                         
{

    QByteArray Data;
    Data.append(fullResultString);
    out_socket->writeDatagram(Data,QHostAddress("192.168.127.10"),60510);
}           

void MainWindow::readyRead()                                            //Read Mode
{
    if (readMode == true) //Readmode
    {
        while(in_socket->hasPendingDatagrams())
        {
            QByteArray UDPBuffer;

             UDPBuffer.resize(in_socket->pendingDatagramSize());
             QHostAddress sender;
             quint16 senderPort;
             in_socket->readDatagram(UDPBuffer.data(),UDPBuffer.size(), &sender, &senderPort);
}
2 Answers

That's to be expected: each invocation of the process attempts to bind the input socket. The first time, with the socket not bound yet - it succeeds. The second time - it fails, as the port is already bound - so reading won't work. Everything aside, the code never checks for errors, and thus leaves the user (i.e. you) unaware that things have failed. With error checking it'd be obvious why it doesn't work. The output socket doesn't need to be bound to an explicitly specified port number. The input socket should only be bound when the program is in receive mode.

QUdpSocket doesn't emit readyRead if it already contains (not read) datagramms when it receives a new datagram. You source code doesn't complete. So we can't see how do you change your global variable 'readMode'.

here the simple demo application:

#include <QApplication>
#include <QWidget>
#include <QUdpSocket>
#include <QPushButton>
#include <QListWidget>
#include <QGridLayout>
#include <QDebug>
#include <QUuid>

int main( int argc, char** argv ) {
    QApplication app( argc, argv );

    QWidget form;
    auto*const log_widget = new QListWidget( &form );
    auto*const client = new QUdpSocket( qApp );
    auto*const server = new QUdpSocket( qApp );
    const quint16 TEST_PORT = 31088;

    if( ! server->bind( QHostAddress::LocalHost, TEST_PORT ) ) {
        exit( 1 );
    }

    QObject::connect( server, &QUdpSocket::readyRead, [=]() {
        log_widget->insertItem( 0, "readyRead() has been received" );
    });

    auto*const write_button = new QPushButton( "&Write", &form );
    QObject::connect( write_button, &QPushButton::clicked, [=]() {
        const auto packet = QUuid::createUuid().toByteArray();
        client->writeDatagram( packet, QHostAddress::LocalHost, TEST_PORT );
        log_widget->insertItem( 0, "A datagram has been sent." );
    });

    auto*const read_button = new QPushButton( "&Read", &form );
    QObject::connect( read_button, &QPushButton::clicked, [=]() {
        if( server->hasPendingDatagrams() ) {
            const int size = static_cast< int >( server->pendingDatagramSize() );
            if( size > 0 ) {
                QByteArray packet;
                packet.resize( size );
                server->readDatagram( packet.data(), size );
                log_widget->insertItem( 0, "Read OK: datagram have been read." );
            } else {
                log_widget->insertItem( 0, "Read Error: invalid datagram size." );
            }
        } else {
            log_widget->insertItem( 0, "Read Error: there is no any datagram." );
        }
    });

    auto*const check_button = new QPushButton( "&Check", &form );
    QObject::connect( check_button, &QPushButton::clicked, [=]() {
        if( server->hasPendingDatagrams() ) {
            log_widget->insertItem( 0, "Check: there is at least one datagram." );
        } else {
            log_widget->insertItem( 0, "Check: there is no any datagram." );
        }
    });

    auto*const grid = new QGridLayout( &form );
    grid->addWidget( write_button, 0, 0 );
    grid->addWidget( read_button, 0, 1 );
    grid->addWidget( check_button, 0, 2 );
    grid->addWidget( log_widget, 1, 0, 1, 3 );

    form.show();

    return app.exec();
}
Related