2012-05-25 3 views
5

Ich verwende blockierende TCP-Sockets für meinen Client und Server. Immer wenn ich lese, überprüfe ich zuerst, ob Daten im Stream verfügbar sind, indem Sie select verwenden. Ich lese und schreibe immer 40 Bytes gleichzeitig. Während die meisten Lesevorgänge nur wenige Millisekunden oder weniger dauern, benötigen einige nur mehr als eine halbe Sekunde. Das nachdem ich weiß, dass auf dem Socket Daten vorhanden sind.Was könnte die Ursache für sehr langsame Socket Reads sein?

Ich verwende auch TCP_NODELAY

Was es verursachen könnte?

EDIT 2

analysiert I den Zeitstempel für jedes Paket gesendet und empfangen und sah, dass diese Verzögerung geschieht nur, wenn der Client das Objekt zu lesen versucht, bevor das nächste Objekt durch den Server geschrieben wird. Zum Beispiel schrieb der Server die Objektnummer x und danach versuchte der Client das Objekt x zu lesen, bevor der Server mit dem Schreiben der Objektnummer x + 1 beginnen konnte. Dies lässt mich vermuten, dass auf der Serverseite eine Art Verschmelzung stattfindet.

EDIT

Der Server, auf 3 verschiedenen Ports überwacht. Der Client verbindet sich nacheinander mit jedem dieser Ports.

Es gibt drei Verbindungen: Eine, die einige Daten häufig vom Server an den Client sendet. Ein zweiter, der nur Daten vom Client an den Server sendet. Und eine dritte, die sehr selten verwendet wird, um einzelne Datenbytes zu senden. Ich stehe vor dem Problem mit der ersten Verbindung. Ich überprüfe unter Verwendung select(), dass Daten auf dieser Verbindung verfügbar sind, und wenn ich dann das 40-Byte-Lesen Zeitstempel, finde ich, dass ungefähr eine halbe Sekunde für diesen Lesevorgang genommen wurde.

Alle Hinweise, wie diese zum Profil auf Linux wäre

mit gcc sehr hilfreich.

rdrr_server_start(void) {
int rr_sd; int input_sd; int ack_sd; int fp_sd;

startTcpServer(&rr_sd, remote_rr_port); startTcpServer(&input_sd, remote_input_port); startTcpServer(&ack_sd, remote_ack_port); startTcpServer(&fp_sd, remote_fp_port);

connFD_rr = getTcpConnection(rr_sd); connFD_input = getTcpConnection(input_sd); connFD_ack= getTcpConnection(ack_sd); connFD_fp=getTcpConnection(fp_sd); }

static int getTcpConnection(int sd) { socklen_t l en;
struct sockaddr_in clientAddress; len = sizeof(clientAddress); int connFD = accept(sd, (struct sockaddr*) &clientAddress, &len); nodelay(connFD); fflush(stdout); return connFD; }

static void startTcpServer(int *sd, const int port) { *sd= socket(AF_INET, SOCK_STREAM, 0); ASSERT(*sd>0);

// Set socket option so that port can be reused int enable = 1; setsockopt(*sd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));

struct sockaddr_in a; memset(&a,0,sizeof(a)); a.sin_family = AF_INET; a.sin_port = port; a.sin_addr.s_addr = INADDR_ANY; int bindResult = bind(*sd, (struct sockaddr *) &a, sizeof(a)); ASSERT(bindResult ==0); listen(*sd,2); } static void nodelay(int fd) { int flag=1; ASSERT(setsockopt(fd, SOL_TCP, TCP_NODELAY, &flag, sizeof flag)==0); }

startTcpClient() { connFD_rr = socket(AF_INET, SOCK_STREAM, 0); connFD_input = socket(AF_INET, SOCK_STREAM, 0); connFD_ack = socket(AF_INET, SOCK_STREAM, 0); connFD_fp= socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in a; memset(&a,0,sizeof(a)); a.sin_family = AF_INET; a.sin_port = remote_rr_port; a.sin_addr.s_addr = inet_addr(remote_server_ip);

int CONNECT_TO_SERVER= connect(connFD_rr, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_input_port; CONNECT_TO_SERVER= connect(connFD_input, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_ack_port; CONNECT_TO_SERVER= connect(connFD_ack, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_fp_port; CONNECT_TO_SERVER= connect(connFD_fp, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

nodelay(connFD_rr); nodelay(connFD_input); nodelay(connFD_ack); nodelay(connFD_fp); }

+0

bewegen bekam ich das Gefühl, dass dieses Problem ist Hardware im Zusammenhang ... – Jah

+0

** Eines, das einige Daten häufig von dem Server an den Client sendet **, welche Größe wir sind sprechen über? – tuxuday

+0

Vielleicht ist TCP_NODELAY (Deaktivieren von Nagle) eine schlechte Wahl, was dazu führt, dass viele kurze Segmente gesendet werden, was zu mehreren Round-Trips pro "logischem" Paket führt. Außerdem viele Systemanrufe an der Seite des Anwendungsprogramms. – wildplasser

Antwort

0

Ein Client und mehrere Verbindungen?

einige der Socket-Funktionen blockieren möglicherweise Ihre Ausführung (d. H. Warten auf das Ergebnis der Funktionen). Ich würde vorschlagen, einen neuen Thread (auf der Serverseite) für jede Verbindung zu öffnen, damit sie sich nicht gegenseitig stören ...

aber ich schieße im Dunkeln; Sie müssen einige zusätzliche Informationen senden ...

+0

Ich habe einige Details in meiner Frage hinzugefügt. – AnkurVj

+0

Ich bin mir nicht sicher, ob Ihr Problem dort liegt, aber Sie sollten wahrscheinlich Ihre startTcpServer/getTcpConnection Funktionen neu anordnen. startTcpServer verfügt über einen Aufruf von listen(), der die Programmausführung blockiert, bis eine neue Verbindung hergestellt wird. Danach solltest du eine neue Verbindung annehmen und weitergehen, aber du hast noch einen weiteren Aufruf an startTcpServer => ein anderes listen ... (alle am selben Thread) Also akzeptierst du ankommende Verbindung, dein socket hört (aber in einer anderen Funktion) ... Vielleicht neu anordnen wie: startTcpServer(); getTcpConnection(); startTcpServer(); getTcpConnection(); ? – Ivica

+0

Ich denke nicht 'listen()' Blöcke? Nur 'accept()' macht IMO! – AnkurVj

0

Ihre Aussage ist immer noch verwirrend, d. h. "mehrere TCP-Verbindungen mit nur einem Client". Offensichtlich haben Sie einen einzigen Server, der an einem Port zuhört. Wenn Sie nun mehrere Verbindungen haben, bedeutet dies, dass mehrere Clients mit dem Server verbunden sind, die jeweils an einem anderen TCP-Client-Port angeschlossen sind. Der Server wird jetzt ausgewählt und antwortet auf den Client, der über Daten verfügt (dh, der Client hat einige Daten an seinen Socket gesendet). Wenn nun zwei Clients Daten gleichzeitig senden, kann der Server sie nur sequenziell verarbeiten. So wird der zweite Client nicht verarbeitet, bis der Server mit der Verarbeitung fertig ist.

Wählen Sie nur ermöglicht Server mehrere Deskriptoren (Sockets) zu überwachen und zu verarbeiten, welche Daten immer verfügbar sind. Es ist nicht so, dass es parallel verarbeitet. Sie benötigen dafür mehrere Threads oder Prozesse.

+0

Nein, der Server hat Verbindungen an drei verschiedenen Ports vom selben Client akzeptiert. Idealerweise sollte jede Verbindung vollständig von der anderen getrennt sein. – AnkurVj

+0

Kannst du bitte deinen Code einfügen, der die Erstellung von drei verschiedenen Server-Sockets zeigt und es zur Auswahl gibt? – fayyazkl

0

Vielleicht ist es etwas mit dem timeout Argument verwandt.

Was setzen Sie für timeout Argument von select Anruf?

Versuchen Sie, das Timeout-Argument zu einem größeren zu ändern und beobachten Sie die Latenz. Manchmal können zu kleine Zeitüberschreitung und sehr oft Systemaufrufe den Durchsatz tatsächlich beenden. Vielleicht können Sie bessere Ergebnisse erzielen, wenn Sie eine etwas größere Latenz voraussetzen, die realisierbar ist.

Ich vermute Timeout oder einen Code-Fehler.

+0

30 Millisekunden – AnkurVj

+0

Könnten Sie den Code mit dem Aufruf von 'select' und' read' aus dem Socket einfügen, nachdem 'select' zurückkehrt? –

+0

@AnkurVj Warum? Das ist eine erstaunlich kurze Zeitüberschreitung. Vielleicht verhungern Sie sich selbst vom Prozessor. Ein select() - Timeout ist normalerweise ein paar Sekunden. Was musst du tun, dass alle 30ms gemacht werden muss? – EJP

1

würde ich diese verdächtigt die Codezeile sein:

ASSERT(setsockopt(fd, SOL_TCP, TCP_NODELAY, &flag, sizeof flag)==0); 

Wenn Sie ein Release-Build ausgeführt wird, dann wird ASSERT meist wahrscheinlich nichts festgelegt, so würde der Anruf nicht tatsächlich durchgeführt werden. Der Aufruf von Setsockopt sollte nicht in der ASSERT-Anweisung sein. Stattdessen sollte der Rückgabewert (in einer Variablen) in der assert-Anweisung verifiziert werden. Asserts with side effects sind in der Regel eine schlechte Sache. Auch wenn dies nicht das Problem ist, sollte es wahrscheinlich geändert werden.

+0

Oh, ich habe den ASSERT-Teil, der in meinem Code definiert ist, nicht gezeigt. Es funktioniert gut. – AnkurVj

+1

Dieser Code ist immer noch anti-idiomatisch; Das Einfügen von Code mit Nebeneffekten als Argument eines Makros namens 'ASSERT' ist für Leser, die wahrscheinlich davon ausgehen, dass sie nicht auf Nicht-Debug-Builds laufen, sehr verwirrend. –

+0

@AnkurVj: Selbst wenn Sie denken, dass es in Ordnung ist, könnten Sie versuchen, den Anruf von der Assert zu entfernen. Nicht, dass ich an deinen Fähigkeiten zweifle, aber es ist selten, dass jemand ein ASSERT-Makro beim ersten Mal richtig schreibt. –

0

Sie können versuchen TCP_CORK (CORK'ed-Modus) mit Kernel-Erweiterungen mit GRO, GSO und TSO von ethtool deaktiviert:

  • innerhalb TCP_CORK gekennzeichnet Sitzung sendet, wird dafür sorgen, dass die Daten nicht in Teil gesendet werden Segment

  • deaktivieren generic-segmentation-offload, und tcp-segmentation-offload stellen Sie sicher, dass der Kernel keine künstlichen Verzögerungen zum Sammeln zusätzlicher einführen wird TCP-Segmente vor Daten zu/von User-Space