Best way to handle streaming TCP Data
-
Hi,
I am pretty new to QT and C++, but I have good experience in C and Verilog and I have designed a fairly large GUI frontend to receive streamed data over TCP from a C server running remotely. Whilst I can plot it quite nicely thanks to QCustomPlot and the guidance from some other users. I am looking to improve the performance of my streaming system, but I am not sure what structures or paradigms to adopt as there are a lot of possibilities with QT. Ultimately I will need to receive and plot four separate graphs (in realtime) from data provided in a single packet (4x2048 samples = 32768 bytes), but either way I am looking for a refresh rate on all plots of about 5 Hz. The code below is what I have, but I can see that it is running sub-optimally hogging an entire CPU core at 100% and stuttering badly and dropping packets when the computer is under load. The function is attached to a readyRead slot as indicated below:
connect(&tcpClient, &QIODevice::readyRead, this, &MainWindow::update);
Could anyone suggest or point me in the direction of a better approach to handle streamed data over TCP in QT ?
double x,y; while(tcpClient.bytesAvailable()){ data_in = tcpClient.read(PayloadSize); int numSamples = data_in.size() / 4; const float* ptrFloat = reinterpret_cast<const float*>(data_in.constData()); for (int i=0; i<numSamples; ++i){ colorMapRTI_1->data()->cellToCoord(i, 0, &x, &y); colorMapRTI_1->data()->setCell(i, 0, *ptrFloat); ptrFloat++; } ui->rti_1->replot(); colorMapRTI_1->rescaleDataRange(); } // end of while ```
Best Regards, Ren
-
Have a look at
ChatClient::onReadyRead
of this example. There is also a paragraph that explains what the code does. In your case you will readfloat
s instead ofQByteArray
-
Hi,
@VRonin I tried your code but it had issues identifying whether it was a Tcp streaming connection as it is not set up in the same way on my server end as it is yours. Thank you for sharing your code and article it is nicely documented. Either way I solved my issue through a simple if statement to just wait until it has received a payload of the correct size, though I still need to resolve some minor packet dropping issues (which I suspect is due to received PayloadSize varying slightly) anyway here is the code:
void MainWindow::onReadyRead(){ while (tcpClient->bytesAvailable()){ data_in = tcpClient->read(PayloadSize); if (data_in.size() == PayloadSize){ di = 0; // set the data index to 0 const float* ptrFloat = reinterpret_cast<const float*>(data_in.constData()); updategraph1(ptrFloat); updategraph2(ptrFloat); } // end of if found the payload } // end of while bytes are available } // end of ready read
Best Regards, Ren
-
Hi,
@VRonin I tried your code but it had issues identifying whether it was a Tcp streaming connection as it is not set up in the same way on my server end as it is yours. Thank you for sharing your code and article it is nicely documented. Either way I solved my issue through a simple if statement to just wait until it has received a payload of the correct size, though I still need to resolve some minor packet dropping issues (which I suspect is due to received PayloadSize varying slightly) anyway here is the code:
void MainWindow::onReadyRead(){ while (tcpClient->bytesAvailable()){ data_in = tcpClient->read(PayloadSize); if (data_in.size() == PayloadSize){ di = 0; // set the data index to 0 const float* ptrFloat = reinterpret_cast<const float*>(data_in.constData()); updategraph1(ptrFloat); updategraph2(ptrFloat); } // end of if found the payload } // end of while bytes are available } // end of ready read
Best Regards, Ren
@Renegade243 said in Best way to handle streaming TCP Data:
data_in = tcpClient->read(PayloadSize);
The "dropping" issue is here. It should be:
void MainWindow::onReadyRead(){ while (tcpClient->bytesAvailable()>=PayloadSize){ data_in = tcpClient->read(PayloadSize); di = 0; // set the data index to 0 const float* ptrFloat = reinterpret_cast<const float*>(data_in.constData()); updategraph1(ptrFloat); updategraph2(ptrFloat); } // end of while bytes are available } // end of ready read
it had issues identifying whether it was a Tcp streaming connection as it is not set up in the same way on my server end as it is yours.
since your data is
float
s it doesn't matter,QDataStream
will just usereinterpret_cast
+read()
internally making it the same as your read method -
Thanks, sorry I did not see this post immediately. Interestingly I had to re-address the problem when I moved my network setup to another building, the C server is now physically separated from the client by a 70m network cable. This consequently caused my previous if statement to fail as it never received the full packet size (which it did before when they were right next to each other), instead it is received in partial chunks which need to be reconstructed. Here is the code I used to do so and it works quite well, I am experiencing No performance issues and No network problems at the moment, though I haven't tested the absolute limit of this processing topology. I am confident that it won't struggle under higher refresh rates though.
If you see a way it can be significantly improved feel free to comment, I believe it will serve as a nice piece of code for users exploring reliable packet reception and processing, I can then mark this issue as solved.
void MainWindow::onReadyRead(){ while (tcpClient->bytesAvailable()){ data_temp = tcpClient->read(PayloadSize); // tries to read full payload data_temp_size = data_temp.size(); //cout << "Data Temp Size: " << data_temp_size << endl; //cout << "Data In Size: " << data_in_size << endl; if (data_in_size+data_temp_size < PayloadSize){ data_in.append(data_temp); // keep appending data temp to data in data_in_size += data_temp_size; } else{ // more than payload size data_in.append(data_temp.left(PayloadSize - data_in_size)); di = 0; // set the data index to 0 const float* ptrFloat = reinterpret_cast<const float*>(data_in.constData()); updatePlots(ptrFloat); data_in.clear(); data_in.append(data_temp.right(data_temp_size - (PayloadSize - data_in_size))); data_in_size = data_in.size(); } // end of if found the payload } // end of while bytes are available } // end of ready read