Mette a disposizione dei Cs il servizio telefonico. I Cs si connettono dopo aver ottenuto l' in- dirizzo di rete (host-porta) dal NS caldo. Ciò presuppone una fase iniziale di registrazione al NS caldo che avviene utilizzando il Bcast. In risposta al Bcast il ST ottiene la porta sulla quale in NS caldo accetta le richieste di registrazione.
Descrizione sommaria del comportamento:
Codice.
/* codice del server telefonico. Inizialmente il st tenta di registrarsi al NS. In corrispondenza di una registrazione avvenuta il st si mette inservizio. Il programma invia un messaggio al NS contenente i seguenti campi:
<porta_cl> <porta_ud> <porta_res> 10 10 10 caratteri(compreso '\0')
Il programma viene lanciato con i seguenti argomenti:
nome_progr <database> argv[0] argv[1] */
#include <pthread.h> #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 BUFLEN 60 #define BUFFERLEN 20 #define PERM 0700 #define PORTA_CL "35000" #define PORTA_UD "35001" #define PORTA_RES "35002" #define PORTA_BCAST_NS 31000 /*porta Bcast di ricerca dei NS attivi*/
extern void demone(void); 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* keepalive_funct(void *); void* match_thread(void *); void match(void); void* updata_thread(void *); void updata(void); void resume(void); void lock_file(char *); void unlock_file(char *); void allarme(); void fine();
struct hostent *hp; struct sockaddr_in claddr, /* socket d' ascolto per i clients */ udaddr, /* socket d' ascolto per l'updata */ nsaddr, /* e' la socket di com. col NS */ baddr, /*socket b_cast datagram*/ peeraddr, /*socket peer datagram*/ resaddr; /*socket di resume*/ typedef struct { unsigned short stato; unsigned short porta_ka; } MSG_PROBE;
typedef struct { unsigned short porta_dg_cl; unsigned short porta_ls_st; } MSG_NS;
MSG_PROBE*ptr_probe; MSG_NS*ptr_ns;
int s,ls_cl,ls_ud,ls_res,udsock,bsock,lenght,len=sizeof(struct sockaddr_in); char linkfile[20],command[30],database[10],buffer[BUFFERLEN],*host_NS;
FILE* fddbg;
pthread_t tid_ka;
main(argc,argv) int argc; char* argv[]; { struct sockaddr_in myaddr; /*e' la socket del ST */ fd_set read_mask,temp_mask; char buf[BUFLEN],host[24]; int sd,i;
signal(SIGALRM,allarme); sigset(SIGUSR1,SIG_IGN);
ptr_probe=(MSG_PROBE*)malloc(sizeof(MSG_PROBE)); memset((MSG_PROBE*)ptr_probe,0,sizeof(MSG_PROBE)); memset(host,0,sizeof(host)); gethostname(host,sizeof(host));
ptr_ns=(MSG_NS*)malloc(sizeof(MSG_NS)); memset((MSG_NS*)ptr_ns,0,sizeof(MSG_NS));
if(argc<2) {fprintf(stderr,"Uso: %s <db> \n",argv[0]); exit();}
clear_screen(); fprintf(stderr,"ST su %s: porta di servizio %s; porta di UD %s;\n\n",host, PORTA_CL,PORTA_UD);
fprintf(stderr,"attendere prego...");sleep(1);
/*invia un b_cast a significato implicito per ricercare nella rete un NS*/ if(connection(&baddr,&bsock,0,PORTA_BCAST_NS,'B',stderr)) {fprintf(stderr,"errore nella creazione della socket B_CAST\n"); exit();} if(sendto(bsock,buffer,BUFFERLEN,0,(struct sockaddr *)&baddr,len)<0) {fprintf(stderr,"fallito l'invio del b_cast alla ricerca di un NS\n"); exit();}
alarm(5);
if(recvfrom(bsock,ptr_ns,sizeof(MSG_NS),0,(struct sockaddr *)&peeraddr,&len)<0) {fprintf(stderr,"fallita la ricezione del b_cast alla ricerca di un NS\n"); exit();}
close(bsock); alarm(0);
lenght=ntohl(peeraddr.sin_addr.s_addr); hp=gethostbyaddr((char*)&lenght,sizeof(struct in_addr),peeraddr.sin_family); if(hp==NULL) host_NS=(char *)inet_ntoa(peeraddr.sin_addr); else host_NS=hp->h_name;
fprintf(stderr,"Le informazioni pervenute dal NS sono le seguenti:\n\t\t\ host del NS: %s\n\t\tporta_dg_cl del NS: %d\n\t\tporta_ls_st del NS: %d\n\n", host_NS,(*ptr_ns).porta_dg_cl,(*ptr_ns).porta_ls_st);
/*assegna la struttura d'indirizzo per la sock_ST di connessione col NS*/ if(connection(&nsaddr,&s,host_NS,(*ptr_ns).porta_ls_st,'S',stderr)) {fprintf(stderr,"errore nella creazione della connessione\n"); exit();} if(getsockname(s,(struct sockaddr *)&myaddr,&len)==-1) {fprintf(stderr,"%s: getsockname\n",strerror(errno));exit();} fprintf(stderr,"Connessione a %s sulla porta %u \n",host_NS, ntohs(myaddr.sin_port));
/*invia il messaggio di registazione ed attende una risposta*/ memset(buf,0,BUFLEN); sprintf(buf,"%s %s %s",PORTA_CL,PORTA_UD,PORTA_RES); if(send(s,buf,BUFLEN,0)!=BUFLEN) {fprintf(stderr,"%s: send di registrazione\n",strerror(errno));exit();}
if(recv(s,ptr_probe,sizeof(MSG_PROBE),0)!=sizeof(MSG_PROBE)) {fprintf(stderr,"%s: Errore di ricezione dal NS\n",strerror(errno)); exit();} fprintf(stderr,"Il messaggio ricevuto e' :\n\t\taccettazione/~rifiuto %d\n\ \t\tporta_ka di ascolto del NS %d\n\n",ptr_probe->stato,ptr_probe->porta_ka);
/*nel caso di risposta negativa il ST termina*/ if(!ptr_probe->stato) { fprintf(stderr,"Non e' possibile registrarsi:\n\t\tChiusura della \ connessione a %s \n\t\tsulla porta %u e ...terminazione\n\n\n",host_NS, ntohs(myaddr.sin_port)); free(ptr_probe);free(ptr_ns);exit(); }
demone();
memset(command,0,30); memset(database,0,10); strcpy(database,argv[1]);
sprintf(command,"st%sdebug",argv[1]); fddbg=fopen(command,"w+");
/*crea il Thread a cui demanda la funzionalità di KeepAlive*/ pthread_create(&tid_ka,NULL,&keepalive_funct,NULL);
/*assegna la struttura d'indirizzo per: - la sock_ST di listen per le richieste di servizio da parte dei CLIENTS. - la sock_ST di listen per le richieste di aggiornamento. - la sock_ST di listen per il "resume" del keepalive.*/
if(make_socket(&claddr,&ls_cl,atoi(PORTA_CL),'S',fddbg)) {fprintf(fddbg,"fallita la creazione della socket di listen dei clients\n"); exit();} if(make_socket(&udaddr,&ls_ud,atoi(PORTA_UD),'S',fddbg)) {fprintf(fddbg,"fallita la creazione della socket di listen di updata\n"); exit;}
if(make_socket(&resaddr,&ls_res,atoi(PORTA_RES),'S',fddbg)) {fprintf(fddbg,"fallita la creazione della socket di listen di updata\n"); exit;}
FD_ZERO(&read_mask); FD_SET(ls_cl,&read_mask); FD_SET(ls_ud,&read_mask); FD_SET(ls_res,&read_mask); temp_mask=read_mask;
/*ciclo infinito nel quale il ST si sospende in modo BLOCCANTE in attesa di richieste di aggiornamento da parte del processo TERMINALE o di servizio da parte dei CLIENTS.*/ for(;;) { select(ls_res+1,&read_mask,NULL,NULL,NULL); if(FD_ISSET(ls_cl,&read_mask)) match(); if(FD_ISSET(ls_ud,&read_mask)) updata(); if(FD_ISSET(ls_res,&read_mask)) resume(); read_mask=temp_mask; } }
/***************************************************************************/ /* allarme(): codice eseguito dal ST */ /***************************************************************************/ void allarme() { fprintf(stderr,"\n\nNessun NS attivo !!! Non è possibile alcuna \ registrazione.\n\n"); close(bsock);free(ptr_ns); exit(0); }
/***************************************************************************/ /* match(): codice eseguito dal ST */ /***************************************************************************/ void match() { int s; pthread_t tid;
if((s=accept(ls_cl,(struct sockaddr *)&claddr,&len))==-1) {fprintf(fddbg,"%s: accept client\n",strerror(errno));fflush(fddbg); return;} pthread_create(&tid,NULL,&match_thread,(void *)s); }
/***************************************************************************/ /* match_thread(): codice eseguito dal thread */ /***************************************************************************/ /*in questa funzione il Thread_di_Match si occupa della gestione delle richieste di match da parte dei clients.*/
void* match_thread(void *ptr_sock) { typedef struct{ char utente[20]; char telefono[20]; }RECORD; RECORD* prec;
/*mes_out è il messaggio di risposta che il ST invia al CLIENT contenente eventualmente più di un n° telefonico*/ typedef char TELEFONO[20]; typedef struct{ int len; TELEFONO data[10]; }MESSAGGIO; MESSAGGIO mes_out;
struct hostent *hp; char *host_CL; int lenght,fdf,l,i,count; char buffer[BUFFERLEN];
pthread_detach(pthread_self());
memset(buffer,0,BUFFERLEN); prec=(RECORD*)malloc(sizeof(RECORD)); memset(prec,0,sizeof(RECORD));
/*ricerca informazione sul CLIENT che si è connesso*/ lenght=ntohl(claddr.sin_addr.s_addr); hp=gethostbyaddr((char*)&lenght,sizeof(struct in_addr),claddr.sin_family); if(hp==NULL) host_CL=(char *)inet_ntoa(claddr.sin_addr); else host_CL=hp->h_name; fprintf(fddbg,"\n\nRichiesta di connessione dal nodo %s ;porta %u \n\n", host_CL,ntohs(claddr.sin_port));fflush(fddbg);
/*il Thread mantiene la connessione finchè il CLIENT non la chiude*/ while(recv((int)ptr_sock,buffer,BUFFERLEN,0)) { count=0; fprintf(fddbg,"Messaggio ricevuto dal client: %s\n",buffer);fflush(fddbg); if((fdf=open(database,O_RDONLY))<0) { fprintf(fddbg,"%s: open file\n",strerror(errno)); fprintf(fddbg,"Attenzione: impossibile aprire il dB\n"); fflush(fddbg); } lock_file(database); lseek(fdf,0,0); while(read(fdf,prec,sizeof(RECORD))>0) { if(strcmp(buffer,prec->utente)==0) strcpy(mes_out.data[count++],prec->telefono); memset(prec,0,sizeof(RECORD)); } close(fdf); unlock_file(database); mes_out.len=count; l=sizeof(int)+sizeof(TELEFONO[mes_out.len]); if(count) { fprintf(fddbg,"l'utente %s ha i seguenti numeri:\n",buffer); for(i=0;i<count;i++) fprintf(fddbg,"\t%s\n",mes_out.data[i]); } else fprintf(fddbg,"utente non presente nel db \n");
if((send((int)ptr_sock,&mes_out,l,0))!=l) {fprintf(fddbg,"%s: send\n",strerror(errno));fflush(fddbg);} else {fprintf(fddbg,"Inviata la risposta al nodo %s ;porta %u \n\n", host_CL,ntohs(claddr.sin_port));fflush(fddbg);} }
/*terminata la connessione il Thread termina*/ fprintf(fddbg,"THREAD DI MATCH: chiudo le socket e termino\n\n"); fflush(fddbg); close((int)ptr_sock);free(prec); pthread_exit(NULL); }
/***************************************************************************/ /* updata(): codice eseguito dal ST */ /***************************************************************************/ void updata() { int s; pthread_t tid;
if((s=accept(ls_ud,(struct sockaddr *)&udaddr,&len))==-1) {fprintf(fddbg,"%s: accept updata\n",strerror(errno)); fflush(fddbg);return;} pthread_create(&tid,NULL,&updata_thread,(void *)s); }
/***************************************************************************/ /* updata_thread(): codice eseguito dal thread */ /***************************************************************************/
void* updata_thread(void *ptr_sock) { typedef struct { char utente[20]; char telefono[20]; } RECORD; RECORD record,recordtemp; RECORD *prec=&record,*prectemp=&recordtemp;
typedef struct { int len; RECORD data[50]; } MESSAGGIO; MESSAGGIO mes_out; typedef struct { int len; RECORD (*ptr_data)[]; } MES; MES mes_in; FILE* fp; int s,i,fdf,fdftemp,flag=1,count=0,l; char dbtemp[20];
pthread_detach(pthread_self()); if(recv((int)ptr_sock,&mes_in,sizeof(int),0)<1) {fprintf(fddbg,"%s: recv di updata\n",strerror(errno));fflush(fddbg); pthread_exit(NULL);} if(!mes_in.len) {close((int *)ptr_sock);pthread_exit(NULL);}
/*controllo sulla natura dell'aggiornamento: INSERIMENTO / ELIMINAZIONE*/ if(mes_in.len>0) {fprintf(fddbg,"\tricevute %d entries da inserire nel db %s\n", mes_in.len,database);fflush(fddbg);} else {fprintf(fddbg,"\tricevute %d entries da eliminare nel db %s\n", -mes_in.len,database);fflush(fddbg);}
mes_in.ptr_data=(RECORD(*)[])malloc(sizeof(RECORD[abs(mes_in.len)]));
if((i=recv((int)ptr_sock,mes_in.ptr_data,\ sizeof(RECORD[abs(mes_in.len)]),0))<0) {fprintf(fddbg,"%s: errore di ricezione\n",strerror(errno)); fflush(fddbg);pthread_exit(NULL);} fprintf(fddbg,"\ti caratteri letti sono %d\n",i); fprintf(fddbg,"\tricevute le seguenti utenze:\n");
/*ciclo di verifica del messaggio ricevuto.Viene visualizzato il campo telefo- no anche in caso di eliminazione in quanto in questo caso tale campo è annul- lato dal processo TERMINALE prima dell'inviodel messaggio*/ for(i=0;i<abs(mes_in.len);i++) fprintf(fddbg,"\t%s %s\n",(*mes_in.ptr_data)[i].utente, (*mes_in.ptr_data)[i].telefono); fflush(fddbg); fdf=open(database,O_RDWR); lock_file(database);
if(mes_in.len>0)/*inserimento nuove entries*/ { fprintf(fddbg,"\tInserimento nuove entries\n\n"); lseek(fdf,0,2); for(i=0;i<mes_in.len;i++) { memset(prec,0,sizeof(RECORD)); strcpy(record.utente,(*mes_in.ptr_data)[i].utente); strcat(record.utente,"\0"); strcpy(record.telefono,(*mes_in.ptr_data)[i].telefono); strcat(record.telefono,"\0"); write(fdf,prec,sizeof(RECORD)); strcpy(mes_out.data[count].utente,prec->utente); strcpy(mes_out.data[count++].telefono,prec->telefono); } close(fdf); } else/*eliminazione vecchie entries*/ { fprintf(fddbg,"\tEliminazione vecchie entries\n\n"); lseek(fdf,0,0); memset(dbtemp,0,20); sprintf(dbtemp,"%stemp",database); fdftemp=creat(dbtemp,PERM); memset(prec,0,sizeof(RECORD)); /*ogni entries del dB viene controllata co l'elenco delle entries da eliminare*/ while((read(fdf,prec,sizeof(RECORD))>0)) { for(i=0;i<abs(mes_in.len);i++) { memset(prectemp,0,sizeof(RECORD)); strcpy(recordtemp.utente,(*mes_in.ptr_data)[i].utente); strcat(recordtemp.utente,"\0"); if(strcmp(recordtemp.utente,prec->utente)==0) { flag=0; strcpy(mes_out.data[count].utente,prec->utente); strcpy(mes_out.data[count++].telefono,prec->telefono); break; } } /*flag individua le entries che DEVONO rimanere nel dB*/ if(flag) write(fdftemp,prec,sizeof(RECORD)); memset(prec,0,sizeof(RECORD));flag=1; } close(fdftemp);close(fdf); memset(command,0,30); sprintf(command,"mv %s %s",dbtemp,database); if((fp=popen(command,"r"))==NULL) {fprintf(fddbg,"\t%s: popen\n",strerror(errno));fflush(fddbg);} }
unlock_file(database); free(mes_in.ptr_data);
/*terminato l'aggionamento il Thread invia un messaggio al processo TERMINALE che renda note le operazioni effettuate*/ mes_out.len=count;l=sizeof(int)+sizeof(RECORD[mes_out.len]); if(send((int)ptr_sock,&mes_out,l,0)!=l) {fprintf(fddbg,"%s: send\n",strerror(errno));fflush(fddbg);} close((int)ptr_sock); pthread_exit(NULL); }
/****************************************************************************/ /* funzioni che lockano e unlockano il dB */ /****************************************************************************/ void lock_file(char *db) {
memset(linkfile,0,20); sprintf(linkfile,"%slockato",db); while(link(db,linkfile)<0) { if(errno!=EEXIST) {fprintf(fddbg,"%s: errore di link\n",strerror(errno));fflush(fddbg);} else {fprintf(fddbg,"%s: file %s è lockato. Sono in attesa che si liberi...\n", strerror(errno),db);fflush(fddbg);} sleep(1); } }
/****************/
void unlock_file(char *db) { if(unlink(linkfile)<0) {fprintf(fddbg,"%s: ATTENZIONE unlock di %s fallito\n",strerror(errno),db); fflush(fddbg);} }
/***************************************************************************/ /* keepalive_funct(): codice eseguito dal thread */ /***************************************************************************/ void *keepalive_funct(void *ptr) { struct sockaddr_in keepaddr; char bufkeep[10]; int skeep; pthread_detach(pthread_self()); signal(SIGUSR1,fine);
/*invia al NS un ack col quale notifica la sua operativita'*/ if(send(s,buffer,BUFFERLEN,0)!=BUFFERLEN) {fprintf(fddbg,"%s: Errore di trasmissione dell'ack al NS\n", strerror(errno)); fflush(fddbg);close(s);pthread_exit(NULL);} close(s);
/*si connette alla socket D_GRAM di KeepAlive predisposta dal NS*/ if(connection(&keepaddr,&skeep,host_NS,(*ptr_probe).porta_ka,'D',fddbg)) {fprintf(fddbg,"fallita la creazione della socket di KA\n"); fflush(fddbg);pthread_exit(NULL);}
fprintf(fddbg,"l' host al quale invio i PROBEs è %s\n",host_NS); fprintf(fddbg,"la socket di KA alla quale invio PROBEs è %d\n", (*ptr_probe).porta_ka); for(;;) { if(sendto(skeep,bufkeep,10,0,(struct sockaddr *)&keepaddr,len)!=10) {fprintf(fddbg,"errore di trasmissione di PROBE\n");fflush(fddbg);} sleep(5); }
return(NULL); } void fine() {pthread_exit(NULL);} /***************************************************************************/ /* resume(): codice eseguito dal ST */ /***************************************************************************/ void resume() { fprintf(fddbg,"\n\n\tSono all'interno della funzione di resume.\n"); if((s=accept(ls_res,(struct sockaddr *)&resaddr,&len))==-1) {fprintf(fddbg,"%s: accept resume\n",strerror(errno)); fflush(fddbg);return;}
lenght=ntohl(resaddr.sin_addr.s_addr); hp=gethostbyaddr((char*)&lenght,sizeof(struct in_addr),resaddr.sin_family); if(hp==NULL) host_NS=(char *)inet_ntoa(peeraddr.sin_addr); else host_NS=hp->h_name;
fprintf(fddbg,"\tRicevuta una richiesta di resume.\n"); memset((MSG_PROBE*)ptr_probe,0,sizeof(MSG_PROBE));
if(recv(s,ptr_probe,sizeof(MSG_PROBE),0)!=sizeof(MSG_PROBE)) {fprintf(fddbg,"%s: Errore di ricezione dal NS\n",strerror(errno)); return;}
pthread_kill(tid_ka,SIGUSR1); pthread_create(&tid_ka,NULL,&keepalive_funct,NULL); }