I stumbled upon an issue while programming some network application for myself using the QT-Framework. I really like the signals / slot system but I have the feeling I'm running into a race condition here.
My server is rapidly sending small "telegrams" which are stuck together because they are not worth a TCP-packet of their own (thats fine).
On the client side, I am using the sockets readyRead-slot and process the incomming data in my handler-slot "socketHasData".
The problem is, it seems that the signal is not re-emitted while the code inside the slot is executed.
I put a loop around it, which helps a little bit (I receive more telegrams). It seems like if the loop but not the slot is exited and then data is received, the signal gets skipped. After execution returns from the slot, my client is stuck and waiting forever while the data is piling up in the sockets buffer.
What can I do? Is it possible todo this event based in QT at all? Did I understand something wrong? How can I immplement telegram based transfer of data this way?
Here the problem seems solved: How to make sure that readyRead() signals from QTcpSocket can't be missed? but I don't make the mentioned mistakes:
- I'm not calling waitForReadyRead
- I'm not entering any event loop
- I don't use any thread besides the main thread
and still run into the issues.
Here is some example code with the behaviour in question:
Server
#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTcpServer *tcpServer = new QTcpServer(NULL);
tcpServer->listen(QHostAddress("127.0.0.1"), 5573);
tcpServer->waitForNewConnection(-1);
QTcpSocket *socket = tcpServer->nextPendingConnection();
QByteArray telegram;
for (int i = 0; i < 10000; ++i) {
telegram.clear();
QString message = "Test number " + QString::number(i);
// Each "telegram" is just that string and an incrementing number.
telegram.append(static_cast<char>(message.length()));
telegram.append(message.toLocal8Bit());
socket->write(telegram);
}
socket->flush();
socket->close();
tcpServer->close();
return a.exec();
}
Client
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow() {
delete ui;
}
void MainWindow::on_pushButton_clicked() {
this->socket = new QTcpSocket(this);
this->socket->connectToHost("127.0.0.1", 5573);
connect(this->socket, SIGNAL(readyRead()), this, SLOT(socketHasData()));
}
void MainWindow::socketHasData() {
do {
QByteArray header = this->socket->read(1);
int length = header.at(0);
while (this->socket->bytesAvailable() < length);
QByteArray payload = this->socket->read(length);
qDebug() << "[RX]" << QString::fromLocal8Bit(payload);
} while (this->socket->atEnd() == false); // I added the loop, which helps somewhat
}
Client-Header
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpSocket>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
void socketHasData();
private:
Ui::MainWindow *ui;
QTcpSocket *socket;
};
#endif // MAINWINDOW_H