2016-12-01 74 views
0

這是我第一次嘗試使用fork()實現多宿主文件服務器(種類)。目的是處理多個以「create delete open close write read seek -filetarget ...」形式發送操作的主機(例如,創建-hello.c寫入-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; 

} 

服務器創建一個監聽套接字,接受一個新的連接,fork()的本身,父親又回到聽取和兒童服務於客戶。 具體來說,孩子收到客戶端請求並將其分成兩部分:作爲要執行的操作的operazione []和作爲目標的file []。然後控制它們並執行操作。重複,直到請求字符串被終止。

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; 
} 

客戶應發送到所述服務器的請求,在需要時發送更多的文本(例如寫入或搜索)並在需要時顯示它(例如讀取),然後顯示的狀態操作(成功或錯誤)執行後由服務器發送。

我的問題是,在客戶端鍵入請求後,它似乎被卡住,什麼都不做。沒有一個控制printf顯示「嘿」或「你好」。 如果我將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)函數將返回零。你的客戶端循環while(recv(server,risposta,sizeof(risposta),0)== 0);'會在收到消息時退出(儘管你不知道消息有多長時間,因爲你沒有捕獲長度)或者是否有錯誤。但是一旦服務器關閉了連接,你的客戶端代碼將永遠進入尾部旋轉。重新思考你在這個循環中正在做什麼!你應該使用'recv()'來代替while(1)'循環。 –

+1

printfs被緩衝,直到你輸出換行符。因此,在printfs中添加'\ n'應該有助於您的調試。 – user3386109

回答

1

如果不將recv()的結果存儲到一個變量中,並且測試它爲(i)-1,表示錯誤,(ii)零,表示對等關閉了連接,或者(iii) )一個正數,表示您實際收到的字節數。如果(i)您需要打印或記錄錯誤,請關閉套接字並退出; (ii)您需要關閉插座並退出。

您也不能假設整個請求都是在一個recv()中收到的:您必須循環;或者任何單個recv()操作產生的緩衝區都是空終止的。

您還需要測試send()的結果:您不能僅僅假定它成功。

修復這一切,然後再試一次。

+0

如果我不能假設整個請求都是在單個'recv()'中接收的,那麼應如何循環? while(send(...)> 0)'是否足夠?或者我必須實現一些更復雜的功能,如接收所有部件併合並在一起的功能? – Kociss

+0

我有另一個問題。 即使客戶端建立了良好的連接,服務器端的accept()仍然卡住。爲什麼? – Kociss