【问题标题】:How to constantly read UDP packets in QT?如何在 QT 中不断读取 UDP 数据包?
【发布时间】:2018-09-06 02:39:15
【问题描述】:

我在这里通常使用这种方法向自己发送 UDP 数据包:

How to receive proper UDP packet in QT?

我正在设置模式,以便在写入模式下,它每秒发送 UDP 数据包,而在读取模式下,它只接收数据包。为此,我设置了一个名为 readmode 的简单布尔变量,当 readmode = true 它在 = false 时进行读写。我将connect(socket, SIGNAL(readyRead()), this,SLOT(readyRead())); 和对send 函数的调用留在MainWindow 构造函数中,并将if 语句放在发送函数和readyRead() 函数本身中测试readMode 的状态以实际执行某些操作。

我的问题是,只有在打开程序写入窗口之前先以读取模式启动程序时,我才会收到数据包。如果我在打开 Read 程序之前开始发送数据包,我将一无所获。

为了澄清起见,我打开了这个程序的两个实例,我将一个设置为读取,另一个设置为写入,但是必须首先启动读取才能使其工作,但一旦状态发生更改,它就会停止工作。

我怎样才能让它一直阅读?

代码示例:

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

【问题讨论】:

  • boolean 变量应该设置/测试true/false,而不是1/0。尽管它可能只是一个坏主意和令人困惑的(你让读者认为这是int)。

标签: c++ qt sockets udp


【解决方案1】:

这是意料之中的:进程的每次调用都会尝试绑定输入套接字。第一次,套接字尚未绑定 - 它成功了。第二次 - 它失败,因为端口已经绑定 - 所以读取将不起作用。除了一切,代码从不检查错误,因此用户(即您)不会意识到事情已经失败。通过错误检查,很明显为什么它不起作用。输出套接字不需要绑定到明确指定的端口号。只有在程序处于接收模式时才应该绑定输入套接字。

【讨论】:

  • 在这种情况下错误检查会是什么样子?这是我的代码的精简版本,问题所在,idk,但我可能已经进行了“错误检查”,您介意澄清一下吗?
  • 当你在一个套接字上调用一个方法时,不要忽略它的返回值,除非你能解释你为什么这样做。在您的示例代码中,您必须检查 bindwriteDatagram 等的返回值。
  • 我又为此工作了一天,但我不确定它试图再次绑定端口的位置。它绑定它一次,然后移动到就绪读取。第二次失败的绑定在哪里?
  • 给定 IP 地址+IP 协议对的端口是系统全局资源。您必须考虑绑定到端口的所有进程,而不仅仅是您想到的一个进程。请记住,您正在启动 两个 进程。他们每个试图绑定。只有第一个进程会成功。在第一个进程终止或解除绑定套接字之前,没有其他 进程 会,以先到者为准。 在进程内你只绑定一次并不重要 - 你在进程中绑定多次。
  • 谢谢,我解决了这个问题。如果您有时间,我将如何让端口随着我启动的每个实例而更改?
【解决方案2】:

如果 QUdpSocket 在接收到新数据报时已经包含(未读取)数据报,则不会发出 readyRead。您的源代码未完成。所以我们看不到如何更改全局变量“readMode”。

这里是简单的演示应用程序:

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

【讨论】:

  • 我使用按钮来改变状态。按下 Read 时,readMode 更改为 true,按下 Write 时,readMode 更改为 false。
  • 所以我们有以下场景:
  • 2.按下“Write”按钮,readMode 设置为 false,将发送数据报
  • 3. readyRead() 调用(通过套接字的信号),但 readMode 为 false,我们不读取接收到的数据报。
  • 4.下一次写入不会产生socket的readyRead信号,我们的readyRead槽也不会被调用。
猜你喜欢
  • 1970-01-01
  • 2020-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-24
  • 2017-07-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多