Socketprogrammierung high-optimized

Alles rund um die Programmierung mit Qt
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

Socketprogrammierung high-optimized

Beitrag von KartoffelKiffer »

Hallo,

wir haben zurzeit zwei Programme laufen, die sich über einen Socket miteinander unterhalten. Programm A ist unter Qt 3.11 geschrieben und sendet mittels QSocket und QTextStream eine Mitteilung an Programm B, geschrieben in Qt 4.4.0.

Dabei wird in Programm A ein Connect im Konstruktor aufgebaut und über die Funktion SendMessage(QString) eine Mitteilung an Programm B gesendet.

Programm B liest diese Mitteilung ein, sobald ein SIGNAL(readyRead()) aus der "nextPendingConnection" eintrifft. Diese Message wird per

Code: Alles auswählen

QString msg = "";
QTextStream is(m_ClientConnection); // InputStream
msg.append(is.readAll());
eingelesen.

Rufe ich nun direkt 3x hintereinander die Funktion SendMessage auf, so kommt in Programm B lediglich eine Mitteilung an. Der Inhalt besteht aus Mitteilung 1 + Mitteilung 2 + Mitteilung 3 gesendet aus Programm A.

Nun habe ich mit einem Arbeitskollegen gesprochen, der meinte, dass das Betriebssystem und auch TCP/IP hochoptimiert arbeitet, was Traffic angeht. Das OS "sieht" also, das Programm A möchte gern etwas direkt hintereinander senden, packt die drei Mitteilungen also in ein Paket zusammen und verschickt es als solches?

Interessant dabei ist noch, dass wenn ich hinter die SendMessage-Funktionen in Programm A einen Breakpoint setze, und langsam durchsteppe, in Programm B erst etwas angezeigt wird, sobald die letzte Message aus Programm A versendet wurde.

Über eine Lösung des Problems, oder auch Hinweise bin ich sehr gespannt und natürlich auch dankbar.




Mfg KK
Zuletzt geändert von KartoffelKiffer am 19. Juni 2009 14:08, insgesamt 1-mal geändert.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Du kannst das nicht verhindern - Du musst dich um solche Dinge (messages splitten) schon selbst kümmern.
Warum es beim Debuggen auch nicht geht liegt wohl daran, dass Du kein flush() machst und es alles noch in irgend einen Buffer vor sich hinschlummert. Aber wie gesagt - hilft Dir eh nichts zur Lösung deines Problems.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
MiKla
Beiträge: 134
Registriert: 29. Juli 2007 15:29

Beitrag von MiKla »

unter Windows (WinApi) (Linux wahrscheinlich auch) gibt es:

Code: Alles auswählen

setsockopt(sock, IPPROTO_TCP,  TCP_NODELAY, (char *) &flag,  sizeof(int));
damit wird jedes Paket sofort rausgeschickt, dass heisst es wird nicht es gesammelt bis der Frame groß genug ist.

Ob es in Qt 'ne Option dafür gibt weiss ich nicht.
MiKla
Beiträge: 134
Registriert: 29. Juli 2007 15:29

Beitrag von MiKla »

hab noch das gefunden:

Code: Alles auswählen

/* Disable the Nagle (TCP No Delay) algorithm ON UNIX PLATFORMS */
int sockID, flag, ret;
sockID = yourQTcpSocket->socketDescriptor();
flag = 1;
ret = setsockopt( sockID, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag) );

if (ret == -1) {
   // error while trying to disable the nagle optimization
} 

/* Disable the Nagle (TCP No Delay) algorithm ON WINDOWS PLATFORMS */
SOCKET sockId = yourQTcpSocket->socketDescriptor();
int ret;
BOOL bOptVal = true;      // seems to be true here, but not completely sure.
ret = setsockopt(sockID, IPPROTO_TCP, TCP_NODELAY, (char*)&bOptVal, sizeof(bool))

if ( ret == SOCKET_ERROR) {
    // error while trying to disable the nagle optimization
    int error = WSAGetLastError(void);
} 
vielleicht hilft's weiter
PeterLustig
Beiträge: 386
Registriert: 21. November 2007 20:07

Beitrag von PeterLustig »

@Michael.Klank

Deine Hilfsbereitschaft in Ehren, aber
- Benutz doch bitte die Editierfunktion :) Dafür ist sie da
- Das hier ist ein Qt Forum, er programmiert mit Qt, es geht um Qt, warum also Code von der puren Socket API? Die wird nicht im geringsten helfen
- Christian hat den Befehl bereits genannt: flush
MiKla
Beiträge: 134
Registriert: 29. Juli 2007 15:29

Beitrag von MiKla »

@Michael.Klank
Deine Hilfsbereitschaft in Ehren, aber
- Benutz doch bitte die Editierfunktion Smile Dafür ist sie da
- Das hier ist ein Qt Forum, er programmiert mit Qt, es geht um Qt, warum also Code von der puren Socket API? Die wird nicht im geringsten helfen
- Christian hat den Befehl bereits genannt: flush
http://www.qtcentre.org/forum/f-qt-prog ... -3037.html

ist vielleicht doch nicht soweit weg :wink:
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Nun habe ich mit einem Arbeitskollegen gesprochen, der meinte, dass das Betriebssystem und auch TCP/IP hochoptimiert arbeitet, was Traffic angeht. Das OS "sieht" also, das Programm A möchte gern etwas direkt hintereinander senden, packt die drei Mitteilungen also in ein Paket zusammen und verschickt es als solches?
Das OS sieht das zwar nicht, aber die Wahrscheinlichkeit ist einfach hoch, dass man mehr als nur ein paar Bytes versenden möchte... Die Treiber optimieren also so nach "best practice"...

Ein wichtiger Punkt noch: die Frames einzeln zu versenden ist (in diesem Fall) idiotisch, weil die Daten auf der Empfangsseite ja wieder zusammengeführt werden (OS sammelt die Eingangsdaten in einem Empfangsbuffer). D.h., du kannst auch einmal Message 1 und die Hälfte von Message 2 im Puffer haben (bei grossen Messages sogar wahrscheinlich)!

Betrachte das ganze also als "Stream" und definiere ein eigenes Protokoll (wie schon von Chr. vorgeschlagen).
Zuletzt geändert von solarix am 10. Juni 2008 08:53, insgesamt 1-mal geändert.
MiKla
Beiträge: 134
Registriert: 29. Juli 2007 15:29

Beitrag von MiKla »

Das OS sieht das zwar nicht, aber wie Wahrscheinlichkeit ist einfach hoch, dass man mehr als nur ein paar Bytes versenden möchte... Die Treiber optimieren also so nach "best practice"...

Ein wichtiger Punkt noch: die Frames einzeln zu versenden ist (in diesem Fall) idiotisch, weil die Daten auf der Empfangsseite ja wieder zusammengeführt werden (OS sammelt die Eingangsdaten in einem Empfangsbuffer). D.h., du kannst auch einmal Message 1 und die Hälfte von Message 2 im Puffer haben (bei grossen Messages sogar wahrscheinlich)!
Das sehe ich nicht ganz so. In meinem Fall war es so, dass ich alle 60ms ein TCP Paket (16 Bytes) an eine SPS verschicken mußte (Bilderkennung, Druckmarke). Ohne "TCP_NODELAY" hat das OS die Nachrichten gesammelt und dann erst verschickt. Das war in diesem Fall tötlich, weil die SPS eine Druckmarkenregelung macht und so nicht mehr sichergestellt war, dass das TCP Paket zur Druckmarke gepasst hat.
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

es war klar, dass sowas kommt... ich habe das "in diesem Fall" nicht einfach so aus Langeweile geschrieben..
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

Beitrag von KartoffelKiffer »

Seid gegrüßt. Ein simples flush() hat die Sache schonmal verbessert, aber nicht behoben. Über kurz oder lang werde ich nicht drum herum kommen, ein eigenes Protokoll zu schreiben.

Schonmal besten Dank für die Hilfe!




Mfg KK
Zuletzt geändert von KartoffelKiffer am 19. Juni 2009 14:08, insgesamt 1-mal geändert.
Markus
Beiträge: 105
Registriert: 31. Januar 2005 16:21

Beitrag von Markus »

Sind es nur Strings, die zwischen den Programmen verschickt werden? Wenn ja, dann kannst Du ein CR/LF anhängen, wenn die Daten in den Socket gehen und auf der Gegenseite einfach mit canReadline() und readLine() die Daten zeilenweise abholen.
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

Beitrag von KartoffelKiffer »

Hallo Markus,

ja es sind bloß Strings, habe es nun so gelöst, wie Du gesagt hast. Guter Einfall, danke!



Mfg KK
Zuletzt geändert von KartoffelKiffer am 19. Juni 2009 14:08, insgesamt 1-mal geändert.
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

Beitrag von KartoffelKiffer »

Hallo,

ich habe noch etwas die Anwendung getestet, da genau dieser Engpass in einer weiteren Applikation aufgetreten ist.

Ich sende jede Millisekunde eine Mitteilung an den Server ab.

Dabei ist folgende Variante am Schnellsten gewesen:

Code: Alles auswählen

void DataIfc::IncomingMessage()
{
	QString buffer = "";
	QTextStream is(m_ClientConnection); // InputStream

	buffer.append(is.readAll());

	QStringList splitted = buffer.split("\n");

	QString message = "";
	for (int i = 0; i < splitted.size(); ++i)
	{
		message = splitted.at(i);
		if (message.length() > 0)
		{
			emit GotData(message);
		}
	}
}
Wobei bei jedem Senden ein \n hinter den String geschrieben werden muss.

Lese ich den Text per readLine() aus, bekomme ich genau 400 Mitteilungen in der Sekunde mit, bei der o.g. sind es genau 500. Was mir allerdings noch auffällt, ich muss in der for-Schleife die Abfrage auf Größe > 0 machen, da bei mir einige leere Strings dazwischen waren. Ungefähr jeder zweite (was den anderen 500 Mitteilungen entsprechen würde).


Ich hoffe damit trotzdem noch jemandem geholfen zu haben.




Mfg KK
Zuletzt geändert von KartoffelKiffer am 19. Juni 2009 14:08, insgesamt 1-mal geändert.
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Ich hoffe damit trotzdem noch jemandem geholfen zu haben.
Und ich hoffe, dass dieser Code von niemandem kopiert wird.. denn bei

Code: Alles auswählen

buffer.append(is.readAll());
QStringList splitted = buffer.split("\n"); 
dürfte die letzte Meldung jeweils unvollständig sein.. besonders wenn die Meldungen i.d.R. grösser sind..
MiKla
Beiträge: 134
Registriert: 29. Juli 2007 15:29

Beitrag von MiKla »

Sollten dann nicht bei 1 Message/ 1ms = 1000 Messages /s sein und nicht 500 Messages/s ???
Antworten