【问题标题】:Read (many) values from QTcpSocket (fast)从 QTcpSocket 读取(许多)值(快速)
【发布时间】:2018-01-09 19:27:30
【问题描述】:

我正在使用一个测量设备,它使用最高 70 kHz 的 tcp 套接字发送(二进制)浮点值。

我的目标是尽可能快地读取这些值并将它们用于我程序的其他部分。

到目前为止,我能够使用 QTcpSocket 和 QDataStream 按值提取值:

首先我创建套接字并将流连接到它

mysock = new QTcpSocket(this);
mysock->connectToHost(ip, port);
QDataStream stream(mysock);
stream.setByteOrder(QDataStream::LittleEndian);
stream.setFloatingPointPrecision(QDataStream::SinglePrecision);

然后我从套接字读取并将流数据写入我的浮点值

while(true) //only for test purpose (dont stop reading)
if (mysock->waitForReadyRead())
{
    while (mysock->bytesAvailable() >= 6)
    {
        QByteArray a = mysock->read(6); //each value sent is 6 bytes long
        stream.skipRawData(2); //first 2 bytes don't belong to the number
        float result;
        stream >> result;
        //qDebug()<<result;
    }
}

当我测量 while(true) 循环的迭代频率时,我能够达到大约 30 kHz。 每次读取多个值我可以达到 70 Khz。 (不考虑其他可能会减慢我速度的计算)

我的问题是:

  • 如果我一次读取多个值,如何从 QDataStream 中提取这些值?我需要一个 6 个字节的间距,其中只有 4 个字节包含该值。

答案:在我的情况下,有 2 个字节(垃圾)后跟已知数量的值,例如浮点数为 4 个字节,另一个浮点数为 4 个字节,uint16 为 2 个字节。

stream >> trashuint16 >> resultfloat1 >> resultfloat2 >> resultuint16
  • 扩展 1:我可以配置我的设备以发送需要写入不同变量的不同类型(int、float)的不同值。

答案:相同。

  • 是否有更有效的方法从 QTcpSocket 读取多个值?

答案:在 cmets 中回答。

更新(回答一些问题):

  • 以字节为单位的最大速率:70 kHz x 6 字节(对于一个值)= 420 kB/s(似乎没有那么多:))

更新 2

  • 新问题:当我开始交易(使用stream.startTransaction)时,我想知道二进制代码流中的内容。
  • 我不明白QDataStream::startTransaction 的工作原理。将读取多少字节?我没有使用 &gt;&gt; 提取的数据会怎样?

我尝试了以下方法:

if (mysock->waitForReadyRead())
{
    stream.startTransaction();

    char *c = new char[40];
    stream.readRawData(c, 40);    //I want to know whats really inside    
    QByteArray a(c);
    qDebug() << a <<stream.status();
    if (!stream.commitTransaction())
        break;
}

一次又一次地这样做,我有时会得到 status = -1(读取太多),有时不会。如何获取流的“大小”?

【问题讨论】:

  • 你能告诉我最大速率是多少字节吗?
  • 读取更大的字节块并对其进行计数,以确保所有字节都在您的套接字缓冲区中接收。然后在循环之前删除QByteArray a。不要在循环中定义新的内存部分,定义一个缓冲区然后附加到它。
  • 首先考虑Qt是一个事件驱动的框架。使用 while(true) 不是最好的方法。 This answer 应该有帮助。
  • 我同意@TheDarkKnight。可能你看到了一个性能问题,你想:“让我们使用线程来提高性能”,而不是理解这个问题。多线程很难。它的错误非常难以找到,因此由于您是新手,所以在您获得一些经验之前根本不要使用它们。它会为您节省很多时间和痛苦。

标签: c++ qt qtcpsocket qdatastream


【解决方案1】:

您的代码有几个错误。

在使用QDataStream 的同时,您正在从套接字进行直接读取。这可能会破坏东西。

您的代码还假设您的应用程序将接收与另一端发送的数据块相同的数据。你没有这样的保证!您可能会收到在帧中间结束的块数据。它只是靠运气,或者你忽略了应用程序的一些错误。

这应该是这样的:

while(true)
if (mysock->waitForReadyRead()) // IMO doing such loop is terrible approach
// but this is Out of the scope of question, so ignoring that
{
    while (true)
    {
        stream.startTransaction();
        float result;
        qint32 somedata
        stream >> somedata >> result; // I do not know binary format your application is using

        if (!in.commitTransaction())
            break;

        AddDataToModel(result, somedata);
    } 
}


编辑:

来自评论:

如果我错了,请纠正我,但如果我想丢弃 2 个字节,我需要执行“stream >> someint(2 byte) >> somefloat(4 byte)”吗?如何处理流中的多个值?

qint16 toBeDiscarded;
float value; 
// note stream.setFloatingPointPrecision(QDataStream::SinglePrecision); 
// is needed to read float as 32 bit floating point number

stream >> toBeDiscarded >> value;
ProcessValue(value);

【讨论】:

  • 似乎我已经将 QTcpSocket 与 QDataStream 结合起来的概念错误了。如果您能更详细地解释您的第一点,那就太好了。您关于二进制格式的问题:6 字节,前 2 字节为 0xff 0xff,然后 4 字节包含浮点值(单精度)。要使用 startTransaction() 我需要升级 QT(我在 5.6)
  • 当你写旧的 qt 时,它没有缓存。至少我检查了 Qt4.8 的源代码,它直接从套接字读取数据,以防事务实现它有一个缓存。
  • 我检查了 Qt 源我错了 :(。QDataStream 没有任何类型的缓冲区,这是由 IODevice (socket) 处理的。
  • 我能够更新到 QT5.8(没有用于 msvc2013 32bit 的预编译 Qt5.9)。调整您的代码并使其运行。好像我从来没有理解 >> 运算符。如果我错了,请纠正我,但是如果我想丢弃 2 个字节,我需要执行“stream >> someint(2 byte) >> somefloat(4 byte)”吗?如何处理流中的许多值?那我需要循环流>>吗?
  • QStream 数据以二进制方式读取数据,无论您运行的是什么平台,都具有定义的字节序处理。 Qt 也在处理大端的平台。它还有助于维护具有多种标准并且在不同平台上可以具有各种格式的浮点数。大多数网络标准都使用大端,而现在大多数处理器都使用小端,所以你必须处理它。
猜你喜欢
  • 1970-01-01
  • 2013-11-09
  • 2013-08-03
  • 2012-07-09
  • 2014-02-16
  • 1970-01-01
  • 2016-12-02
  • 2019-07-25
相关资源
最近更新 更多