2014-11-26 1 views
5

두 가지 Qt 애플리케이션이 있습니다. App1은 App2에서 QTcpServer을 통해 연결을 허용하고 QTcpSocket* tcpSocket의 인스턴스에 저장합니다. App1은 30Hz 시뮬레이션을 실행합니다. 각각의 시뮬레이션 실행을 위해, 몇 킬로바이트로 구성된 QByteArray은 (주/GUI 스레드에서) 다음 코드를 사용하여 전송됩니다QTcpSocket을 사용하여 소량 데이터 패키지를 자주 보내는 방법은 무엇입니까?

수신기 소켓 메인/GUI에합니다 (QTcpSocket :: readDataBlock 신호 수신
QByteArray block; 
    /* lines omitted which write data into block */ 
    tcpSocket->write(block, block.size()); 
    tcpSocket->waitForBytesWritten(1); 

thread)를 호출하고 해당 타임 스탬프를 GUI에 인쇄합니다.

App1과 App2가 모두 동일한 시스템에서 실행되면 패키지가 완벽하게 동기화됩니다. 그러나 App1과 App2가 네트워크를 통해 연결된 다른 시스템에서 실행될 때 App2는 App2의 시뮬레이션과 더 이상 동기화되지 않습니다. 패키지가 훨씬 느리게 들어옵니다. 더 놀라운 것은 (우리의 구현이 잘못되었음을 나타냄) 사실은 시뮬레이션 루프를 멈추었을 때 패키지가 더 이상 수신되지 않는다는 사실입니다. TCP 프로토콜에서 모든 패키지가 결국 도착할 것으로 기대하기 때문에 이것은 놀랄 일입니다.

Qt의 fortune example을 기반으로 TCP 로직을 구축했습니다. 그러나 포춘 서버는받는 클라이언트마다 하나의 패키지 만 전송하기 때문에 다릅니다. 누군가가 우리가 잘못한 것을 확인할 수 있습니까?

참고 : 우리는 MSVC2012 (App1), MSVC2010 (App2) 및 Qt 5.2를 사용합니다.

편집 : 패키지는 숫자가 많은 단일 시뮬레이션 실험의 결과를 의미하며 QByteArray block에 기록됩니다. 그러나 첫 번째 비트에는 QByteArray의 길이가 포함되어 있으므로 클라이언트는 모든 데이터가 수신되었는지 여부를 확인할 수 있습니다. 데이터 패키지에 의해

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 패키지/패킷이 없습니다 - 그냥 바이트 스트림이다. 어떻게 데이터를 잘라 냈습니까? 즉, 자신의 데이터 "패킷"중 하나가 시작되고 또 다른 끝이 언제 수신되는지를 애플리케이션이 어떻게 알 수 있습니까?또한 TCP에는 흐름 제어가 있으므로 데이터 읽기를 중지하면 TCP 스택의 버퍼가 가득 차고 더 이상 데이터가 전송되지 않습니다 (소켓이 비 블로킹 모드가 아닌 경우 송신하려고하면 송신자가 차단됩니다). 그러나 우리가 당신이 무엇을하는지, 어떻게 시뮬레이션이 작동하는지 또는 어떤 식으로 어떻게 동기화되지 않는지를 알지 못할 때 당신이 잘못한 것을 추측하는 것은 매우 어렵습니다. – nos

+0

원래 질문을 편집하여 질문에 대답하려고했습니다. 좀 봐주세요. –

+0

당신이 원하는 것보다 더 많은 데이터를 읽는 것 같습니다. 당신의 블록 사이즈는 1024이지만'tcpSocket-> bytesAvailable()'은 1222 바이트를 사용할 수 있습니다. 다음 블록의 시작 부분도 보입니다. 그래서'in.readBytes() '를 사용해야 할 것입니다. '대신 읽는 양을 지정할 수 있습니다. (그리고 실제로 많은 양의 데이터를 읽었 음을 나중에 확인하십시오.) 그러나 그것은 또한 프로토콜이 어떤지에 달려 있습니다. 예를 들어 그것은 단순한 요청/응답입니다. 송신자가 이전 패킷에 대한 응답을받을 때까지 데이터를 보내지 않습니다. 현재 코드는 OK입니다. – nos

답변

5

우리의 구현에 문제가 발생했다가 쌓여되고 만 부분적으로 도착한 패키지를 잘못 처리 :이 신호 QTcpSocket :: readDataBlock가 방출 될 때 호출되는 코드입니다.

대답은 Tcp packets using QTcpSocket입니다. 그러나 우리는 QByteArray 대신에 QDataStream에 의존하기 때문에이 대답은 간단한 방식으로 적용될 수 없습니다.

다음 코드 (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의 처음 세 바이트가 우리 자신의 네트워크 프로토콜의 일부임을 : blockSizedataType 바이너리 덩어리를 역 직렬화하는 데 도움이 완전한 하나의 패키지에 대한 바이트 수를 나타냅니다. 의 TCP 연결을 통해 객체를 전송 패킷에 주름을 해제의 대기 시간을 줄이기 위해 편집

은 매우 유용했다 :

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