Seite 1 von 1
merkwürdiges Network-Problem mit QTcpServer und QTcpSocket
Verfasst: 12. April 2011 13:06
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
Verfasst: 12. April 2011 14:10
von solarix
Die Kristallkugel ist leider im Frühlingsservice..
Code zur Sende/Empfangsroutine?
Verfasst: 12. April 2011 14:27
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

Verfasst: 12. April 2011 14:54
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
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..
Verfasst: 12. April 2011 14:57
von Willi2793
Das ist nahezu eine exakte Kopie des Parts aus meinem Programm

Verfasst: 12. April 2011 15:10
von solarix
wunderbar
Hast du mal mit Wireshark die ankommenden Daten (respektive dein Netzwerkprotokoll überhaupt) verifiziert?
Verfasst: 12. April 2011 15:14
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?
Verfasst: 12. April 2011 15:55
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).
Verfasst: 13. April 2011 09:14
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.
Verfasst: 13. April 2011 09:17
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
Verfasst: 14. April 2011 10:29
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!