Problem mit Netzwerk Programmierung

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
dakuan
Beiträge: 97
Registriert: 28.04.2011 22:09:39

Problem mit Netzwerk Programmierung

Beitrag von dakuan » 14.08.2023 22:14:41

Ich habe da ein Problem mit meiner Client-Server Netzwerkanwendung. Bisher wird für jede Anfrage eine Verbindung aufgebaut und nach Erhalt der Antwort oder eines Fehlers geschlossen. Daher war es bisher nicht notwendig, die Verbindung weiter zu prüfen.

Nach einer Erweiterung des Clients kann es aber passieren, das in Folge einer Anfrage weitere Anfragen gewünscht werden. Tatsächlich geht es dabei um die Abfrage eines Verzeichnisses wobei, je nach Vorgeschichte, zusätzlich eine Vorschaudatei geladen werden soll. Dafür wird bisher eine neue Verbindung mit einem neuen Socket Filediscriptor erzeugt, was irgendwie nicht optimal ist.

Ich frage mich daher, ob es möglich ist, den vorhandenen Filedescriptor weiter zu nutzen und wie ich das prüfen kann. Der Punkt ist, wie kann ich feststellen, ob die Gegenstelle (Gegner :twisted: ) die Verbindung bereits geschlossen hat?

Bisher verwende ich folgende mir bekannte Fuktionen:

Code: Alles auswählen

    sock_fd = socket( rp->ai_family, rp->ai_socktype, rp->ai_protocol );

    if( connect( sock_fd, rp->ai_addr, rp->ai_addrlen ) != -1 )

    if( send( sock_fd, sbuf, strlen( sbuf ), 0 ) < 0 ) {

    if( (n = recv( sock_fd, rcv_buf, RCVBUF_SIZE, 0 )) > 0 ){

    n_rcv = read( socfd, &ch, 1 );
Ich weiß, dass die Gegenstelle die Verbindung schließen kann, aber wie kann der dumme Client das erfahren?

uname
Beiträge: 12099
Registriert: 03.06.2008 09:33:02

Re: Problem mit Netzwerk Programmierung

Beitrag von uname » 15.08.2023 07:16:33

Ich glaube ich habe schon +20 Jahre nicht mehr Sockets in C programmiert. Schön, dass sich scheinbar nichts verändert hat. Nach etwas Suche kannst du dir mal diese Seite anschauen.

Ich habe auch mal eine KI gefragt. Diese hat auf die Funktionen rev() oder select() verwiesen und mir schöne Programme geschrieben. Nach Rückfrage (mit der Antwort einer KI sollte man nie zufrieden sein) hat die KI auch noch auf shutdown() sowie aus sigpipe_handler() verwiesen. Letztgenannte Funktion gehört zu signal.h. Zudem hat die KI auf deine Befehle send() und connect() jeweils mit Rückgabewert -1 hingewiesen.

Auch habe ich noch nach einem Gedicht zur Socketprogrammierung in C gefragt. Es ist gar nicht mal so schlecht.
https://chat.chatgptdemo.net hat geschrieben:In C, wo Sockets zum Leben erwachen,
Verbindungen entstehen, Daten erwachen.
Durch TCP oder UDP's Macht,
Kommunikation nimmt ihren Flug.

Server und Client, Hand in Hand,
Ein Netzwerk-Tanz im ganzen Land.
Akzeptieren, Verbinden, Senden mit Anmut,
Daten fließen in rasendem Mut.

Bytes werden ausgetauscht, Nachrichten gesendet,
In Paketen reisen sie, niemals beendet.
Von Quelle zu Ziel, weit entfernt,
In der Welt der Sockets, wo Verbindungen entstehn.

Durch Sockets in C, eine Welt entfaltet,
Wo Daten reisen, Geschichten gestaltet.
Im Code erschaffen wir, ein Netzwerk gewebt,
Socket-Programmierung, eine Magie, die man glaubt.

dakuan
Beiträge: 97
Registriert: 28.04.2011 22:09:39

Re: Problem mit Netzwerk Programmierung

Beitrag von dakuan » 15.08.2023 15:33:32

Schön, dass sich scheinbar nichts verändert hat.
Da sind einige neuere Funktionen hinzugekommen, wie z.B. getaddrinfo(). Damit ist es wohl etwas einfacher IP4 und IP6 zu handhaben.

Aber mein Problem scheint nicht einfach lösbar zu sein. Die meisten Fehler werden wohl erst bemerkt, wenn eine Aktion gestartet wird.

Am vielversprechendsten scheint mir das Signal SIG_PIPE zu sein, jedenfalls wenn der Server die Verbindung beendet. SIG_PIPE hatte ich gar nicht mit Sockets in Verbindung gebracht. Bei richtigen Fehlern kann ich sowieso nicht weitermachen. Aber da bekomme ich ja auch schon einen Fehlercode von socket() oder connect().

Ob getsockopt() mit SO_ERROR etwas bringt, muss ich auch mal ausprobieren. Das kann aber etwas dauern.

JTH
Moderator
Beiträge: 3026
Registriert: 13.08.2008 17:01:41
Wohnort: Berlin

Re: Problem mit Netzwerk Programmierung

Beitrag von JTH » 15.08.2023 16:33:44

Da du von Verbindung sprichst, geh ich mal von einem TCP-Socket aus. Du solltest normalerweise einfach wiederholt versuchen, etwas von der Gegenseite zu empfangen. Denn recv() gibt dir bei Bedarf zurück, wenn die Verbindung von der Gegenseite geschlossen wurde:
man recv.2 hat geschrieben: RETURN VALUE
[…]

When a stream socket peer has performed an orderly shutdown, the return value will be 0 (the traditional "end-of-file" return).
Also irgendwas in die Richtung

Code: Alles auswählen

while ((n = recv(sock_fd, rcv_buf, RCVBUF_SIZE, 0)) > 0) {
  // tu was
}

if (n == 0) {
  // reguläres Ende
} else {
  // errno verrät, was schiefgelaufen ist
}
Vorausgesetzt, du hast nicht O_NONBLOCK auf dem Socket gesetzt. Falls doch, müsste man das Herausspringen aus der Schleife anders gestalten.
Manchmal bekannt als Just (another) Terminal Hacker.

dakuan
Beiträge: 97
Registriert: 28.04.2011 22:09:39

Re: Problem mit Netzwerk Programmierung

Beitrag von dakuan » 15.08.2023 19:31:23

Es ist wohl besser, wenn ich (wieder mal) meinen Ablauf ändere. Die Grundidee war, dass der Client genau eine Abfrage (HTTP GET) sendet und dann die Antwort auswertet. Das kann dann der Download einer Video Datei, einer Vorschaudatei oder ein Verzeichnisinhalt sein.

Bei der Verzeichnisabfrage hatte ich kürzlich noch die Anzeige einer Vorschaudatei eingebaut, wenn der Dateiname auch lokal vorhanden ist. Genau dadurch ist jetzt das Problem entstanden, da der hierfür zuständige Programmteil einen neuen Socket eröffnen will, was bei einer Abfrage durch den Anwender auch richtig ist. Ich muss das jetzt irgendwie trennen.
Denn recv() gibt dir bei Bedarf zurück, wenn die Verbindung von der Gegenseite geschlossen wurde:
Im Prinzip mache ich das auch so:

Code: Alles auswählen

...
        case VT_ACT_FILE:       // receive a video file
            if( file_type == FTYPE_VIDEO  &&  content_length > 0 ) {
                if( (fp = fopen( name, "wb" )) ) {
                    fsize = content_length;
                    while( content_length > 0 ) {
                        n = recv( sock_fd, rcv_buf, RCVBUF_SIZE, 0 );
                        if( n > 0 ) {
                            fwrite( rcv_buf, n, 1, fp );
                            content_length -= n;
                        } else {
                            if( n == 0 )
                                break;  // OK
                            if( errno == EINTR )
                                continue;
                            err = -1;
                            break;
                        }
                        progress->value( (float)(fsize-content_length)/fsize );
                        Fl::check();
                    }
                    if( ferror( fp ) != 0 ) {
                        err = -1;
                        fl_alert( "Error - writing to file:\n%s", name );
                    }
...
Ich muss das wohl sauber trennen und nicht während eine Abfrage noch nicht abgeschlossen ist, eine neue starten wollen.

Antworten