При использовании QTcpSocket
для приема данных используется сигнал readyRead()
, который сигнализирует, что новые данные доступны.
Однако, когда вы находитесь в соответствующей реализации слота для чтения данных, никаких дополнительных readyRead()
не будет выбрано.
Это может иметь смысл, поскольку вы уже находитесь в функции, где вы читаете все доступные данные.
Описание проблемы
Однако предположим следующую реализацию этого слота:
void readSocketData()
{
datacounter += socket->readAll().length();
qDebug() << datacounter;
}
Что делать, если некоторые данные поступают после вызова readAll()
, но перед выходом из слота?
Что, если это последний пакет данных, отправленный другим приложением (или, по крайней мере, последний в течение некоторого времени)?
Никакого дополнительного сигнала не будет выбрано, поэтому вы должны обязательно прочитать все данные самостоятельно.
Один из способов минимизации проблемы (но не избежать ее полностью)
Конечно, мы можем изменить слот следующим образом:
void readSocketData()
{
while(socket->bytesAvailable())
datacounter += socket->readAll().length();
qDebug() << datacounter;
}
Однако мы не решили проблему. По-прежнему возможно, что данные поступают сразу после socket->bytesAvailable()
-check (и даже размещение/другая проверка на абсолютном конце функции не решает этого).
Убедитесь, что у вас есть возможность воспроизвести проблему
Поскольку эта проблема, конечно, происходит очень редко, я придерживаюсь первой реализации слота, и я даже добавлю искусственный тайм-аут, чтобы убедиться, что проблема возникает:
void readSocketData()
{
datacounter += socket->readAll().length();
qDebug() << datacounter;
// wait, to make sure that some data arrived
QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
loop.exec();
}
Затем я разрешаю другому приложению отправлять 100 000 байт данных. Вот что происходит:
новое соединение!
32768 (или 16K или 48K)
Первая часть сообщения считывается, но конец больше не читается, поскольку readyRead()
не будет вызываться снова.
Мой вопрос: какой лучший способ быть уверенным, эта проблема никогда не возникает?
Возможное решение
Одно из решений, с которыми я столкнулся, снова вызывает один и тот же слот в конце и проверяет в начале слота, если есть больше данных для чтения:
void readSocketData(bool selfCall) // default parameter selfCall=false in .h
{
if (selfCall && !socket->bytesAvailable())
return;
datacounter += socket->readAll().length();
qDebug() << datacounter;
QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
loop.exec();
QTimer::singleShot(0, this, SLOT(readSocketDataSelfCall()));
}
void readSocketDataSelfCall()
{
readSocketData(true);
}
Поскольку я не вызываю этот слот напрямую, но использую QTimer::singleShot()
, я предполагаю, что QTcpSocket
не может знать, что я снова вызываю слот, поэтому проблема, из-за которой readyRead()
не испускается больше не может произойти.
Причиной, по которой я включил параметр bool selfCall
, является то, что слот, который вызывается QTcpSocket
, не разрешается раньше, иначе может возникнуть одна и та же проблема, что данные поступают точно в неподходящий момент и readyRead()
не испускается.
Это действительно лучшее решение для решения моей проблемы? Является ли существование этой проблемы ошибкой дизайна в Qt или я что-то не хватает?