Как сделать блокировку 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 продолжает ждать, пока не истечет время ожидания.

У меня кончились идеи. Как правильно с этим бороться?

620   1  

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

    Ничего не найдено.