#include #include #include #include #include #include #define SERVER_PORT 7111 #define LEN_RECVBUFFER 1000 int main() { int sfd; int clientsfd; struct sockaddr_in saddr; struct sockaddr clientaddr; socklen_t lenclientaddr; pid_t childpid; ssize_t lenrecv, lensent, len; int optval; /*--- PF_INET waehlt die IP-Protokollfamilie aus. SOCK_STREAM wahlt Byte-Strom-orientierte Kommunikation aus. Das bedeutet zZt automatisch TCP ueber IP ---*/ if ( 0 > ( sfd = socket(PF_INET,SOCK_STREAM,0) ) ) { perror("socket()-call"); exit(1); } /*--- das BS darf die IP/PORT-Adresse sofort wieder vergeben: ---*/ optval = 1; if ( -1 == setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR, &optval,sizeof(optval)) ) { perror("setsockopt()-call"); } /* Server bindet IP-Adresse und Port an den Socket ---*/ bzero((char *)&saddr,sizeof(saddr)); // saddr initialisieren saddr.sin_family = AF_INET; // Protokollfamilie saddr.sin_addr.s_addr = htonl(INADDR_ANY); // trage // IP Adresse des lokalen Rechners ein. //htonl(): "Host to Network LONG" (32-Bit-Worte): // IP und Port-Adressen muessen in der NETWORK // BYTE ORDER (Big Endian Format) angegeben werden. // htons(): Wandlung von 16-Bit Worten in Network // Byte Order. saddr.sin_port = htons(SERVER_PORT); if ( 0 > bind(sfd,(struct sockaddr *)&saddr, sizeof(saddr)) ) { perror("bind()-call"); exit(1); } // Server teilt dem BS mit, dass er nun bereit ist, // auf dem Socket zu hoeren. "5" ist das BACKLOG, dh, die // Anzahl der Verbindungsaufbau-Wuensche, die das BS // intern puffert. if ( 0 > listen(sfd,5) ) { perror("listen()-call"); exit(1); } // Standardverfahren fuer das Entwurfsmuster PARALELL SERVER: // Fuer jeden aktiven Client wird vom Server ein Kindprozess // erzeugt, der diesen Client bedient. while ( 1 ) { lenclientaddr = sizeof(clientaddr); // accept() liefert einen NEUEN Socket Filedescriptor // zurueck, mit dem die Kommunikation zum CLient, // der das zugehoerige connect() ausgeloest hat // abgewickelt wird. // &clientaddr gibt die Adresse des Puffers an, // in die das BS die Adresse des Clients schreiben soll. // Bei lenclientaddr muss VOR dem Aufruf die // maximale Laenge von clientaddr stehen. NACH dem // Aufruf steht in lenclientaddr die tatsaechliche // Laenge der empfangenen Adresse. if ( 0 > (clientsfd = accept(sfd,&clientaddr,&lenclientaddr)) ) { perror("accept()-call"); exit(1); } if ( 0 <= clientsfd ) { // Hier wird ein Kindprozess erzeugt: // fork() legt eine Kopie des laufenden Prozesses // an und aktiviert diese. // Parent-Prozess und Child laufen jeweils // im naechsten Statement nach dem fork() weiter. // Der Elternprozess bekommt in childpid die Prozess // Id des neu erzeugten Kindes ( > 0) mit. Das // Kind erhaelt childpid == 0 als Rueckgabewert. if ( 0 > (childpid = fork()) ) { perror("fork()-call"); exit(1); } if ( !childpid ) { // Dieser if-Zweig wird nur vom // Kindprozess ausgefuehrt, da der Elternprozess // childpid > 0 erhaelt. // Jeder Kindprozess benoetogt einen // eigenen Kommunikationspuffer, // um den Datenaustausch mit seinem CLient // abzuwickeln. char *recvbuffer = calloc(LEN_RECVBUFFER,1); lenrecv = 0; while ( 1 ) { if ( 0 > (lenrecv = read(clientsfd, recvbuffer, LEN_RECVBUFFER)) ) { perror("read"); exit(1); } if ( 0 < lenrecv ) { recvbuffer[lenrecv] = 0; printf("Server received %s\n",recvbuffer); if ( !strncmp(recvbuffer,"STOP",strlen("STOP")) ) { // Kindprozess terminiert: // close() auf den Socket-FD // und free() auf dynamisch allokierten // Kommunikationspuffer. printf("Server-Child exits\n"); close(clientsfd); free(recvbuffer); exit(0); } // wir moechten lenrcv Bytes ueber Socket versenden // Dazu machen wir eine Schleife, bis tatsaechlich // alle Bytes vom BS akzeptiert wurden. // Bei jedem Write() wird die Anfangsadresse des // Sendepuffers auf das ERSTE NOCH NICHT VERSENDETE // Byte gesetzt. lensent = 0; while ( lensent < lenrecv ) { if ( 0 > (len = write(clientsfd, &(recvbuffer[lensent]), lenrecv - lensent)) ) { perror("wire()-call"); exit(1); } lensent = lensent + len; } } } } } } }