2015-04-04 3 views
2

Я работаю с 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 продолжает ждать, пока он не истечет.

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

+0

Я не уверен, что вам действительно нужен блокирующий сокет. Что мешает вам использовать неблокирующие операции? (Проблема с блокировкой ввода-вывода заключается в том, что он не позволяет графическому интерфейсу выполнять такие действия, как обработка событий мыши, и, таким образом, приводит к плохому пользовательскому опыту, если только вы не выполняете все свои операции ввода-вывода в отдельных потоках, t множество причин использовать QTCPSocket вместо простого старого API-интерфейсов BSD) –

+0

Socket работает в собственном потоке, поэтому взаимодействие с GUI не является проблемой.На самом деле код также должен работать в не-GUI-приложении (GUI является необязательным). Это (блокирование) необходимо, потому что используемый им код предполагает, что эти вызовы блокируются, и это не предмет изменения. – Googie

+0

В этом случае, возможно, просто вызовите socketDescriptor() в QTCPSocket, чтобы получить базовый дескриптор сокета/файла, и используйте API-интерфейсы BSD Socket (т. Е. Send() и recv()), а не вызовы QTCPSocket. Таким образом, вам не придется иметь дело с не-блокирующим-и-ориентированным дизайном QTCPSocket. –

ответ

4

Я хотел бы предложить использовать асинхронный подход, но если вы действительно хотите идти с синхронным способом, то лучшим способом будет использовать локальный цикл обработки событий:

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 байт не чтения или таймаута.

+0

Можно подключить сигнал таймера на том же eventloop, что и сигнал readyRead (в данном случае в цикле QEventLoop)? –

+0

@MilanKocic Я не уверен, что вы имеете в виду, но вы можете подключить сигнал «timeout» к любому другому сигналу или слоту без аргументов. – Nejat

+0

Я спросил, можем ли мы это сделать: loop.connect (socket, SIGNAL (readyRead()), SLOT (quit())); loop.connect (& timer, SIGNAL (timeout()), SLOT (quit())); –

Смежные вопросы