merkwürdiges Network-Problem mit QTcpServer und QTcpSocket

Alles rund um die Programmierung mit Qt
Antworten
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

merkwürdiges Network-Problem mit QTcpServer und QTcpSocket

Beitrag von Willi2793 »

Hallo,

ich habe ein Problem mit QTcpServer und QTcpSocket das ich nicht verstehe. Ich weiß auch nicht ob es ein QT-Problem ist aber ich würde es trotzdem gerne hier mal zur Diskussion stellen.

Wie also schon mehrfach erwähnt habe ich ein Server-Programm und einen Client dazu der den Server befeuert.

Der Ablauf eines Client-Durchlaufs ist folgendermassen:

- Verbindungsaufbau
- 1. Nachricht senden
- 1. Antwort lesen
- 2. Nachricht senden
- 2. Antwort lesen
- 3. Nachricht senden
- 3. Antwort lesen
- Verbindung abbauen

Das funktioniert auch. Aufgrund der Signal/Slot-Verknüpfungen weiß ich das alle Verbindungen abgebaut werden und alle angelegten Objekte wieder gelöscht werden.

Ich teste die beiden Programme nun auf folgenden Rechnern:

- Rechner A (Win XP SP3)
- Rechner B (Win XP SP3)
- Rechner C (Win 7 SP1)
- Rechner D (Win 7 SP1)
(die Unix-Umgebung kommt später dazu)

Jetzt habe ich folgende Kombinationen ausprobiert:

Code: Alles auswählen

Client            Server

Rechner C     Rechner C
Rechner D     Rechner D
Rechner A     Rechner A
Rechner B     Rechner A
Rechner A     Rechner B
Alles funktioniert reibungslos bis auf die Kombinationen bei denen der Client auf Rechner A läuft. Dann stockt die Verarbeitung nach einer nicht reproduzierbaren Anzahl an Durchläufen, aber einigermassen schnell, an dem Punkt "1. Antwort lesen". Er scheint also das "readyRead()" Signal nicht zu bekommen.

Hat irgendjemand eine Idee oder einen Ansatzpunkt bzw. Erklärung dafür?

Viele Grüße,
Willi
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Die Kristallkugel ist leider im Frühlingsservice..

Code zur Sende/Empfangsroutine?
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Ich dachte erstmal allgemein zu diekutiren. Es ist ja auch nur auf dem einen Rechner reproduzierbar. Aber okay, hier der Server-Code zum schicken und empfangen:

Code: Alles auswählen

void LDCSProtocol::startWork() {
    connect(socket, SIGNAL(readyRead())        , this, SLOT(readyRead()));
    connect(socket, SIGNAL(disconnected())     , this, SLOT(disconnected()));
    connect(socket, SIGNAL(destroyed(QObject*)), this, SIGNAL(ended()));
    readData.clear();  // Als Klassen-Member definiertes QByteArray
}

void LDCSProtocol::readyRead() {
    readData.append(socket->readAll());
    log->log(QThread::currentThread()->objectName()+": LDCS-Data read. Size: "+QString().setNum(readData.size()),6);
    log->log(QThread::currentThread()->objectName()+":   Content: "+QString().append(readData.toHex()),6);
    if (readData.size() >= 64) {
        ldcsIn.setComplete(readData);
        if (readData.size() == ldcsIn.getHeaderError()+ldcsIn.getHeaderDataSize()+64) {
            readData.clear();
            process();
        }
    }
}


void LDCSProtocol::disconnected() {
    socket->flush();
    socket->close();
    socket->deleteLater();
}


void LDCSProtocol::process() {
    QString job(VCAClass::getVCATag(ldcsIn.getData(),"JOB"));
    QByteArray ldcsOut;
    switch(ldcsIn.getHeaderCommand()) {
    case 1001:
        ldcsOut = QByteArray().append("Antwort");
        break;
    case 1008:
        ldcsOut = QByteArray().append("Antwort");
        break;
    case 1009:
        ldcsOut = QByteArray().append("Antwort");
        break;
    }
    socket->write(ldcsOut);
    socket->flush();
}
Das logging im readyRead-Slot wird dann im Fall das er hängen bleibt nicht mehr ausgeführt. Es liegt also nicht an fehlerhaften Daten.

Und hier der Client-Teil:

Code: Alles auswählen

void ConnectionTest::startWork() {
    socket = new QTcpSocket(this);
    log->log(name+": Start Work", 6);
    connect(socket, SIGNAL(connected()), this, SLOT(connected()));

    connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
    connect(socket, SIGNAL(destroyed())   , this  , SIGNAL(ended()));

    socket->connectToHost("localhost"), 36001, QIODevice::ReadWrite);
}

void ConnectionTest::connected() {
    QString locAdr(socket->localAddress().toString()+":"+QString().setNum(socket->localPort()));
    QString remAdr(socket->peerAddress().toString()+":"+QString().setNum(socket->peerPort()));
    log->log(name+": Connected: local: "+locAdr+"   Remote: "+remAdr, 6);
    QByteArray cmd(QByteArray::fromHex("00000000e9030000010000000000000000000000000000000100000001000000010000000c0000000000000000000000000000000000000000000000000000004a4f423d3534343737340d0a"));
    disconnect(socket, SIGNAL(connected()), this, SLOT(connected()));
    connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
    socket->write(cmd);
    socket->flush();
}


void ConnectionTest::readyRead() {
    readData.append(socket->readAll());
    log->log(name+": Read Data. Size: "+QString().setNum(readData.size()), 6);
    if (readData.size() >= 64) {
        ldcsIn.setComplete(readData);
        if (readData.size() == ldcsIn.getHeaderError()+ldcsIn.getHeaderDataSize()+64) {
            readData.clear();
            process();
        }
    }
}

void ConnectionTest::process() {
    LDCS ldcsOut;
    switch(ldcsIn.getHeaderCommand()) {
    case 1001:
        ldcsOut.setComplete(QByteArray::fromHex("00000000f003000001000000000000000000000000000000010000000100000001000000170000000000000000000000000000000000000000000000000000004a4f423d303030303030353434373734302e5344460d0a"));
        socket->write(ldcsOut.getComplete());
        socket->flush();
        log->log(name+": LDCS-Answer. Size: "+QString().setNum(ldcsOut.getComplete().size()),6);
        break;
    case 1008:
        ldcsOut.setComplete(QByteArray::fromHex("00000000f103000001000000000000000000000000000000010000000100000001000000170000000000000000000000000000000000000000000000000000004a4f423d303030303030353434373734312e5344460d0a"));
        socket->write(ldcsOut.getComplete());
        socket->flush();
        log->log(name+": LDCS-Answer. Size: "+QString().setNum(ldcsOut.getComplete().size()),6);
        break;
    case 1009:
        socket->flush();
        socket->close();
        socket->disconnectFromHost();
        break;
    }
}
Und viel Glück das Deine Kugel gute gewartet wird ;)
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Willi2793 hat geschrieben:Ich dachte erstmal allgemein zu diekutiren. Es ist ja auch nur auf dem einen Rechner reproduzierbar. [...]
Naja.. erfahrungsgemäss liegt der Fehler ganz einfach eher am Applikationscode als an Qt :wink:

Der Code sieht nicht all zu schlecht aus... ich gehe davon aus, dass dies wieder multithreading ist (mit einem nicht abgeleiteten QThread)..

Auf den ersten Blick frage ich mich daher nur, ob hier

Code: Alles auswählen

void LDCSProtocol::startWork() {
    connect(socket, SIGNAL(readyRead())        , this, SLOT(readyRead())); 
der vom QTcpServer erzeugte Socket korrekt in den Thread geschoben wurde.. so in etwa

Code: Alles auswählen

  QTcpSocket *sock = server->nextPendingConnection();
  ...
  sock->setParent(NULL);
  sock->moveToThread(thread);
Nicht dass auf dem armen Socket noch zwei Threads gleichzeitig rumtanzen..
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Das ist nahezu eine exakte Kopie des Parts aus meinem Programm :)
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

wunderbar :wink:

Hast du mal mit Wireshark die ankommenden Daten (respektive dein Netzwerkprotokoll überhaupt) verifiziert?
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Nein, diese Mühe habe ich mir jetzt noch nicht gemacht weil es manchmal 1000 Durchläufe braucht, manchmal auch nur 30. Aber das wäre mal eine Variante. Wonach soll ich denn dann im Protokoll Ausschau halten?
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Sobald die Kommunikation stockt würde ich den Wireshark-Scan anhalten und schauen, ob der letzte Nachricht/Antwort-Durchgang korrekt (nach deinen Vorstellungen) abgelaufen ist.

Minimal würde ich die Grösse der versendeten/empfangenen Pakete vergleichen (Applikations-Log zu Wireshark-Angabe).
odt
Beiträge: 128
Registriert: 12. August 2010 11:49
Kontaktdaten:

Beitrag von odt »

Beim schnellen Überfliegen sticht mir LDCSProtocol::readyRead ins Auge. Hier scheinst Du Dich darauf zu verlassen, dass Du ein synchrones Protokoll hast. Kann es sein, dass der Client 2 Pakete schickt, die zusammen beim Server ankommen und als eine Nachricht verarbeitet wird?

Im LDCSProtocol::startWork() verbindest Du die Signale und Slots, anschliessend leerst Du den Buffer. In ner Multithreaded-Umgebung kann es sein, dass vorher ein ready-Read reinkam, Daten eingelesen wurden und sie hier überschrieben wurden.

Beides scheint nicht so auf Deinen Bug zu deuten. Wie verhält es sich bei n gleichzeitigen Clients? Wenn Du via Wireshark das Problem nicht eingrenzen kannst, vereinfachen und reduzieren. Ein kleines F5-Projekt könnte ich für Dich auch mal auf ux laufen lassen.
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Vielen Dank für die Anregungen und das Angebot. Ich komme erst morgen wieder auf den Rechner der die Probleme macht. Ich werde dann berichten
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Hm, ich glaube es ist ein Timing-Problem. Mit WireShark auf beiden Seiten oder Logging auf beiden Seiten ist das Problem nicht reproduzierbar. Ich werde es erstmal ad acta legen müssen.

Vielen Dank für die Hilfe!
Antworten