ubuntuusers.de

Komplexes debugging Problem (Netzwerk)

Status: Gelöst | Ubuntu-Version: Ubuntu MATE 20.04 (Focal Fossa)
Antworten |

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6514

Wohnort: Hamburg

Ich habe da ein komplexes Problem. Eine größere Anwendung erzeugt ein weiteres Fenster zur Kommunikation mit einem Server. Grundsätzlich funktioniert das auch. Aber es passiert immer wieder mal, das ein connect() zum Server mit Fehler zurück kommt. Bei einem erneutem Versuch bekomme ich aber das gewünschte Ergebnis.

Eine Analyse mit tcpdump/wireshark kann mir da jetzt auch nicht wirklich weiter helfen. Der Server meldet auf der Konsole, dass eine Verbindung zustande gekommen ist, wobei es aber nicht weiter geht. Das ist auch anhand des Quellcodes des Clients nachvollziehbar:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int vt_netwin::send_request( char * sbuf ) {
    char *  buffer = (char*)malloc( E_BUF );
    struct  sockaddr_in address;
    int     n;
    char *  cp = NULL;

    if( (sock_fd = socket( AF_INET, SOCK_STREAM, 0 )) < 0 )
        return -1;
    address.sin_family = AF_INET;
    address.sin_port = htons( std::stoi( server_port ) );
    inet_aton( server_addr.c_str(), &address.sin_addr );
    if( connect( sock_fd, (struct sockaddr*)&address, sizeof( address ) ) < 0 ) {
        fl_alert( "No connection to:\n%s", server_addr.c_str() ); // <<< Fehlerfenster
        close( sock_fd );
        sock_fd = -1;
        return -1;
    }
    if( send( sock_fd, sbuf, strlen( sbuf ), 0 ) < 0 ) {
        fl_alert( "Send error!" );
        close( sock_fd );
        sock_fd = -1;
        return -1;
    }
...

Der Fehler tritt allerdings nur sporadisch auf, beim Wechsel vom Netzwerkfenster zur Anwendung.

Was ich mir zur Fehlereingrenzung wünsche, wäre so etwas wie eine Historie der zuletzt ausgeführten Funktionen, am besten mit den relevanten Variablen, so wie es einige Debugger nach einem Core-Dump anzeigen können.

Gibt es da etwas was ich machen kann?

p.s. Ich habe 2 Anhänge hoch geladen, aber das geht wohl nicht mehr so wie Früher.

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6514

Wohnort: Hamburg

gelöscht

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

Dakuan schrieb:

Ich habe da ein komplexes Problem. Eine größere Anwendung erzeugt ein weiteres Fenster zur Kommunikation mit einem Server. Grundsätzlich funktioniert das auch. Aber es passiert immer wieder mal, das ein connect() zum Server mit Fehler zurück kommt. Bei einem erneutem Versuch bekomme ich aber das gewünschte Ergebnis.

Das ist Netzwerk - da kann grundsätzlich immer etwas schief gehen.

Eine Analyse mit tcpdump/wireshark kann mir da jetzt auch nicht wirklich weiter helfen.

Du würdest zumindest sehen können, dass der Request vom Client rausgegangen und beim Server angekommen ist. Und wie der dann reagiert hat.

Der Server meldet auf der Konsole, dass eine Verbindung zustande gekommen ist, wobei es aber nicht weiter geht. Das ist auch anhand des Quellcodes des Clients nachvollziehbar:

Wie soll das am Client Code nachvollziehbar sein, dass es am Server "nicht weiter geht"?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int vt_netwin::send_request( char * sbuf ) {
    char *  buffer = (char*)malloc( E_BUF );
    struct  sockaddr_in address;
    int     n;
    char *  cp = NULL;

    if( (sock_fd = socket( AF_INET, SOCK_STREAM, 0 )) < 0 )
        return -1;
    address.sin_family = AF_INET;
    address.sin_port = htons( std::stoi( server_port ) );
    inet_aton( server_addr.c_str(), &address.sin_addr );
    if( connect( sock_fd, (struct sockaddr*)&address, sizeof( address ) ) < 0 ) {
        fl_alert( "No connection to:\n%s", server_addr.c_str() ); // <<< Fehlerfenster
        close( sock_fd );
        sock_fd = -1;
        return -1;
    }
    if( send( sock_fd, sbuf, strlen( sbuf ), 0 ) < 0 ) {
        fl_alert( "Send error!" );
        close( sock_fd );
        sock_fd = -1;
        return -1;
    }
...

Ist sock_fd ein Member der Klasse? Ich sehe das nicht in der Funktion definiert. Falls ja, kann es da Probleme mit parallelem Schreiben geben?

Der Fehler tritt allerdings nur sporadisch auf, beim Wechsel vom Netzwerkfenster zur Anwendung.

Was ich mir zur Fehlereingrenzung wünsche, wäre so etwas wie eine Historie der zuletzt ausgeführten Funktionen, am besten mit den relevanten Variablen, so wie es einige Debugger nach einem Core-Dump anzeigen können.

strace.

p.s. Ich habe 2 Anhänge hoch geladen, aber das geht wohl nicht mehr so wie Früher.

Da hat sich nix geändert. Hast Du die Anhänge wirklich hochgeladen?

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6514

Wohnort: Hamburg

Wie soll das am Client Code nachvollziehbar sein, ...

In Zeile 7 kann man sehen, dass der Socket erzeugt wird. Der Connect in Zeile 12 funktioniert aber nicht. Gesendet wird nichts weiter, das hätte in Zeile 18 passieren sollen.

Der Server hat die Verbindung angenommen, sagt er, bekommt aber keine Daten. Der Request-Header wurde nicht gesendet.

Ist sock_fd ein Member der Klasse? Ich sehe das nicht in der Funktion definiert. Falls ja, kann es da Probleme mit parallelem Schreiben geben?

Ja, ist Member aber Threads kommen nicht vor. Allerdings gibt es verschiedene Wege zum send_request(). Deswegen setze ich sock_fd beim Schließen auf -1 (ungültig).

Hast Du die Anhänge wirklich hochgeladen?

Das hatte ich zuerst vergessen, weswegen ich den Beitrag nochmal editieren wollte. Dabei entstand aber ein komplettes Duplikat. Ich war dann so genervt, dass ich den zweiten Beitrag gleich wieder gelöscht hatte. Mit den Bildern wollte ich die Situation besser zeigen. Die Bilder sind jetzt aber auf einem anderen PC.

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6514

Wohnort: Hamburg

So, ich habe jetzt von tcpdump zwei dump-Dateien (532 + 2544 Bytes). Der Anfang sieht für mich bei beiden gleich aus (SYN, SYN+ACK, ACK), aber warum es dann nicht weiter geht, kann ich daraus nicht erkennen.

Wie es bisher aussieht, tritt das Problem nur auf, wenn die Anforderung vom Hauptprogramm kommt. Wenn die Anfurderung direkt vom Netzwerkfenste kommt, ist das bisher noch nicht passiert.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

Dakuan schrieb:

Wie es bisher aussieht, tritt das Problem nur auf, wenn die Anforderung vom Hauptprogramm kommt. Wenn die Anfurderung direkt vom Netzwerkfenste kommt, ist das bisher noch nicht passiert.

Für mich klingt das nach einem Timing-Problem, wie es oft bei Multithreading auftritt.

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6514

Wohnort: Hamburg

Oh man, wie komme ich denn da ran? Eigentlich benutze ich keine Threads. Allerdings habe ich den Verdacht, das mein Toolkit (FLTK) Die eigentliche Anwendung in einem anderen Thread laufen lässt. Aber ich kann mir jetzt nicht vorstellen, inwieweit das hier eine Rolle spielen kann. Denn wenn ein Button oder Menüpunkt aktiviert wurde, läuft alles im Anwenderbereich weiter.

Ich beschreibe mal anhand des Screeshots was da passiert. Im linken, gelben, Fenster wird eine Datei angeklickt. Die dazu gehörige Vorschaudatei wird geladen und die Metadaten werden angezeigt. Am Ende wird dann geprüft ob das Netzwerkfenster aktiv ist. Wenn das aktiv ist wird der Dateiname dorthin kopiert.

1
2
3
4
5
6
7
    ...
    check_buttons();
    #ifdef WITH_NETSUPPORT
    if( netwin )
        netwin->set_filename( fname ); // <<<<<
    #endif
}

Der Dateiname erscheint dann in der Zeile unter der Dateiliste im Netzwerkfenster. Ein anklicken eines Namens in der (Server-)Dateiliste bewirkt das selbe, es wird die selbe Methode benutzt. Ein Ändern des Inhalts dieser Zeile bewirkt eine Statusabfrage beim Server um die Daten für Size und Date zu bekommen.

Das ist eigentlich eine geradlinige Sache. Einen irgendwie von mir veranlassten Threadwechsel kann ich da jetzt nicht erkennen. Aber vielleicht sollte ich noch erwähnen, das der Server auf einem Beispiel aus dem Buch "Linux-UNIX-Programmierung" beruht und für jede Anfrage einen neuen Prozess erzeugt.

output46.dump (532 Bytes)
Download output46.dump
output47.dump (2.5 KiB)
Download output47.dump
Bilder

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11267

Wohnort: München

Dakuan schrieb:

Aber vielleicht sollte ich noch erwähnen, das der Server auf einem Beispiel aus dem Buch "Linux-UNIX-Programmierung" beruht und für jede Anfrage einen neuen Prozess erzeugt.

Läuft dein Programm da eventuell in irgendwelche Ressourcen-Beschränkungen (z.B. gleichzeitig offene Dateien, Anzahl der Threads/Prozesse pro User usw.)?

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6514

Wohnort: Hamburg

Keine Ahnung. Wie kann ich das herausfinden? Mit der Anzahl der gleichzeitig offenen Dateien hatte ich letztmalig unter Win 98 zu tun. Seit dem bin ich da nie wieder drüber gestolpert.

Aber in diesem Fall denke ich nicht, das es mit zu vielen offenen Datei-Descriptoren zu tun hat. Es können ja nicht zu viele sein, da die sock_fd's ja nach jeder Anfrage geschlossen werden.

Zur Serverseite kann ich jetzt allerdings noch nicht viel sagen. Da könnte ja auch das Problem liegen. Allerdings kann ich das, mit meinen mangelnden TCP Kenntnissen, nicht wirklich beurteilen. Aus den Dumps kann ich das jedenfalls nicht ablesen.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

Dakuan schrieb:

Oh man, wie komme ich denn da ran? Eigentlich benutze ich keine Threads. Allerdings habe ich den Verdacht, das mein Toolkit (FLTK) Die eigentliche Anwendung in einem anderen Thread laufen lässt.

Das ist ein recht übliches Verfahren, dass ein GUI-Toolkit einen eigenen Thread für die Event-Bearbeitung startet, so dass man i.d.R. schon mal zwei Threads hat.

Aber vielleicht sollte ich noch erwähnen, das der Server auf einem Beispiel aus dem Buch "Linux-UNIX-Programmierung" beruht und für jede Anfrage einen neuen Prozess erzeugt.

Ein unwesentliches Detail... 😉 Wichtig wäre zu wissen, wie Du dabei mit Dateideskriptoren umgehst. Wenn ein Prozess einen Socket aufmacht und dann forked, dann wird der offene Dateideskriptor an das Kind vererbt. Erst wenn er in beiden Prozessen geschlossen wird, gibt der Kernel die Ressource (z.B. Netzwerkverbindung) frei. Normalerweise macht man die Deskriptoren im Parent und Child nach einem fork zu, die man nicht mehr braucht.

Dakuan schrieb:

Aber in diesem Fall denke ich nicht, das es mit zu vielen offenen Datei-Descriptoren zu tun hat. Es können ja nicht zu viele sein, da die sock_fd's ja nach jeder Anfrage geschlossen werden.

s.o. Und: Du kannst mit strace herausfinden, welcher Prozess wann welche FD öffnet und schließt.

Zur Serverseite kann ich jetzt allerdings noch nicht viel sagen. Da könnte ja auch das Problem liegen. Allerdings kann ich das, mit meinen mangelnden TCP Kenntnissen, nicht wirklich beurteilen. Aus den Dumps kann ich das jedenfalls nicht ablesen.

Ist der Server denn auch von Dir oder etwas anderes?

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6514

Wohnort: Hamburg

Ist der Server denn auch von Dir oder etwas anderes?

Der basiert auf einem Beispiel aus dem Buch "Linux-UNIX-Programmierung". Neben einigen Ergänzungen habe ich da bisher nur die Pufferung der Sendedaten verändert indem ich send() durch sendfile() ersetzt habe. Das Beispiel ging davon aus, dass die zu sendende Datei komplett in den Puffer passt.

Mit strace hatte ich mich bisher nur einmal zergnügt. Das haut einem extrem viel Daten um die Ohren.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11267

Wohnort: München

Dakuan schrieb:

Mit strace hatte ich mich bisher nur einmal zergnügt. Das haut einem extrem viel Daten um die Ohren.

Wenn du weißt, was dich interessiert, kannst du ja gezielt danach filtern - eventuell hilft eine deutschsprachige Einführung: Harald König: Linux-Versteher dank Strace

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6514

Wohnort: Hamburg

Meine Sicht auf strace hat sich durch das längliche Video etwas geändert. Allerdings bin ich damit noch nicht weiter gekommen.

Beim Einsatz von strace tritt das Problem nicht mehr auf. Aber das Programm wird sehr träge, auch wenn ich den Output in eine Datei schreibe.

strace -o st.txt -e trace=socket,connect,send ./vted

Außerdem sehe ich den send() Befehl nicht und bei write() sehe ich nur den Konsolen Output. Ich dachte send() stützt sich auf write().

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6514

Wohnort: Hamburg

Ich habe jetzt mir mal nach einem erfolglosen connect() die Variable errno ausgeben lassen:

connect error: 4

und übersetzen lassen

~$ errno 4
EINTR 4 Unterbrechung während des Betriebssystemaufrufs
~$ 

Was bedeutet das? Ich hätte da eher so etwas wie "busy" erwartet. Aber eigentlich ist das auch Quatsch, denn der Request ist ja schon beim Server angekommen, wie man in den Dateien von tcpdump sehen kann. -grübel-

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11267

Wohnort: München

Dakuan schrieb:

Beim Einsatz von strace tritt das Problem nicht mehr auf.

Wenn eine Verlangsamung reicht, riecht das nach einer Race-Condition.

Aber das Programm wird sehr träge, auch wenn ich den Output in eine Datei schreibe.

Für Race-Conditions am besten mit einer möglichst schnellen Maschine testen, wenn du keine Idee hast, wo die herkommen könnten.

strace -o st.txt -e trace=socket,connect,send ./vted

Außerdem sehe ich den send() Befehl nicht und bei write() sehe ich nur den Konsolen Output. Ich dachte send() stützt sich auf write().

Wenn du Aktionen der geforkten Prozesse sehen willst, müsstest du noch -f bzw. -ff als Parameter setzen, um entweder alles in einer Datei oder nach PID aufgesplittet in Dateien zu scheiben. Mit -tt bekommst du noch hochaufgelöste Zeitstempel.

Antworten |