0

I'm using a measurement device which sends (binary) float values using a tcp socket with up to 70 kHz.

My goal is to read these values as fast as possible and use them in other parts of my program.

Till now I'm able to extract value by value using a QTcpSocket and QDataStream:

First I create the socket and connect the stream to it

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

Then I read from the socket and write the stream data to my float value

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

When I measure the iteration frequency of the while(true) loop I'm able to achieve about 30 kHz. Reading multiple values per read I can reach up to 70 Khz. (Not taking other calculations into account which might slow me down)

My questions are:

  • If I read multiple values at once, how do I extract these values from the QDataStream? I need a 6 bytes spacing with only 4 bytes containing the value.

Answer: In my case there is 2 bytes (trash) followed by a known number of values, for example 4 bytes for a float, 4 bytes for another float, 2 bytes for an uint16.

stream >> trashuint16 >> resultfloat1 >> resultfloat2 >> resultuint16
  • Expands 1: I can configure my device to send different values of different type (int, float) which need to be written to different variables.

Answer: Same.

  • Is there a more efficient way to read many values from a QTcpSocket?

Answer: Anwered in the comments.

Update (to answer some questions):

  • Max rate in Bytes: 70 kHz x 6 Byte (for one value) = 420 kB/s (Doesnt seem that much :))

Update 2

  • New Question: When i start a transaction (using stream.startTransaction) I would like to know whats inside that stream in binary code.
  • I dont understand how QDataStream::startTransaction works. How many bytes will be read? what happens with the data I dont extract using >>?

I've tried the following:

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

Doing this again and again, I'll sometimes get status = -1 (read too much) and sometimes not. How do I get the "size" of the stream?

Famok
  • 3
  • 4
  • Can you tell me what is Max rate in Bytes ? – Farhad Aug 02 '17 at 14:04
  • Read a bigger chunk of bytes and count it, to make sure all bytes is receiving in your socket buffer. then remove `QByteArray a` before loop. do NOT define a new part of memory in a loop, define a buffer then append to it. – Farhad Aug 02 '17 at 14:13
  • 2
    First consider that Qt is an event driven framework. Using a while(true) is not the best way of doing this. [This answer](https://stackoverflow.com/questions/26909682/tcp-packets-using-qtcpsocket/26909955#26909955) should help. – TheDarkKnight Aug 02 '17 at 14:21
  • I agree with a @TheDarkKnight. Probably you you saw a performance issue, you thought: "lets use threads to improve performance", instead understanding the issue. Multi threading is hard. Bugs for it are extremely hard to find, so since you are newbie do not use them at all until you will gain some exp. It will save you lots of time an pain. – Marek R Aug 02 '17 at 15:05

1 Answers1

2

Your code has couple mistakes.

You are doing direct reading from socket when in the same time you are using QDataStream. This can break stuff.

Also your code is assuming that your application will receive data in same chunks as it was sent by other end. You do not have such warranty! It may happen that you will receive chunk data which are ending in middle of your frame. It works just by pure luck or you are ignoring some bugs of your application.

This should go like this:

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


Edit:

From comment:

Please correct me if I'm wrong, but if I want 2 bytes to be discarded I need to do "stream >> someint(2 byte) >> somefloat(4 byte)"? How can I handle many values in stream?

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);
Marek R
  • 32,568
  • 6
  • 55
  • 140
  • Seems like I've got the concept on QTcpSocket in combination with QDataStream wrong. It would be great if you could explain your first point a little bit more in detail. Your question about the binary format: 6 byte, with 0xff 0xff for the first 2 byte and then 4 byte containing the float value (single precission). To use startTransaction() I'll need to upgrade QT (I'm on 5.6) – Famok Aug 02 '17 at 15:39
  • When you writting about older qt that it doesn't have a cache. At leat I checked the source of Qt4.8 and it reads data directly from socket, in case implementation with transactions it has a cache. – Marek R Aug 02 '17 at 21:05
  • I've checked Qt source I was wrong :(. `QDataStream` doesn't have any kind of buffer, this is handled by IODevice (socket). – Marek R Aug 02 '17 at 21:39
  • I was able to update to QT5.8 (there is no precompiled Qt5.9 for msvc2013 32bit). Adapted your code and got it running. It seems like I've never understood the >> operator. Please correct me if I'm wrong, but if I want 2 bytes to be discarded I need to do "stream >> someint(2 byte) >> somefloat(4 byte)"? How can I handle many values in stream? Do I need to loop over stream>> then? – Famok Aug 03 '17 at 07:48
  • `QStream` data reads data binary manner with defined endian handling no matter what platform you are running. Qt is handling platforms with big endian too. It also helps maintain floating point numbers which have couple standards and on different platforms the can have various format. Most network standards are using big endian and most processors now are using little endian so you have to handle that. – Marek R Aug 03 '17 at 08:22
  • I still need to figure out what happens after `startTransaction`. It seems like stream might contain multiple values for each startTransaction but I'm only reading one value per `stream >> toBeDiscarded >> value;` I've found out, that I get 256 values per readyRead. – Famok Aug 03 '17 at 09:04
  • transactions are a common patter in general (for example in database). In this case when commit of transaction fails state is restored to moment when transaction was started, so you can start reading again when more data are available new transaction can succeed. – Marek R Aug 03 '17 at 12:09
  • Can you comment on, what the stream contains after startTransaction (how much does it read, and how do i know)? And how do i know I've ready every value? – Famok Aug 04 '17 at 12:06