Julian Baumann: C++: TCP Server, Riesenproblem beim Empfang von Daten

Hallo und Grüß Gott,

ich schneie hier herein mit einem (Riesen-) Problem.

Ich komme einfach nicht mehr weiter und hoffe nun hier Hilfe zu bekommen.

Also erstmal ein wenig Code und Erklärung kommen danach:

  
// hier sind die #include  
typedef void (*sighandler_t)(int);  
static sighandler_t  
my_signal(int sig_nr, sighandler_t signalhandler)  
{  
   struct sigaction neu_sig, alt_sig;  
   neu_sig.sa_handler = signalhandler;  
   sigemptyset (&neu_sig.sa_mask);  
   neu_sig.sa_flags = SA_RESTART;  
   if(sigaction (sig_nr, &neu_sig, &alt_sig) < 0) {  
      return SIG_ERR;  
   }  
   return alt_sig.sa_handler;  
}  
  
static void no_zombie (int signr)  
{  
  pid_t pid;  
  int ret;  
  while((pid = waitpid(-1, &ret, WNOHANG)) > 0) {  
   printf ("Child-Server mit pid=%d hat sich beendet\n",pid);  
  }  
  return;  
}  
  
int main (void)  
{  
  int sockfd, connfd, fd, j, n;  
  struct sockaddr_in adresse;  
  const int y = 1;  
  socklen_t adrlaenge = sizeof (struct sockaddr_in);  
  char puffer[1024];  
  pid_t pid;  
  
  if((sockfd = socket (PF_INET, SOCK_STREAM, 0)) < 0) {  
     printf ("Fehler bei socket() ...(%s)\n", strerror(errno));  
     exit (EXIT_FAILURE);  
  }  
  printf ("Socket erfolgreich angelegt\n");  
  adresse.sin_family = AF_INET;  
  adresse.sin_port = htons (PORT_NUMMER);  
  memset (&adresse.sin_addr, 0, sizeof (adresse.sin_addr));  
  setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(int));  
  if(bind ( sockfd, (struct sockaddr *)&adresse, sizeof(adresse))) {  
     printf ("Fehler bei bind() ...(%s)\n", strerror(errno));  
     exit (EXIT_FAILURE);  
  }  
  if(listen (sockfd, 5) ) {  
   printf ("Fehler bei listen() ...(%s)\n", strerror(errno));  
   exit (EXIT_FAILURE);  
  }  
  my_signal (SIGCHLD, no_zombie);  
  printf ("Server ist bereit und wartet ...\n");  
  for(;;) {  
     connfd = accept(sockfd, (struct sockaddr *)&adresse, &adrlaenge);  
     if(connfd < 0) {  
        if(errno == EINTR) {  
         continue;  
  } else {  
         printf("Fehler bei accept() (%s)\n",strerror(errno));  
         exit(EXIT_FAILURE);  
        }  
     }  
     printf ("Connection accepted ...\n");  
  
     if ((pid = fork ()) == 0) {  
        close (sockfd);  
 printf("Children started ... \n");  
   printf("Wait for message ... \n");  
  
        j = 0;  
 // // // // // // // //  
        while ((n = read (connfd, &puffer[j], 1)) > 0) { // Diese 3 Zeilen while(){}-Schleife sind mein Problem!  
         j++;  
        }  
 // // // // // // // //  
  
 puffer[j] = '\0';  
        if(n < 0) {  
         printf ("Fehler bei read() ...\n");  
         exit (EXIT_FAILURE);  
        }  
 printf("Message:\n%s\n", puffer);  
  
 close (fd);  
        exit(EXIT_SUCCESS);  
     }  
     close (connfd);  
  }  
  close (sockfd);  
  exit (EXIT_SUCCESS);  
}  

Das Programm wird bei mir absolut Fehlerfrei erstellt.
Das spielt sich bei mir auf der Konsole ab:

Socket erfolgreich angelegt
Server ist bereit und wartet ...
Connection accepted ...
Children started ...
Wait for message ...
/* [Hier passiert dann gar nichts mehr, das heisst der Client (z.B. Firefox) sendet aber der Server gibt momentan nichts aus.
 Der Client hat fertig übertragen und wartet auf Antwort
 Der Server wartet meines erachtens immer noch auf Nachricht obwohl der Client schon fertig ist
 Beendet man den Client dann gibt der Server das aus was schon lange auf dem Bildschirm zu sehen sein sollte.] */
Message:
GET / HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.8.1.4) Gecko/20070603 Fedora/2.0.0.4-2.fc7 Firefox/2.0.0.4
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cache-Control: max-age=0

Child-Server mit pid=9797 hat sich beendet

Warum ist das so?
Wie kann ich Abhilfe schaffen?
Meine Quelle ist dieses tutorial.
Habe auch schon andere Tutorials ausprobiert aber die ähneln sich eh ziemlich.

Julian Baumann

  1. Eine Alternative Frage wäre, wie dieser Codeteil bei anderen (Http-) Server aussieht.
    Kann mir vielleicht jemand sagen wie die Datei z. B. beim Apache heisst?

    Ich denke soviele (Tutorialersteller und Bücherschreibe) irren nicht so leicht wie einer (ich).
    Also mache ich was Falsch.
    Nur was?

    Ich will doch nur einen simplen TCP-Server schreiben, nichts großartiges dann happert es an sowas/(so einer Kleinigkeit, zwar nicht für mich wie man sieht ...).

    Vielleicht kann mir/will mir/mag mir noch jemand helfen?!
    Wäre echt toll!

    Ich verbleibe mit Freundlichen Grüßen.

    Julian Baumann

  2. Hallo Julian,

    j = 0;
    // // // // // // // //
            while ((n = read (connfd, &puffer[j], 1)) > 0) { // Diese 3 Zeilen while(){}-Schleife sind mein Problem!
             j++;
            }
    // // // // // // // //

    puffer[j] = '\0';
            if(n < 0) {
             printf ("Fehler bei read() ...\n");
             exit (EXIT_FAILURE);
            }
    printf("Message:\n%s\n", puffer);

    read gibt bei Erfolg die Anzahl der gelesenen Zeichen zurück. Ein Wert von 0 wird bei EOF geliefert. Die Schleife wird bei EOF oder Fehler verlassen. Der Fehlerzweig wird aber gemäß Deiner Angaben unten nicht durchlaufen.

    Das Programm wird bei mir absolut Fehlerfrei erstellt.
    Das spielt sich bei mir auf der Konsole ab:

    Socket erfolgreich angelegt
    Server ist bereit und wartet ...
    Connection accepted ...
    Children started ...
    Wait for message ...
    /* [Hier passiert dann gar nichts mehr, das heisst der Client (z.B. Firefox) sendet aber der Server gibt momentan nichts aus.
    Der Client hat fertig übertragen und wartet auf Antwort
    Der Server wartet meines erachtens immer noch auf Nachricht obwohl der Client schon fertig ist

    Die Frage ist, ob der Client an dieser Stelle schon den Socket geschlossen hat. Erst dann wird beim Server EOF signalisiert und die Schleife verlassen.

    Beendet man den Client dann gibt der Server das aus was schon lange auf dem Bildschirm zu sehen sein sollte.] */

    Warum sollte vorher was auf dem Bildschirm stehen? Erst jetzt hat der Sever ein EOF auf dem Socket erhalten und kann mit der Verarbeitung fortfahren.

    Viele Grüße
    Frank

    1. read gibt bei Erfolg die Anzahl der gelesenen Zeichen zurück. Ein Wert von 0 wird bei EOF geliefert. Die Schleife wird bei EOF oder Fehler verlassen. Der Fehlerzweig wird aber gemäß Deiner Angaben unten nicht durchlaufen.

      Nein, der Fehlerzweig wird nicht durchlaufen.
      Aber deswegen ist das Verhalten des Servers trotzdem nicht normal.

      Die Frage ist, ob der Client an dieser Stelle schon den Socket geschlossen hat. Erst dann wird beim Server EOF signalisiert und die Schleife verlassen.

      Der Server verhält sich genauso wenn ich den Client von der selben Seite verwende und ein wenig umschreibe ähnlich wie den Server.

      Warum sollte vorher was auf dem Bildschirm stehen? Erst jetzt hat der Sever ein EOF auf dem Socket erhalten und kann mit der Verarbeitung fortfahren.

      Aber dieses Verhalten ist nicht erwünscht. Wenn der Client beendet wird schliesst er auch sein Socket  und kann somit keine Antwort mehr empfangen. Das EOF vom Client wird meines Erachtens von meinem Server wenn er es denn bekommt nicht beachtet. Weil sonst würde er ja laut manpage die while-Schleife abbrechen und alles wäre i. O..

      Ich bin echt verwirrt. Ich habe schon einige TCP-Anwendungen geschrieben aber sowas ist mir noch nie passiert. Kann mir das auch absolut nicht erklären.

      Julian Baumann

      1. Hallo Julian,

        Nein, der Fehlerzweig wird nicht durchlaufen.

        Sagte ich doch.

        Der Server verhält sich genauso wenn ich den Client von der selben Seite verwende und ein wenig umschreibe ähnlich wie den Server.

        Das verstehe ich jetzt nicht.

        Warum sollte vorher was auf dem Bildschirm stehen? Erst jetzt hat der Sever ein EOF auf dem Socket erhalten und kann mit der Verarbeitung fortfahren.

        Aber dieses Verhalten ist nicht erwünscht.

        Wie sollte es denn sein?

        Wenn der Client beendet wird schliesst er auch sein Socket  und kann somit keine Antwort mehr empfangen.

        In dem Code, den Du gezeigt hast, wird aber auch keine Antwort gesendet.

        Das EOF vom Client wird meines Erachtens von meinem Server wenn er es denn bekommt nicht beachtet. Weil sonst würde er ja laut manpage die while-Schleife abbrechen und alles wäre i. O..

        Die Schleife wird aber gerade wegen EOF verlassen. read() gibt einen Wert größer 0 zurück, wenn Zeichen gelesen wurden, 0 bei EOF und kleiner im Fehlerfall. Die Schleife wird bei <= 0 verlassen, also bei Fehler oder EOF. Der Fehlerzweig wird nicht durchlaufen; also ist EOF aufgetreten.

        Viele Grüße
        Frank