2016-12-01 6 views
0

これは、fork()を使用してマルチホームファイルサーバ(種類)を実現しようとする最初の試みです。意図は、 'create delete open close write read seek -filetarget ...'の形式で操作を送信する複数のホストを処理することです(例えば、create -hello.c write -hello.c delete -hello.c)。マルチホームTCPサーバとクライアントがループに詰まった

サーバ

#include<stdio.h> 
#include<stdlib.h> 
#include<errno.h> 
#include<unistd.h> 
#include<string.h> 
#include<fcntl.h> 
#include<ctype.h> 
#include<netdb.h> 
#include<sys/types.h> 
#include<sys/stat.h> 
#include<netinet/in.h> 
#include<sys/socket.h> 

#define BACKLOG 10 

extern int inet_ntoa(); 
extern int inet_pton(); 

int master(int, int); 
int control(char []); 
int execute(int, int, char [], char [], char[], int); 

int main(int argc, char *argv[]){ 

int server, accepted, porta, nuovo; 
struct sockaddr_in listener, client; 
socklen_t len;  

if(argc!=2){ //CONTROLLO PARAMETRI 
    printf("Errore nei parametri.\n"); 
    return -1; 
}else porta = atoi(argv[1]); //CONVERSIONE NUMERO DI PORTA 

if((server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))<0){ //CREAZIONE DELLA SOCKET 
    perror("Errore nella creazione della socket."); 
    return -1; 
} 

memset(&listener, 0, sizeof(listener)); //SETTAGGIO ATTRIBUTI LISTENER 
client.sin_family = AF_INET; 
client.sin_addr.s_addr = htonl(INADDR_ANY); 
listener.sin_port = htons(porta); 

if(bind(server, (struct sockaddr *)&listener, sizeof(listener)) < 0){ //BINDING SERVER 
    perror("Errore binding!"); 
    return -1; 
} 

if(listen(server, BACKLOG) < 0){ //LISTENING 
    perror("Errore listening!\n"); 
    return -1; 
} 

printf("Socket inizializzata con successo..\n"); 
sleep(2); 
system("clear"); 

while(1){ 

    printf("FATHER: *** in attesa ***\n"); 
    len = sizeof(client); 
    accepted = accept(server, (struct sockaddr *)&client, &len); //ACCETTO NUOVA CONNESIONE SU ACCEPTED 
    if(accepted < 0){ 
     perror("Errore nella accept!"); 
     return -1; 
    } 
    printf("FATHER: *** connessione stabilita con il client %d ***\n", inet_ntoa(client.sin_addr)); 

    nuovo = fork(); //FORK() 
    if(nuovo == 0){  //FIGLIO 
     master(accepted, server);  
    }else if(nuovo < 0){ 
     perror("Errore fork!"); 
     exit(-1); 
    }else close(accepted); 
} 

return 0; 

} 
int master(int accepted, int server){ 

     int fd, i, k, j, flag; 
     char richiesta[256], operazione[256], result[256], file[256], file_opened[256]; 

     printf("Figlio\n"); 
     close(server); //CHIUDO SERVER CHE HO EREDITATO E NON MI SERVE 
     recv(accepted, richiesta, sizeof(richiesta), 0); //RICEVO RICHIESTA 
     //printf("Richiesta -> %s", richiesta); 
     if(strcmp(richiesta,"exit") == 0){ //SE RICHIESTA DI USCITA, ESCO 
      close(accepted); 
      exit(0); 
     } 

     fd = -1; //AZZERO GLI INDICI E PONGO IN STATO DI ERRORE fd 
     j = 0; 
     k = 0; 
     i = 0; 
     while(i < strlen(richiesta)){  //FINCHÈ LA RICHIESTA NON È STATA ESAMINATA PER INTERO 
      while(richiesta[i] != '-'){   //FINCHÈ NON INCONTRO UN CARATTERE "-" 
       operazione[j] = richiesta[i]; //COPIO OGNI LETTERA DI RICHIESTA IN OPERAZIONE 
       j++;        
       i++; 
      } 
      operazione[strlen(operazione) - 1] = '\0'; //TERMINO LA STRINGA CON '\0' 
      i = i+1;         //AVANZO DI UNO SUPPONENDO DI TROVARMI SU UNO SPAZIO 
      while(richiesta[i] != ' '){     //FINCHÈ NON TROVO UN ALTRO SPAZIO 
       file[k] = richiesta[i];     //COPIO OGNI LETTERE DI RICHIESTA IN FILE 
       i++; 
       k++; 
      } 
      if(!isalpha(file[strlen(file) - 1]))file[strlen(file) - 1] = '\0'; //TERMINO LA STRINGA CON '\0' 
      flag = control(operazione);           //CONTROL VERIFICA LA VALIDITÀ   
      if(flag == -1) strcpy(result,"Errore nella richiesta!\n\0");  //SE ERRORE, RESULT CONTERRÀ IL MESSAGGIO DI ERRORE 
      else execute(flag, fd, result, file, file_opened, accepted); //ALTRIMENTI SI PROCEDE CON L'ESECUZIONE DI QUANTO CHIESTO 
      send(accepted, result, sizeof(result), 0);  //SENDO IL RISULTATO  
      memset(result, '\0', sizeof(result));     //AZZERO LE STRINGHE ED I CONTATORI UTILIZZATE 
      memset(file, '\0', sizeof(file)); 
      memset(operazione, '\0', sizeof(operazione)); 
      j = 0; 
      k = 0; 
     } 
     send(accepted, "end", sizeof("end"), 0); //NOTIFICO LA FINE DELL'ESECUZIONE E CHIUDO 
     close(accepted); 
     printf("Fine figlio\n"); 
     exit(0); 
} 

int control(char operazione[]){ 

    if((strcmp(operazione,"write"))==0) return 1; 
    else if((strcmp(operazione,"read"))==0) return 2; 
    else if((strcmp(operazione,"seek"))==0) return 3; 
    else if((strcmp(operazione,"open"))==0) return 4; 
    else if((strcmp(operazione,"close"))==0) return 5; 
    else if((strcmp(operazione,"delete"))==0) return 6; 
    else if((strcmp(operazione,"create"))==0) return 7; 
    else return -1; 

} 

int execute(int flag, int fd, char result[], char file[], char file_opened[], int client_descriptor){ 

char testo[8192], off[5]; 
int offset; 
char operation[3][6] = {"read\0", "write\0", "seek\0"}; 
char noop[] = "noop"; 

if(fd != -1){ 
    if(strcmp(file_opened, file) != 0){ 
     strcpy(result,"Errore, il file aperto non è quello sul quale si vuole operare!\n\0"); 
     return -1; 
    } 
} 

switch(flag){ 
    case 1: //write 
     if(fd == -1){ 
      strcpy(result,"Errore, nessun file aperto!\n\0"); 
      return -1; 
     }else{ 
      send(client_descriptor, operation[1], strlen(operation[1]), 0); //ask for text over network 
      recv(client_descriptor, testo, sizeof(testo), 0); 
      while(lockf(fd, F_TEST, 0) != 0); 
      lockf(fd, F_LOCK, 0); 
      write(fd, testo,sizeof(testo)); 
      lockf(fd, F_ULOCK, 0); 
      memset(testo, '\0', sizeof(testo)); 
     } 
     break; 
    case 2: //read 
     if(fd == -1){ 
      strcpy(result,"Errore, nessun file aperto!\n\0"); 
      return -1; 
     }else{ 
      send(client_descriptor, operation[0], strlen(operation[0]), 0); 
      while(read(fd, testo, sizeof(testo)) > 0) send(client_descriptor, testo, strlen(testo), 0); 
     } 
     break; 
    case 3: //seek 
     if(fd == -1){ 
      strcpy(result,"Errore, nessun file aperto!\n\0"); 
      return -1; 
     }else{ 
      send(client_descriptor, operation[2], strlen(operation[2]), 0); 
      recv(client_descriptor, off, sizeof(off), 0); 
      offset = atoi(off); 
      while(lockf(fd, F_TEST, 0) != 0); 
      lockf(fd, F_LOCK, 0); 
      lseek(fd, (long int)offset, SEEK_SET); 
      lockf(fd, F_ULOCK, 0); 
     } 
     break; 
    case 4: //open 
     send(client_descriptor, noop, sizeof(noop), 0); 
     if(fd == -1){ 
      if((fd = open(file, O_RDWR))<0){ 
       strcpy(result,"Errore, file inesistente!\n\0"); 
       return -1; 
      }else strcpy(file_opened, file); 
     }else{ 
      strcpy(result,"Errore, un file è già aperto!\n\0"); 
      return -1; 
     } 
     break; 
    case 5: //close 
     send(client_descriptor, noop, sizeof(noop), 0); 
     if(fd == -1){ 
      strcpy(result,"Errore, nessun file aperto!\n\0"); 
      return -1; 
     }else{ 
      close(fd); 
      memset(file_opened, '\0', strlen(file_opened)); 
     } 
     break; 
    case 6: //delete 
     send(client_descriptor, noop, sizeof(noop), 0); 
     if(strcmp(file_opened, file) == 0){ 
      strcpy(result,"Errore, il file da eliminare è attualmente aperto!\n\0"); 
      return -1; 
     }else if(remove(file) < 0){ 
      strcpy(result,"Errore, il file da eliminare non esiste!\n\0"); 
      return -1; 
     } 
     break; 
    case 7: //create 
     send(client_descriptor, noop, sizeof(noop), 0); 
     if(open(file, O_CREAT)<0){ 
      strcpy(result,"File inestente, creane uno prima di scriverci!\n\0"); 
      return -1; 
     } 
     break; 
} 
strcpy(result,"\nSuccesso!\n\0"); 
return 0; 

} 

Serverの新しい接続、フォーク()自体を受け入れ、リスニングソケットを作成し、父は聞くに戻って子供がクライアントを提供しています。 具体的には、子はクライアント要求を受信し、実行する操作であるoperazione []とターゲットである[]を分割します。その後、それらを制御して操作を実行します。要求文字列が終了するまで繰り返す。

CLIENT

#include<stdio.h> 
#include<stdlib.h> 
#include<unistd.h> 
#include<errno.h> 
#include<string.h> 
#include<netdb.h> 
#include<sys/types.h> 
#include<netinet/in.h> 
#include<sys/socket.h> 

extern int inet_pton(); 

int main(int argc, char *argv[]){ 

int server, porta; 
struct sockaddr_in addr; 
char result[256], richiesta[256], risposta[256], testo[8192]; 
socklen_t len; 

if(argc!=3){        //CONTROLLO I PARAMETRI 
    printf("Errore nei parametri.\n"); 
    return -1; 
}else porta = atoi(argv[2]);    //CONVERTO IN NUMERO LA PORTA 

if((server = socket(AF_INET, SOCK_STREAM, 0))<0){  //CREAZIONE SOCKET 
    perror("Errore nella creazione della socket."); 
    return -1; 
} 

memset(&addr, 0, sizeof(addr));       //AZZERO LA STRUTTURA 
addr.sin_family = AF_INET;        //SETTAGGIO ATTRIBUTI STRUTTURA 
addr.sin_port = htons(porta); 
if((inet_pton(AF_INET, argv[1], &addr.sin_addr))<0){ 
    printf("Settaggio attributi fallito.\n"); 
    return -1; 
} 

len = sizeof(addr); //LUNGHEZZA IN BYTE DELLA STRUTTURA 

if((connect(server, (struct sockaddr *)&addr, len))<0){  //CONNESSIONE AL SERVER 
    perror("Connessione fallita."); 
    return -1; 
} 

printf("Connessione stabilita!\n"); 

while(1){ //PER SEMPRE 

    sleep(2); 
    system("clear");                //PULISCI SCHERMO 
    memset(richiesta, '\0', sizeof(richiesta));          //AZZERAMENTO RICHIESTA 
    memset(risposta, '\0', sizeof(risposta));          //AZZERAMENTO RISPOSTA 
    do{ 
     printf("SUPPORTATE (read write seek open close delete create) -file ...\n"); 
     printf("Richiesta: "); 
    }while((fgets(richiesta, sizeof(richiesta), stdin)) == NULL); 
    printf("RICHIESTA %s\n", richiesta); 
    printf("Hey");              //ACQUISISCO RICHIESTA 
    if(strcmp(richiesta,"exit") == 0){      //SE È UGUALE ALLA STRINGA "exit", ESCE DAL CICLO 
     send(server, "exit\0", 5, 0);      //SENDO "exit" AL SERVER 
     close(server);          //CHIUDO LA CONNESSIONE 
     return 0; 
    } 
    printf("HELLO"); 
    send(server, richiesta, strlen(richiesta), 0); //SENDO RICHIESTA 

    while(1){ 
     while(recv(server, risposta, sizeof(risposta), 0) == 0); //RICEVO LA PRIMA RISPOSTA  
     if(strcmp(risposta,"end") == 0) break;   //RICHIESTA PROCESSATA PER INTERO 

     if((strcmp(risposta,"read") == 0) || (strcmp(risposta,"write") == 0) || (strcmp(risposta,"seek") == 0)){ //SE LA RISPOSTA È UGUALE A "read", "write" O "seek" 
      memset(testo, '\0', sizeof(testo));         //AZZERO TESTO 
      if(strcmp(risposta,"read") == 0){         //SE È UGUALE A "read"        
       while(recv(server, testo, sizeof(testo), 0) > 0){    //LEGGO TUTTO E STAMPO A VIDEO 
        printf("%s", testo); 
        memset(testo, '\0', sizeof(testo)); 
       } 
      }else if(strcmp(risposta,"write") == 0){       //SE È UGUALE A "write" 
       printf("Testo da scrivere sul file: "); 
       scanf("%s", testo); 
       send(server, testo, sizeof(testo), 0);       //ACQUISISCO IL TESTO E LO MANDO AL SERVER 
      }else if(strcmp(risposta,"seek") == 0){        //SE È UGUALE A "seek" 
       printf("Numero di byte spostamento dall'inizio del file: "); 
       scanf("%s", testo);            //ACQUISISCO NUMERO BYTE E SENDO 
       send(server, testo, sizeof(testo), 0); 
      } 
     } 
     recv(server, result, sizeof(result), 0); 
     printf("RESULT %s\n", result);      //STAMPO LA RISPOSTA & AZZERO LA RISPOSTA 
     memset(risposta, '\0', sizeof(risposta)); 
     memset(result, '\0', sizeof(result)); 
    } 
} 
return 0; 
} 

クライアントが必要なときに(例えば、書き込みまたは求める)以上のテキストを送信、サーバーに要求を送信し、必要なときにそれを表示する(例えば読み取り)し、その後の状態を表示する必要があります実行後にサーバーによって送信された操作(成功またはエラー)。

私の問題は、クライアントにリクエストをタイプした後、スタックして何もしないように思えるということです。このような 'Hey'や 'Hello'のようなコントロールは表示されません。 while(recv(server, risposta, sizeof(risposta), 0) == 0);recv(server, risposta, sizeof(risposta), 0);に置き換えると表示されますが、recv()がブロックしないかのようにループを開始します。

どこにエラーがありますか?私は怒っている。

+2

['recv()'](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html)関数は、リモートシステムが正常にシャットダウンしたときにゼロを返します。あなたのクライアントは 'recv(server、risposta、sizeof(risposta)、0)== 0);'をループすると、メッセージを受信するたびに終了します。長さ)またはエラーがある場合。しかし、サーバーが接続をシャットダウンすると、クライアントコードは永遠に末尾のスピンに入ります。あなたはそのループで何をやっているのか考え直してください! 'while(1)'ループの代わりに 'recv()'の条件を使うべきです。 –

+1

改行文字を出力するまで、printfsはバッファリングされます。 printfsに '\ n'を追加すると、デバッグに役立ちます。 – user3386109

答えて

1

recv()の結果を変数に格納せずに、(i)-1、エラーを示す、(ii)ピアが接続を閉じたことを示すゼロ、または)正の数。実際に受信したバイト数を示します。 (i)エラーを出力または記録し、ソケットを閉じて終了する必要がある場合。 (ii)の場合は、ソケットを閉じて終了する必要があります。

また、全体の要求が単一のrecv()で受信されると仮定することはできません。ループする必要があります。単一のrecv()オペレーションの結果として生じたバッファがNULL終端されていることを示します。

また、send()の結果をテストする必要があります。成功したと考えることはできません。

これをすべて修正してもう一度お試しください。

+0

私は、要求全体が単一の 'recv()'内で受信されるとは仮定できない場合、 ループを実行する必要がありますか? while(send(...)> 0) 'で十分ですか?あるいは、私は、すべての部分を受け取り一緒にマージする関数のように、より洗練されたものを実装しなければなりませんか? – Kociss

+0

別の問題があります。 クライアント側で良好な接続が確立されていても、サーバー側のaccept()がスタックします。どうして? – Kociss

関連する問題