Я работаю с QTcpSocket
. Мне нужно, чтобы любые вызовы записи/чтения в сокет были синхронными (блокирование).Как сделать блокировку tcp-сокета с Qt?
Я знаю, что есть 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.
Я знаю, что на самом деле, что там были данные не возвращается для чтения, так как он используется для работы со вторым подходом (но у него есть свои проблемы).
Второе:
У меня есть вид сигнала «блокаторов», который блокирует текущий контекст и обрабатывает цикл событий, пока определенный сигнал не излучается. Это "блокиратор":
SignalWait.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();
};
SignalWait.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
продолжает ждать, пока он не истечет.
У меня нет идей. Каков правильный способ справиться с этим?
Я не уверен, что вам действительно нужен блокирующий сокет. Что мешает вам использовать неблокирующие операции? (Проблема с блокировкой ввода-вывода заключается в том, что он не позволяет графическому интерфейсу выполнять такие действия, как обработка событий мыши, и, таким образом, приводит к плохому пользовательскому опыту, если только вы не выполняете все свои операции ввода-вывода в отдельных потоках, t множество причин использовать QTCPSocket вместо простого старого API-интерфейсов BSD) –
Socket работает в собственном потоке, поэтому взаимодействие с GUI не является проблемой.На самом деле код также должен работать в не-GUI-приложении (GUI является необязательным). Это (блокирование) необходимо, потому что используемый им код предполагает, что эти вызовы блокируются, и это не предмет изменения. – Googie
В этом случае, возможно, просто вызовите socketDescriptor() в QTCPSocket, чтобы получить базовый дескриптор сокета/файла, и используйте API-интерфейсы BSD Socket (т. Е. Send() и recv()), а не вызовы QTCPSocket. Таким образом, вам не придется иметь дело с не-блокирующим-и-ориентированным дизайном QTCPSocket. –