Select e Segnali

Questo codice propone un Server che predispone due socket (porta 35000, porta 35001) sulle quali offre rispettivamente il messaggio di risposta  <ciao>  ed il messaggio di risposta <bye bye>. Queste due socket vengono assegnate alla maschera della select dopo che il server è stato predisposto per ricevere il segnale SIGUSR1(#16) agganciandolo ad un handler che non fa altro che visualizzare un messaggio che notifichi la ricezione del segnale.


/*
   server
          */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/file.h>
#include <netdb.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#define PORTA_IT   35000
#define PORTA_USA  35001
extern int make_socket(struct sockaddr_in*,int*,ushort,char,FILE*);
extern int connections(struct sockaddr_in*,int*,char*,ushort,char,FILE*);
extern void clear_screen(void);
extern int errno;
void segnale();
void reply_it(void);
void reply_usa(void);
struct hostent *hp;
struct sockaddr_in claddr_it,    /* socket d' ascolto per i clients I */
                   claddr_usa;    /* socket d' ascolto per i clients USA */
int sock_it,sock_usa,lenght,len=sizeof(struct sockaddr_in);
char buf[10],*host;
main()
{
fd_set read_mask,temp_mask;
sigset(SIGUSR1,segnale);
/*assegna la struttura d'indirizzo per:
  - la sock_DG per le richieste di servizio da parte dei CLIENTS I.
  - la sock_DG per le richieste di servizio da parte dei CLIENTS USA.*/
if(make_socket(&claddr_it,&sock_it,PORTA_IT,'D',stderr))
  {fprintf(stderr,"fallita la creazione della socket dei clients italiani\n");
   exit();}
if(make_socket(&claddr_usa,&sock_usa,PORTA_USA,'D',stderr))
  {fprintf(stderr,"fallita la creazione della socket dei clients amerrecani\n");
   exit;}
FD_ZERO(&read_mask);
FD_SET(sock_it,&read_mask);
FD_SET(sock_usa,&read_mask);
temp_mask=read_mask;
printf("\n\nSono il processo %d.
Inviami un SIGUSR1 ogni tanto per vedere come si comporta la SELECT 
quando il processo che la esegue riceve un segale.\n",getpid());
for(;;)
  {
  printf("\n\tSono sospeso sulla SELECT\n");
  select(sock_usa+1,&read_mask,NULL,NULL,NULL);
  if(FD_ISSET(sock_usa,&read_mask)) reply_usa();
  if(FD_ISSET(sock_it,&read_mask)) reply_it();
  read_mask=temp_mask;
  } 
}
void reply_it()
{
struct sockaddr_in peeraddr;
printf("\n\nRicevuta una richiesta da un client I!\n");
if(recvfrom(sock_it,buf,sizeof(buf),0,(struct sockaddr *)&peeraddr,&len)<0)
   {fprintf(stderr,"fallita la ricezione del client I\n");return;}
   
lenght=ntohl(peeraddr.sin_addr.s_addr);
hp=gethostbyaddr((char*)&lenght,sizeof(struct in_addr),peeraddr.sin_family);
if(hp==NULL)
  host=(char *)inet_ntoa(peeraddr.sin_addr);
else
  host=hp->h_name;
fprintf(stderr,"Richiesta di connessione  dal nodo %s ;porta %u \n\n",
	host,ntohs(peeraddr.sin_port));
sprintf(buf,"ciao");  
if(sendto(sock_it,buf,sizeof(buf),0,(struct sockaddr *)&peeraddr,len)<0)
  {fprintf(stderr,"fallito l'invio del b_cast alla ricerca di un NS\n");
    return;}
}
void reply_usa()
{
struct sockaddr_in peeraddr;
printf("\nRicevuto una richiesta da un client USA!\n");
if(recvfrom(sock_usa,buf,sizeof(buf),0,(struct sockaddr *)&peeraddr,&len)<0)
   {fprintf(stderr,"fallita la ricezione del client USA\n");return;}
   
lenght=ntohl(peeraddr.sin_addr.s_addr);
hp=gethostbyaddr((char*)&lenght,sizeof(struct in_addr),peeraddr.sin_family);
if(hp==NULL)
  host=(char *)inet_ntoa(peeraddr.sin_addr);
else
  host=hp->h_name;
fprintf(stderr,"Richiesta di connessione  dal nodo %s ;porta %u \n\n",
	host,ntohs(peeraddr.sin_port));
sprintf(buf,"bye bye");  
if(sendto(sock_usa,buf,sizeof(buf),0,(struct sockaddr *)&peeraddr,len)<0)
  {fprintf(stderr,"fallito l'invio del b_cast alla ricerca di un NS\n");
    exit();}
}
void segnale()
{
printf("Ricevuto il segnale utente\n");
}

/*
   client italiano 
                   */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#define PORTA_IT 35000
extern int make_socket(struct sockaddr_in*,int*,ushort,char,FILE*);
extern int connections(struct sockaddr_in*,int*,char*,ushort,char,FILE*);
extern void clear_screen(void);
extern int errno;
main(argc,argv)
int argc;
char* argv[];
{
int sock,len=sizeof(struct sockaddr_in);
char buf[10],answer;
struct sockaddr_in nsaddr;
if(connection(&nsaddr,&sock,argv[1],PORTA_IT,'D',stderr))
   {fprintf(stderr,"errore nella creazione della connessione\n");exit(0);}
do
 {
  if(sendto(sock,buf,sizeof(buf),0,(struct sockaddr *)&nsaddr,len)<0)
    {fprintf(stderr,"fallito l'invio dela richiesta\n");
      exit(0);}
    
  if(recvfrom(sock,buf,sizeof(buf),0,(struct sockaddr *)&nsaddr,&len)<0)
     {fprintf(stderr,"fallita la ricezione del messaggio\n");
      exit(0);}    
    
  printf("\nIl msg ricevuto e' %s\n",buf);
  printf("Vuoi continuare s/n ?");answer=getchar();getchar();
 }
while((answer=='s')||(answer=='S'));
}

/*
   client americano
                     */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#define PORTA_USA 35001
extern int make_socket(struct sockaddr_in*,int*,ushort,char,FILE*);
extern int connections(struct sockaddr_in*,int*,char*,ushort,char,FILE*);
extern void clear_screen(void);
extern int errno;
main(argc,argv)
int argc;
char* argv[];
{
int sock,len=sizeof(struct sockaddr_in);
char buf[10],answer;
struct sockaddr_in nsaddr;
if(connection(&nsaddr,&sock,argv[1],PORTA_USA,'D',stderr))
   {fprintf(stderr,"errore nella creazione della connessione\n");exit(0);}
do
 {
  if(sendto(sock,buf,sizeof(buf),0,(struct sockaddr *)&nsaddr,len)<0)
    {fprintf(stderr,"fallito l'invio dela richiesta\n");
      exit(0);}
    
  if(recvfrom(sock,buf,sizeof(buf),0,(struct sockaddr *)&nsaddr,&len)<0)
     {fprintf(stderr,"fallita la ricezione del messaggio\n");
      exit(0);}    
    
  printf("\nIl msg ricevuto e' %s\n",buf);
  printf("Vuoi continuare s/n ?");answer=getchar();getchar();
 }
while((answer=='s')||(answer=='S'));
}

La ricezione del segnale provoca sul server un duplice effetto:

    - l' interruzione dell' esecuzione del codice per eseguire l' handler associato al segnale (e fin qui niente di nuovo...)

    - il settaggio di tutti i bit della maschera della select associati alle corrispondenti sockets !?!

Conseguentemente, in questo codice dopo la gestione del segnale, il server passa ad eseguire la funzione reply_usa() sospendendosi indefinitamente in attesa di un messaggio sulla socket sock_usa. A questo punto il codice funziona correttamente solamente nel caso in cui ,successivamente al segnale, pervengono al server due richieste la PRIMA da un client americano e la SECONDA da un client  italiano. Se arriva PRIMA una richiesta da un client italiano il server non può gestirla finchè non sopraggiunge una richiesta usa; non solo ma se dopo la prima richiesta usa, che viene correttamente gestita, arriva un' altra richiesta usa, il server non può gestirla finchè non è sopraggiunta una richiesta italiana.

Questo comportamento è indipendente dal tipo di socket utilizzate (D_GRAM nell' esempio) in quanto il segnale va a modificare lo stato della maschera indipendentemente da ciò che è stato assegnato alla maschera.