2016-12-01 2 views
0

Это моя первая попытка реализовать многопользовательский файловый сервер (вид) с помощью fork(). Цель состоит в том, чтобы обрабатывать несколько хостов, которые отправляют операции в форме «create delete open close write read seek -filetarget ...» (например, create -hello.c написать -hello.c удалить -hello.c).Многопользовательский TCP-сервер и клиент, застрявший в цикле

SERVER

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

} 

Сервер создает сокет, принять новое соединение, вилка() сама по себе, отец возвращается, чтобы слушать и ребенка служит клиенту. В частности, ребенок получает запрос клиента и разбивает его на две части: operazione [], которая является операцией для выполнения и файлом [], который является целью. Затем контролируйте их и выполняйте операцию. Повторяйте, пока строка запроса не будет завершена.

КЛИЕНТ

#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 буферизуются до тех пор, пока вы не выведете символ новой строки. Поэтому добавление '\ n' к printfs должно помочь с вашей отладкой. – user3386109

ответ

1

Вы не можете написать правильный сетевой код, не сохраняя результат recv() в переменную и тестируя его для (i) -1, указывая на ошибку, (ii) нуль, указывающий, что сверстник закрыл соединение, или (iii) положительное число, которое указывает количество байтов, которое вы фактически получили. В случае (i) вам необходимо распечатать или зарегистрировать ошибку, закрыть сокет и выйти; в случае (ii) вам нужно закрыть розетку и выйти.

Вы также не можете предположить, что все запросы получены в одном recv(): вам нужно выполнить цикл; или что буфер, полученный в результате любой отдельной операции recv(), заканчивается нулем.

Вам также необходимо проверить результат send(): вы не можете просто предположить, что это удалось.

Исправьте все это и повторите попытку.

+0

Если я не могу предположить, что весь запрос получает в пределах одного «recv()», , как цикл должен быть выполнен? Достаточно ли 'while (отправить (...)> 0)'? Или мне нужно реализовать нечто более сложное, как функцию, которая получает все части и объединяется вместе? – Kociss

+0

У меня есть еще одна проблема. Принимать() на стороне сервера застрял, даже если хорошее соединение сделано клиентом. Зачем? – Kociss