/****************************************************************************************************************** * * Questo esercizio poteva essere risolto in piu' modi, e con diverse interpretazioni al testo tutte piu' o meno * corrette. In questa particolare soluzione: * * -alla ricezione del SIGUSR1 si passa dalla porta 8001 alla 8080. Alla ricezione del SIGUSR2 si attiva * anche la porta 8080 in aggiunta alla 8001. Dopo la ricezione di uno qualunque dei due segnali ENTRAMBI verranno * ignorati; * * -i processi figli che ricevono il messaggio ed estraggono l'ultima parola non escono subito * dopo la prima ricevione, ma restano sempre attivi fino a quando non vengono uccisi; * * -ho deciso di non aprire subito entrambe le socket, ma di aprirne una, chiuderla se necessario, aprirne una seconda * se necessario. Si poteva anche aprirne subito due e decidere quale usare con una var globale * * * ATTENZIONE: per la gestione della doppia porta in contemporaneo la cosa piu' ortodossa e' usare la * funzione select. Questa pero' non e' stata vista dagli studenti del corso di Broggi, quindi qui * di seguito e' mostrata una soluzione alternativa che non ne fa uso. * ********************************************************************************************************************/ /*TESTO Si realizzi in ambienteUnix/C il SERVER della seguente interazione tra processi: -il sistema consistye di due tipi di processi: un server Ps e i client Pci; -per la comunicazione tra Ps e i processi Pci vengono utilizzate socket STREAM; -il server Ps offre un servizion concorrente (un figlio per ogni connessione) alle potre 8001 e/o 8080; -il sevrer, che opera inizialmente sulla porta 8001, dopo aver ricevuto un segnale SIGUSR1 attende connessioni sulla porta 8080 (OPZIONALE: la ricezione di un segnale SIGUSR2 fa si che il sevrer attenda su entrambe le porta); N.B. dopo aver ricevuto un segnale SIGUSR1/2, ulteriori segnali SIGUSR1/2 devono essere ignorati; -il clinet invia una stringa contenente parole separate da spazi, ed ottiene in risposta l'ultima parola inviata Deve essere utilizzata la gestione affidabile dei segnali. Si noti che una accept interrota dalla ricezione di un sengale ritorna -1 con errno che vale EINTR. Come generico clinet Pci si suggerisce di utilizzare i programma telnet invocato come "telnet localhost porta". Una volta connessi scrivere, ad esempio, "pippi tololino pluto" per verificare il funzionamento del programma. Telnet invia assieme alla stringa digitata i caratteri di fine linea \r\n. Si rammenta la presenza della funzione char * strrchr(const char *s,int c). Essa ritorna un puntatore all'ultima occorenza del carattere c nella stringa s. */ #include #include #include #include #include #include #include #include #include #include #include #define PORT1 8001 #define PORT2 8080 int sock,msgsock,porta; struct sockaddr_in server; //funzione per aprire la porta 8080 void apri8080() { //ne creo una nuova sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("creazione stream server"); exit(-1); } server.sin_port=htons(PORT2); //imposto la nuova porta, gli altri campi vanno gia' bene if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0) { perror("binding stream socket"); exit(-1); } printf("Server %d on port # %d\n",getpid(),ntohs(server.sin_port)); listen(sock,5); //listen sulla PORT2, massimo di 5 connesioni in attesa } //Funzione di gestione segnali void handler(int signo) { int ps; struct sigaction sig; //ignoro i successivi SIGUSR1 e SIGUSR2 //sigprocmask non funziona dentro l'handler del segnale stesso //si poteva usare una var globale che controllasse se e' la prima volta che ricevo questo segnale sig.sa_handler = SIG_IGN; //per ignorare il segnale (vedi man sigaction) sigemptyset(&sig.sa_mask); sig.sa_flags=0; sigaction(SIGUSR1,&sig,NULL); sigaction(SIGUSR2,&sig,NULL); switch(signo) //che segnale ho riceuvuto? { case SIGUSR1: { //chiudo la vecchia socket close(sock); //apro una socket sulla porta 8080 apri8080(); break; } case SIGUSR2: //(opzionale) { //creo un nuovo figlio, che fungera' da SERVER sull'altra porta if((ps=fork())<0) { perror("fork nuovo server"); exit(-1); } if(ps==0)//CODICE SERVER FIGLIO { //chiudo la vecchia socket che ho ereditato dal server padre close(sock); //apro una socket sulla porta 8080 apri8080(); }//FINE SERVER FIGLIO break; } case SIGINT: //non richiesto { printf("\nProcesso %d esce\n",getpid()); close(sock); close(msgsock); exit(0); } } } int main() { char buffer[256]; char * ultimo; struct sigaction sig; struct sockaddr_in client; int lenght,pid; //creo la SOCKET sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("creazione stream socket"); exit(-1); } //preparo la struttura sockaddr_in per settare i parametri della socket con PORT1 server.sin_family=AF_INET; //internet protocol server.sin_addr.s_addr=INADDR_ANY; //ascolto da tutte le interfacce server.sin_port=htons(PORT1); //e dalla porta specificata if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0) { perror("binding stream socket"); exit(-1); } printf("Server %d on port # %d\n",getpid(),ntohs(server.sin_port)); listen(sock,5); //SEGNALI sig.sa_handler = handler; //installo il gestore del segnale SIGUSR1 sigemptyset(&sig.sa_mask); sigaddset(&sig.sa_mask,SIGUSR2); //blocco SIGUSR2 durante la gestione di SIGUSR1 sig.sa_flags=0; sigaction(SIGUSR1,&sig,NULL); sigemptyset(&sig.sa_mask); sigaddset(&sig.sa_mask,SIGUSR1); //blocco SIGUSR1 durante la gestione di SIGUSR2 sigaction(SIGUSR2,&sig,NULL); sigemptyset(&sig.sa_mask); sigaction(SIGINT,&sig,NULL); //CICLO PRINCIPALE do //attende connesioni, e crea un figlio per ogni una { lenght=sizeof(client); msgsock=accept(sock,(struct sockaddr *)&client,(socklen_t *)&lenght); //attendo connesioni... if(msgsock == -1 && errno!=EINTR) //controllo se c'e' un errore e se questo e' dovuto ad un segnale, nel qual caso lo ignoro { perror("accept"); //se invece l'errore non dipende da un segnale c'e' qualcosa che non va ed esco exit(-1); } if(errno!=EINTR) //se sono uscito dalla accept per un segnale allora non devo generare nessun figlio gestore... { if((pid=fork())<0) //genero il figlio che gestira' la connesione attuale { perror("fork gestore"); exit(-1); } else if(pid==0) { //CODICE FIGLIO GESTORE DELLA CONNESIONE close(sock); printf("Figlio gestore della connesione su porta %d, utilzza porta %d per comunicazione, pid %d\n",ntohs(server.sin_port),ntohs(client.sin_port),getpid()); do { memset(buffer,0,256);//azzero il buffer di lettura if(read(msgsock,buffer,256)<0) //leggo il messaggio perror("reading message"); else { printf("Figlio %d ha ricevuto %s\n",getpid(),buffer); //estraggo il puntatore all'ultimo spazio ultimo=strrchr(buffer,' '); if(ultimo) ultimo++; //in questo modo punto al primo carattere dopo lo spazio else ultimo=buffer; //se buffer non contiene spazi, allora e' costituito da una sola parola, e rimando tutto buffer if(write(msgsock,ultimo,strlen(ultimo))<0) //speidsco l'ultima parola perror("writing message"); } } while(1); //FINE CODICE FIGLIO GESTORE } } //CODICE PADRE close(msgsock); } while(1); return 0; }