这是我第一次尝试使用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()没有阻塞一样。
错误在哪里?我疯了。
答案 0 :(得分:1)
如果不将recv()
的结果存储到变量中并将其测试为(i)-1,表示错误,(ii)为零,表示对等方已关闭连接,则无法编写正确的网络代码,或(iii)正数,表示您实际收到的字节数。如果(i)您需要打印或记录错误,请关闭套接字并退出;如果(ii)你需要关闭套接字并退出。
您也不能假设在一个recv()
中收到整个请求:您必须循环;或任何单个recv()
操作产生的缓冲区以空值终止。
您还需要测试send()
的结果:您不能只是假设它成功了。
解决所有问题并重试。