3

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);
}
viduwoy
  • 113
  • 12
  • `bool`ean variables should be set to/tested against `true`/`false`, not `1`/`0`. Even though it's *possible* it's just a bad idea and confusing (you make readers think it's a `int`). – Jesper Juhl Sep 05 '18 at 16:24

2 Answers2

4

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.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • What would error checking look like in this case? This is a stripped down version of my code where the problem lies, idk, but I might have "error checking" already, would you mind clarifying? – viduwoy Sep 05 '18 at 16:21
  • 1
    When you call a method on a socket, don't ignore its return value unless you can explain why you do so. In your example code you must check return values of `bind`, `writeDatagram`, etc. – Kuba hasn't forgotten Monica Sep 05 '18 at 18:24
  • I've been working on this again for a day and I am not sure where it is trying to bind the port again. It binds it once then moves to ready read. Where is the second failed bind? – viduwoy Sep 06 '18 at 18:38
  • Ports for a given IP address+IP protocol pair are a system-global resource. You have to think of all the processes that bind to the ports, not just the one process you think of. Remember that you're starting *two* processes. They *each* try to bind. Only the first *process* will succeed. And no other *process* will until the very first process terminate or unbind the socket, whichever comes first. It doesn't matter that *within the process* you only bind once - you bind multiple times across the processes. – Kuba hasn't forgotten Monica Sep 07 '18 at 14:40
  • Thank you, I fixed the problem. If you have time, how would I get the ports to change with each instance I bring up? – viduwoy Sep 07 '18 at 19:12
  • For the either socket: just don't specify the bind port - remove the port argument. `bind()` will then bind to a random port. You don't need a fixed receive port as long as one end of the conversation has a known port number. The endpoint `R` with a random port first sends a packet to the other endpoint `K` with known port. The `K`endpoint sets up a "conversation partner" record for the host_address/random_port combination of the `R` endpoint, and sends all replies to `R` based on that record. The record would keep the state of the protocol that runs on top of UDP, i.e. pending command, etc. – Kuba hasn't forgotten Monica Sep 08 '18 at 01:59
1

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();
}