2014-11-26 8 views
5

Wir haben zwei Qt-Anwendungen. App1 akzeptiert eine Verbindung von App2 über QTcpServer und speichert sie in einer Instanz von QTcpSocket* tcpSocket. App1 führt eine Simulation mit 30 Hz durch.Wie verwende ich QTcpSocket für den häufigen Versand kleiner Datenpakete?

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

Der Empfänger Buchse hört die QTcpSocket :: readDataBlock Signal (in main/GUI: mit dem folgenden Code (von dem Haupt/GUI Gewinde) für jeden Simulationslauf wird ein QByteArray bestehend aus ein paar Kilobyte geschickt thread) und gibt den entsprechenden Zeitstempel an die GUI aus.

Wenn App1 und App2 auf demselben System ausgeführt werden, sind die Pakete perfekt synchronisiert. Wenn App1 und App2 jedoch auf verschiedenen Systemen ausgeführt werden, die über ein Netzwerk verbunden sind, ist App2 nicht mehr synchron mit der Simulation in App2. Die Pakete kommen viel langsamer. Noch überraschender (und anzeigend, dass unsere Implementierung falsch ist) ist die Tatsache, dass, wenn wir die Simulationsschleife stoppen, keine Pakete mehr empfangen werden. Das überrascht uns, weil wir vom TCP-Protokoll erwarten, dass alle Pakete irgendwann ankommen.

Wir haben die TCP-Logik basierend auf Qt fortune example gebaut. Der Vermögenserver ist jedoch anders, weil er nur ein Paket pro ankommendem Client sendet. Könnte jemand herausfinden, was wir falsch gemacht haben?

Hinweis: Wir verwenden MSVC2012 (App1), MSVC2010 (App2) und Qt 5.2.

Edit: Mit einem Paket, das ich das Ergebnis eines einzigen Simulationsexperiment bedeuten, die eine Reihe von Zahlen ist, in QByteArray block geschrieben. Die ersten Bits enthalten jedoch die Länge von QByteArray, so dass der Client prüfen kann, ob alle Daten empfangen wurden. Dies ist der Code, der aufgerufen wird, wenn das Signal QTcpSocket :: readDataBlock emittiert wird:

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 keine Pakete/Pakete hat - es ist nur ein Byte-Stream. Wie haben Sie die Daten aufgeschnitten? Wie weiß Ihre empfangende Anwendung, wenn 1 Ihrer eigenen Datenpakete beginnt und ein anderes Ende?Außerdem hat TCP eine Flusskontrolle. Wenn Sie also aufhören, Daten zu lesen, füllen sich die Puffer im TCP-Stack und es werden keine Daten mehr gesendet (und der Sender blockiert, wenn Sie versuchen, zu senden, solange Ihre Sockets nicht blockiert sind). Es ist jedoch sehr schwer zu erraten, was Sie falsch machen, wenn wir nicht wissen, was Sie tun oder wie Ihre Simulation funktioniert oder wie sie irgendwie "nicht synchron" wird. – nos

+0

Ich habe versucht, Ihre Frage zu beantworten, indem ich meine ursprüngliche Frage bearbeitete. Bitte sehen Sie es sich an. –

+0

Es scheint, dass Sie riskieren, mehr Daten zu lesen, als Sie möchten, wenn z. Deine Blockgröße ist 1024, aber 'tcpSocket-> bytesAvailable()' hat 1222 Bytes zur Verfügung, du liest auch den Anfang des nächsten Blocks, so wie es aussieht - also musst du 'in.readBytes() benutzen 'stattdessen, wo Sie angeben können, wie viel gelesen werden soll. (Und überprüfen Sie danach, dass es tatsächlich so viele Daten liest). Das hängt aber auch davon ab, wie Ihr Protokoll ist. Wenn z.B. Es ist eine einfache Anfrage/Antwort, bei der Ihr Sender * niemals * Daten sendet, bis er eine Antwort für das vorherige Paket erhalten hat. Der aktuelle Code scheint in Ordnung – nos

Antwort

5

Das Problem in unserer Implementierung von Datenpaketen werden aufgestapelt und falsche Handhabung der Verpackungen verursacht wurde, die teilweise nur gekommen war.

Die Antwort geht in Richtung Tcp packets using QTcpSocket. Diese Antwort konnte jedoch nicht auf einfache Weise angewendet werden, da wir uns auf QDataStream statt auf QByteArray beziehen.

Der folgende Code (wird jedes Mal ausgeführt, wenn QTcpSocket::readDataBlock ausgegeben wird) funktioniert für uns und zeigt, wie eine rohe Reihe von Bytes von gelesen werden kann. Leider scheint es nicht möglich, die Daten übersichtlicher zu verarbeiten (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    
     } 

    } 

Bitte nicht, dass die ersten drei Bytes der erwarteten QDataStream Teil unserer eigenen procotol sind: blockSize gibt die Anzahl von Bytes für ein komplettes einziges Paket, hilft dataType die binären Brocken Deserialisieren.

bearbeiten Für die Latenz reduzieren Objekte durch die TCP-Verbindung sendet, Paketbündelungs Deaktivierung war sehr nützlich:

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