Как сделать блокировку tcp сокета с помощью Qt?
Я работаю с QTcpSocket. Мне нужно, чтобы все вызовы записи/чтения в сокет были синхронными (блокирующими).
Я знаю, что есть waitForReadyRead() и waitForBytesWritten(), но эти два метода отмечены в документации Qt, поскольку они могут случайно отказать под Windows. Я не могу себе этого позволить.
Блокирующее чтение является самым важным (так как чтение всегда происходит после записи команды другому узлу, поэтому я знаю, что если данные достигнут другого узла, он ответит).
Я попробовал 2 подходы.
Первый:
QByteArray readBytes(qint64 count)
{
int sleepIterations = 0;
QByteArray resultBytes;
while (resultBytes.size() < count && sleepIterations < 100)
{
if (socket->bytesAvailable() == 0)
{
sleepIterations++;
QThread::msleep(100);
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
continue;
}
resultBytes += socket->read(qMin(count, socket->bytesAvailable()));
}
return resultBytes;
}
Это должно ждать, пока байты будут доступны для чтения на сокете, обрабатывая цикл событий в то же время, так что сокет делает это необходимое внутреннее дело.
К сожалению - по неизвестной мне причине - bytesAvailable() иногда возвращает правильное число байт, но иногда он никогда не возвращает ничего больше 0.
На самом деле я знаю, что там были данные для чтения, потому что раньше он работал со вторым подход (но у него есть свои проблемы).
Второе:
У меня есть своего рода сигнальный "блокиратор", который блокирует текущий контекст и обрабатывает цикл событий, пока не будет испущен определенный сигнал. Это и есть "блокиратор":
Сигнальщик.h:
class SignalWait : public QObject
{
Q_OBJECT
public:
SignalWait(QObject *object, const char *signal);
bool wait(int msTimeout);
private:
bool called = false;
private slots:
void handleSignal();
};
Сигнальщик.cpp:
SignalWait::SignalWait(QObject* object, const char* signal) :
QObject()
{
connect(object, signal, this, SLOT(handleSignal()));
}
bool SignalWait::wait(int msTimeout)
{
QTime timer(0, 0, 0, msTimeout);
timer.start();
while (!called && timer.elapsed() < msTimeout)
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return called;
}
void SignalWait::handleSignal()
{
called = true;
}
А потом я использовал его так:
SignalWait signalWait(socket, SIGNAL(readyRead()));
// ...
// socket->write(...);
// ...
if (!signalWait.wait(30000))
{
// error
return;
}
bytes = socket->read(size);
Этот подход, по-видимому, работает лучше, но время от времени он также терпит неудачу. Я не знаю почему. Похоже, что сигнал
readyRead() был никогда не испускается, и SignalWait продолжает ждать, пока не истечет время ожидания.У меня кончились идеи. Как правильно с этим бороться?
1 ответ:
Я бы предложил использовать асинхронный подход, но если вы действительно хотите пойти синхронным путем, то лучше использовать локальный цикл событий:
QTimer timer; timer.setSingleShot(true); QEventLoop loop; loop.connect(socket, SIGNAL(readyRead()), SLOT(quit())); connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); while (resultBytes.size() < count) { timer.start(msTimeout); loop.exec(); if(timer.isActive()) resultBytes += socket->read(qMin(count, socket->bytesAvailable())); else break; }Здесь он ждет, пока не будут прочитаны байты
countили достигнут таймаут.
Comments