我的任务是处理读取速率约为10kHz的UDP数据。我使用的是Qt 5.13.1(MinGW32(,所以我尝试使用QUdpSocket
我做了一个简单的测试程序,但结果有点令人沮丧。readyRead()
信号太慢。由于某种原因,我每2-4个信号就会延迟1或2毫秒
我制作了一个简单的数据包计数器,并将其与我在wireshark中看到的进行了比较。果不其然,有一个数据包丢失了。
我能做些什么来提高绩效?或者这只是Qt事件循环的极限?
我用Qt Creator 4.10.0运行它。在Windows 7。
更新:根据您的建议:我尝试过:
-
在不同的线程中移动套接字。这会带来更多的性能..一个非常小的
-
LowDelayOption=1-我没有注意到有任何变化
-
ReceiveBufferSizeSocketOption-我没有注意到有任何变化
- 阅读时没有使用QDebug-我没有检查它,只是用于收集统计数据
udapproc.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();
}
networker.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
networker.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;
}
}
}
您无法在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上的这个函数没有任何作用。
在测量代码的时间关键部分时,我建议避免使用qDebug(或任何其他缓慢的打印/调试功能(。它可能对你的实际测量有太大的影响。
我建议您将从QElapsedTimer接收到的定时值存储到一个单独的容器中(例如,QVector,或者只是一个随时间平均的qint64(,并且偶尔只显示一次调试消息(每秒或仅在最后(。这样,由测量引起的开销影响较小。较长时间内的平均值也将有助于测量结果的差异。
我还建议您使用QElapedTimer::nsecsElapsed,以避免在高频情况下出现舍入问题,因为QElapedThiemr::exposed将始终舍入到最接近的毫秒(并且您已经在1ms区域内测量东西(。
当实际显示结果时,您总是可以将纳秒转换为毫秒。
您在10kHz频率下接收的数据大小是多少?