2014-11-26 1 views
5

У нас есть два приложения Qt. App1 принимает соединение из App2 через QTcpServer и сохраняет его в экземпляре QTcpSocket* tcpSocket. App1 запускает симуляцию с частотой 30 Гц. Для каждого прогона моделирования, A QByteArray, состоящие из несколько килобайта отправляются, используя следующий код (из главного/GUI потока):Как использовать QTcpSocket для частой отправки небольших пакетов данных?

QByteArray block; 
    /* lines omitted which write data into block */ 
    tcpSocket->write(block, block.size()); 
    tcpSocket->waitForBytesWritten(1); 

Гнездо приемника слушает сигнал QTcpSocket :: readDataBlock (в главном/GUI thread) и печатает соответствующую метку времени в графическом интерфейсе.

Когда оба приложения App1 и App2 работают в одной системе, пакеты прекрасно синхронизируются. Однако, когда App1 и App2 запускаются на разных системах, подключенных через сеть, App2 больше не синхронизируется с симуляцией в App2. Пакеты идут намного медленнее. Еще более удивительно (и указывает на то, что наша реализация неверна) заключается в том, что когда мы останавливаем цикл моделирования, больше пакетов не принимается. Это нас удивляет, потому что мы ожидаем от TCP-протокола, что все пакеты придут в конечном итоге.

Мы построили логику TCP на основе Qt's fortune example. Однако удачный сервер отличается, потому что он отправляет только один пакет на входящего клиента. Может ли кто-то определить, что мы сделали неправильно?

Примечание: мы используем MSVC2012 (App1), MSVC2010 (App2) и Qt 5.2.

Редактировать: С пакетом я подразумеваю результат одного симуляционного эксперимента, который представляет собой кучу чисел, записанных в QByteArray block. Однако первые биты содержат длину QByteArray, так что клиент может проверить, были ли получены все данные. Это код, который вызывается, когда сигнал QTcpSocket :: readDataBlock испускается:

QDataStream in(tcpSocket); 
    in.setVersion(QDataStream::Qt_5_2); 

    if (blockSize == 0) { 
     if (tcpSocket->bytesAvailable() < (int)sizeof(quint16)) 
     return; // cannot yet read size from data block 

     in >> blockSize; // read data size for data block 
    } 

    // if the whole data block is not yet received, ignore it 
    if (tcpSocket->bytesAvailable() < blockSize) 
     return; 

    // if we get here, the whole object is available to parse 
    QByteArray object; 
    in >> object; 

    blockSize = 0; // reset blockSize for handling the next package 

    return; 
+2

TCP не имеет пакеты/пакетов - это просто поток байт. Как вы отрезали данные? например, как ваше приложение-получатель узнает, когда начинается 1 из ваших собственных «пакетов» данных и другой конец?Кроме того, TCP имеет управление потоком, поэтому, если вы прекратите чтение данных, буферы в стеке TCP заполняются, и больше данных не будет отправлено (и отправитель будет блокироваться при попытке отправить, если ваши сокеты не находятся в режиме блокировки). Однако очень сложно угадать, что вы делаете неправильно, когда мы не знаем, что вы делаете, или как вы имитируете работу, или как она каким-то образом «не синхронизируется». – nos

+0

Я попытался ответить на ваш вопрос, отредактировав исходный вопрос. Пожалуйста, взгляните на это. –

+0

кажется, что вы рискуете читать больше данных, чем хотите, если, например, ваш размер блока равен 1024, но 'tcpSocket-> bytesAvailable()' имеет 1222 байт, вы также читаете начало следующего блока, по внешнему виду - так что вам, вероятно, придется использовать 'in.readBytes() 'вместо этого, где вы можете указать, сколько нужно читать. (И подтвердите впоследствии, что он действительно прочитал много данных). Однако это также зависит от того, как работает ваш протокол. Если, например, это простой запрос/ответ, когда ваш отправитель * никогда не отправляет какие-либо данные до тех пор, пока не получит ответ для предыдущего пакета, текущий код выглядит нормально – nos

ответ

5

Проблема в нашей реализации была вызвана пакетами данных нагромождался и неправильное обращение с пакетами, которые были только частично прибывшими.

Ответ идет в направлении Tcp packets using QTcpSocket. Однако этот ответ не может быть применен прямолинейно, потому что мы полагаемся на QDataStream вместо простого QByteArray.

Следующий код (запускается каждый раз, когда выдается QTcpSocket::readDataBlock), работает для нас и показывает, как необработанную строку байтов можно читать с QDataStream. К сожалению, кажется, что невозможно обработать данные более четко (используя operator>>).

QDataStream in(tcpSocket); 
    in.setVersion(QDataStream::Qt_5_2); 

    while (tcpSocket->bytesAvailable()) 
    { 
     if (tcpSocket->bytesAvailable() < (int)(sizeof(quint16) + sizeof(quint8)+ sizeof(quint32))) 
      return; // cannot yet read size and type info from data block 

     in >> blockSize; 
     in >> dataType; 

     char* temp = new char[4]; // read and ignore quint32 value for serialization of QByteArray in QDataStream  
     int bufferSize = in.readRawData(temp, 4); 
     delete temp; 
     temp = NULL; 

     QByteArray buffer; 

     int objectSize = blockSize - (sizeof(quint16) + sizeof(quint8)+ sizeof(quint32)); 

     temp = new char[objectSize];    
     bufferSize = in.readRawData(temp, objectSize); 
     buffer.append(temp, bufferSize); 
     delete temp; 
     temp = NULL; 

     if (buffer.size() == objectSize) 
     { 
      //ready for parsing    
     } 
     else if (buffer.size() > objectSize) 
     { 
      //buffer size larger than expected object size, but still ready for parsing 
     } 
     else 
     { 
      // buffer size smaller than expected object size 
      while (buffer.size() < objectSize) 
      {    
       tcpSocket->waitForReadyRead(); 
       char* temp = new char[objectSize - buffer.size()];   
       int bufferSize = in.readRawData(temp, objectSize - buffer.size()); 
       buffer.append(temp, bufferSize); 
       delete temp; 
       temp = NULL; 
      } 
      // now ready for parsing 
     } 
     if (dataType == 0) 
     {    
      // deserialize object    
     } 

    } 

Пожалуйста, не то, что первые три байта ожидаемой QDataStream являются частью нашей собственной procotol: blockSize указывает количество байт для полного единого пакета, dataType помогает десериализации бинарный кусок.

Редактировать Для уменьшения задержки отправки объектов через соединение TCP, отключение пакетов группировки был очень полезным:

// disable Nagle's algorithm to avoid delay and bunching of small packages 
    tcpSocketPosData->setSocketOption(QAbstractSocket::LowDelayOption,1);