多宿主TCP服务器和客户端陷入循环

时间:2016-12-01 02:01:49

标签: c sockets tcp fork

这是我第一次尝试使用fork()实现多宿主文件服务器(种类)。目的是处理以表格形式发送操作的多个主机'创建删除打开关闭写入读取搜索-filetarget ...' (例如,创建-hello.c write -hello.c delete -hello.c)。

服务器

#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 []即执行的操作和作为目标的文件[]。然后控制它们并执行操作。重复直到请求字符串终止。

客户端

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

客户端应向服务器发送请求,在需要时发送更多文本(例如写入或查找)并在需要时显示(例如读取),然后显示服务器发送的操作状态(成功或错误)执行。

我的问题是,在客户端中键入请求后,似乎卡住了,什么都不做。没有一个控制打印这样的&#39;嘿&#39;或者&#39;你好&#39;显示。 如果我用while(recv(server, risposta, sizeof(risposta), 0) == 0);替换recv(server, risposta, sizeof(risposta), 0);,但它会开始循环,就好像recv()没有阻塞一样。

错误在哪里?我疯了。

1 个答案:

答案 0 :(得分:1)

如果不将recv()的结果存储到变量中并将其测试为(i)-1,表示错误,(ii)为零,表示对等方已关闭连接,则无法编写正确的网络代码,或(iii)正数,表示您实际收到的字节数。如果(i)您需要打印或记录错误,请关闭套接字并退出;如果(ii)你需要关闭套接字并退出。

您也不能假设在一个recv()中收到整个请求:您必须循环;或任何单个recv()操作产生的缓冲区以空值终止。

您还需要测试send()的结果:您不能只是假设它成功了。

解决所有问题并重试。