Это моя первая попытка реализовать многопользовательский файловый сервер (вид) с помощью 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() не блокируется.
Где ошибки? Я сошел с ума.
Функция ['recv()'] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html) возвращает ноль, когда удаленная система отключена изящно. Ваш клиентский цикл 'while (recv (server, risposta, sizeof (risposta), 0) == 0);' будет выходить всякий раз, когда будет получено сообщение (хотя вы не будете знать, сколько времени сообщение связано с тем, что вы не захватили длина) или если есть ошибка. Но как только сервер отключит соединение, ваш клиентский код навсегда вернется в хвост. Переосмыслите, что вы делаете с этим циклом! Вы должны использовать условие на 'recv()' вместо цикла while (1). –
printfs буферизуются до тех пор, пока вы не выведете символ новой строки. Поэтому добавление '\ n' к printfs должно помочь с вашей отладкой. – user3386109