1

我有一个以~10kHz 读取速率处理 UDP 数据的任务。我正在使用 Qt 5.13.1 (MinGW32),所以我尝试使用QUdpSocket.
我做了一个简单的测试程序,但结果有点令人沮丧。readyRead()信号太慢了。出于某种原因,我每 2-4 个信号延迟超过 1 或 2 毫秒。
我做了一个简单的数据包计数器,并将它与我在wireshark中看到的进行比较。果然有丢包。

我可以做些什么来提高性能?或者也许这只是 Qt 事件循环的限制?

我用 Qt Creator 4.10.0 运行它。在 Windows 7 上。

更新:根据您的建议:我尝试过:

  1. 在不同的线程中移动套接字。这提供了更多的性能.. 非常一点

  2. LowDelayOption = 1 - 我没有注意到任何变化

  3. ReceiveBufferSizeSocketOption - 我没有注意到任何变化

  4. 阅读时不使用 QDebug - 我没有检查它,仅用于收集统计信息

udpproc.h

#ifndef UDPPROC_H
#define UDPPROC_H

#include "QObject"
#include "QUdpSocket"
#include "QHostAddress"
#include "QThread"

#include "QDebug"
#include "networker.h"

class UDPProc : public QObject
{
    Q_OBJECT
public:
    UDPProc();
    ~UDPProc();
private:
    QUdpSocket dataServerSocket;
    NetWorker* netWorker;
    QThread netThread;


};

#endif // UDPPROC_H

udpproc.cpp

UDPProc::UDPProc() {

netWorker = new NetWorker(&dataServerSocket);
netWorker->moveToThread(&netThread);
netWorker->getInnerLoop()->moveToThread(&netThread);

connect(&netThread, SIGNAL(started()), netWorker, SLOT(serverSocketProccessing()));
connect(&this->dataServerSocket, SIGNAL(readyRead()), netWorker->getInnerLoop(), SLOT(quit()));

QString address = "127.0.0.3:16402";
QHostAddress ip(address.split(":").at(0));
quint16 port = address.split(":").at(1).toUShort();
dataServerSocket.bind(ip, port);

//dataServerSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1);
dataServerSocket.moveToThread(&netThread);

netThread.start();

//dataServerSocket.setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 128000);
//qDebug()<<dataServerSocket.socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption).toInt();

}

网络人.h

#ifndef NETWORKER_H
#define NETWORKER_H

#include <QObject>
#include "QElapsedTimer"
#include "QEventLoop"
#include "QUdpSocket"
#include "QVector"

class NetWorker : public QObject
{
    Q_OBJECT
private:

    QElapsedTimer timer;
    QVector<long long> times;

    QEventLoop loop;
    QUdpSocket *dataServerSocket;

    char buffer[16286];
    int cnt = 0;
public:
    NetWorker(QUdpSocket *dataServerSocket);
    ~NetWorker();
    QEventLoop * getInnerLoop();

public slots:
    void serverSocketProccessing();

};

#endif // NETWORKER_H

网络人.cpp

#include "networker.h"

NetWorker::NetWorker(QUdpSocket *dataServerSocket)
{
    this->dataServerSocket = dataServerSocket;
}

NetWorker::~NetWorker()
{
    delete dataServerSocket;
}

QEventLoop *NetWorker::getInnerLoop()
{
    return &loop;
}

void NetWorker::serverSocketProccessing()
{
    while(true){
        timer.start();
        loop.exec();
        times<<timer.nsecsElapsed();
        while(dataServerSocket->hasPendingDatagrams()){
            dataServerSocket->readDatagram(buffer, dataServerSocket->pendingDatagramSize());
        }
        if (times.size() >= 10000){
            long long sum = 0;
            for (int x : times){
                //qDebug()<<x;
                sum += x;
            }
            qDebug() << "mean: "<<sum/times.size();
            break;
        }

    }
}
4

2 回答 2

2

您无法在 Windows 上以如此高的速率接收套接字数据包。这是操作系统的限制。即使使用QAbstractSocket::LowDelayOption并且如果将接收代码移动到无限循环中,如下所示:

socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
...

for (;;)
{
    if(socket->waitForReadyRead(1)) // waits for some events anyway
    {
        // read here
    }
}

或者,您可以在数据包结构中嵌入一些时间码字段,然后一起发送多个数据包,或者使用一些没有丢包的连接。例如,使用TCP 连接 + 事务,因为套接字可能会出现以下情况:

  • 收到完整的数据包
  • 只收到部分数据包
  • 一起收到了几个包

此外,不要尝试更改 readBufferSize:

如果缓冲区大小被限制在某个大小,QAbstractSocket 不会缓冲超过这个大小的数据。特殊情况下,缓冲区大小为 0 意味着读取缓冲区不受限制,并且所有传入数据都被缓冲。这是默认设置。

如果您只在特定时间点读取数据(例如,在实时流应用程序中),或者如果您想保护您的套接字不接收太多数据,则此选项很有用,这可能最终导致您的应用程序用尽记忆。

只有 QTcpSocket 使用 QAbstractSocket 的内部缓冲区;QUdpSocket 根本不使用任何缓冲,而是依赖于操作系统提供的隐式缓冲。因此,在 QUdpSocket 上调用此函数没有任何效果。

于 2019-10-29T10:23:04.310 回答
1

在测量代码的时间关键部分时,我建议避免使用 qDebug(或任何其他慢速打印/调试功能)。它可能对您的实际测量影响太大。

我的建议是将接收自 QElapsedTimer 的计时值存储到一个单独的容器(例如 QVector,或者只是一个随时间平均的 qint64),并且只偶尔显示一次调试消息(每秒或仅在结尾)。这样,由测量引起的开销影响较小。较长时间的平均也将有助于测量结果的差异。

我还建议您使用 QElapsedTimer::nsecsElapsed 来避免高频情况下的舍入问题,因为 QElapsedTiemr::elapsed 将始终舍入到最接近的毫秒(并且您已经在 1ms 区域内测量事物)。

在实际显示结果时,您始终可以稍后将纳秒转换为毫秒。

您以 10kHz 的速率接收的数据大小是多少?

于 2019-10-28T22:01:43.450 回答