[C] serwer współbieżny z użyciem FIFO - problem!

0

Chciałem prosić o pomoc przy pisaniu programu klient-serwer. Sprawa wygląda następująco:

  • tworzę dwa programy: serwer współbieżny i klient
  • ich działanie ma polegać na tym, że uruchamiam serwer a następnie uruchamiam program klienta z parametrem, którym jest ciąg znaków (do 255), klient przesyla następnie zapytanie do serwera (składające się ze wspomnianego ciągu znaków i PID klienta), który odsyła klientowi jako odpowiedź ilość małych znaków w tym ciągu znaków
  • serwer po uruchomieniu tworzy FIFO prywatne do którego klienci mogą zapisywać swoje zapytania, a serwer je stamtąd odczytuje
  • każdy klient tworzy własne FIFO prywatne (postaci /tmp/FIFO_[nr_PID_klienta]) do którego przesyła odpowiedź serwer (czyli liczbę oznaczającą ilość małych znaków w przesłanym mu tekście) i z którego odczytuje ją każdy z klientów
  • serwer w założeniu ma być serwerem współbieżnym czyli powinien być w stanie obsługiwać kilku klientów na raz

Mój problem polega na tym, że nie jestem w stanie sprawić by ten serwer był serwerem współbieżnym (wersję iteracyjną udało mi się zrobić). Zamieszczam tu kod programu klienta i serwera, ale serwer po przyjęciu pierwszego zgłoszenia od klienta zaczyna tworzyć ogromną ilość procesów potomnych i trzeba szybko przerywać jego pracę. Próbowałem już różnych rzeczy, szukałem informacji w internecie i książkach ale cały czas nie wiem w jaki sposób sprawić by to działało.

/*SERWER*/
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<unistd.h>
#include<ctype.h>
#include<string.h>
#include<errno.h>

#define FIFO_PUBLICZNE "/tmp/FIFO_30438"
#define MAX 256
#define TRUE 1

int do_odczytu, do_zapisu, deskr_fpryw, ile_przeczytano, ile_zapisano, wynik, pid, jaki_wynik,
	nr_proc, liczba_potomkow = 0;
char fifo_prywatne[15];

struct wiadomosc {
	pid_t pid_proc;
	char znaki[MAX];
	};
struct wiadomosc msg;

void obsluga_bledu_mkfifo(int jaki_wynik); 
int ile_malych(char ciag[], int length);
void proces_potomny();

int main(int argc, char *argv[]) {

	/*	
	tworzy FIFO publiczne do ktorego swoje zapytania beda zapisywali 
	klienci a serwer bedzie je odczytywal
	*/
	printf("## Tworze FIFO publiczne\n");
	obsluga_bledu_mkfifo(mkfifo(FIFO_PUBLICZNE, 0666));
	
	/*otwiera FIFO publiczne do odczytu*/
	do_odczytu = open(FIFO_PUBLICZNE, O_RDONLY);
	printf("Deskryptor do_odczytu: %d\n", do_odczytu);

	/*otwiera FIFO publiczne do zapisu, co nie bedzie wykorzystywane*/
	do_zapisu = open(FIFO_PUBLICZNE, O_WRONLY);
	printf("Deskryptor do_zapisu: %d\n", do_zapisu);

	while(TRUE) {
		pid = fork();
		if(pid == 0) {
			proces_potomny();
		}
		else if(pid == -1) {
			printf("Blad\n");
			exit(1);
		}
				
		liczba_potomkow++;
		while(liczba_potomkow) {
			nr_proc = waitpid(-1, NULL, WNOHANG);
			if(nr_proc == -1) {
				printf("Blad waitpid()!\n");
			}
			else if(nr_proc == 0) {
				break;
			}
			else {
				liczba_potomkow--;
			}
		}
	}
}

void proces_potomny() {
	/*serwer czyta z FIFO publicznego zapytanie wyslane przez klienta*/	
	ile_przeczytano = read(do_odczytu, &msg, (MAX+4));
			
	printf("Przeczytane znaki: %s\n", msg.znaki);
				
	/*
	serwer oblicza ilosc malych znakow w przeslanym przez
	klienta ciagu znakow
	*/
	printf("## Obliczam ilosc malych znakow w tekscie otrzymanym od klienta... ");	
	wynik = ile_malych(msg.znaki, ile_przeczytano);
	printf("Zakonczylem obliczanie.\n");

	sprintf(fifo_prywatne, "/tmp/FIFO_%d", msg.pid_proc);
				
	/*usypiam serwer na 1sek, by klient zdazyl utworzyc FIFO prywatne*/
	sleep(1); 

	/*
	otworzenie FIFO prywatnego, do ktorego zostanie 
	zapisana odpowiedz dla klienta
	*/
	printf("## Otwieram FIFO prywatne do zapisu... ");
	deskr_fpryw = open(fifo_prywatne, O_WRONLY);
	printf("Otworzylem.\n");
	printf("Deskryptor: %d\n", deskr_fpryw);

	/*
	zapisanie odpowiedzi do FIFO prywatnego,
	kazdy serwer ma swoje wlasne FIFO prywatne
	*/				
	printf("## Zapisuje wynik do FIFO prywatnego... ");
	ile_zapisano = write(deskr_fpryw, &wynik, MAX);
	printf("Zapisalem.\n");

	close(do_odczytu);
	close(deskr_fpryw);
	_exit(0);
}




void obsluga_bledu_mkfifo(int jaki_wynik) {
	if(jaki_wynik == 0) {
		printf("## Utworzono FIFO publiczne: %s\n", FIFO_PUBLICZNE);
	}
	else if(jaki_wynik < 0) {
		switch(errno) {
			case EROFS: 
				printf("## System plikow jedynie do odczytu.", 
					"Nie mozna utworzyc FIFO.\n");
			case ENOSPC:
				printf("## Brak miejsca na dysku.",
					"Nie mozna utworzyc FIFO.\n");
			case EACCES:
				printf("## Jeden z katalogow podanych w sciezce",
					"nie ma ustawionych uprawnien do przeszukiwania\n");
			case EEXIST:
				printf("## FIFO publiczne juz istnieje.\n");
		}
	}
}

int ile_malych(char ciag[], int length) {
	int i, ile_mal=0;
	for(i=0; i<length; i++) {
		if(islower(ciag[i])) {
			ile_mal++;
		}
	}
	return ile_mal;
}
/*KLIENT*/
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<ctype.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>

#define FIFO_PUBLICZNE "/tmp/FIFO_30438"
#define MAX 256

void obsluga_bledu_mkfifo(int jaki_wynik, char nazwa_fifo_pryw[]); 

int ile_zapisano, ile_odczytano, deskr_fpubl, deskr_fpryw, wynik;
char fifo_prywatne[15];
struct wiadomosc {
	pid_t pid_proc;
	char znaki[MAX];
	};
struct wiadomosc msg;

int main(int argc, char *argv[]) {
	if(argc == 1) {
		printf("Blad! Musisz podac parametr.\n");
	}
	else if(argc > 2) {
		printf("Blad! Musisz podac tylko jeden parametr.\n");
	}
	else if(argc==2) {
		/*zapisuje w strukturze msg pid klienta*/		
		msg.pid_proc = getpid();

		/*
		zapisuje w strukturze msg ciag znakow podanych jako parametr 
		podczas uruchomienia klienta
		*/
		strcpy(msg.znaki, argv[1]);	
	
		/*otwiera FIFO publiczne do zapisu*/		
		printf("## Otwieram FIFO publiczne do zapisu... ");
		deskr_fpubl = open(FIFO_PUBLICZNE, O_WRONLY);
		printf("Otworzylem.\n");
		printf("Deskryptor: %d.\n", deskr_fpubl);
		
		/*zapisuje do FIFO publicznego strukture msg*/
		printf("## Zapisuje do FIFO publicznego... ");
		ile_zapisano = write(deskr_fpubl, &msg, (MAX+4));
		printf("Zakonczylem zapisywanie.\n");
 
		sprintf(fifo_prywatne, "/tmp/FIFO_%d", msg.pid_proc);

		/*
		tworzy FIFO prywatne, do ktorego serwer zapisze
		odpowiedz i z ktorego nastepnie klient ja odczyta
		*/	
		printf("## Tworze FIFO prywatne.\n");
		obsluga_bledu_mkfifo(mkfifo(fifo_prywatne, 0666), fifo_prywatne);
		
		/*klient otwiera do odczytu FIFO prywatne*/
		printf("## Otwieram FIFO prywatne do odczytu... \n");
		deskr_fpryw = open(fifo_prywatne, O_RDONLY);
		printf("Deskryptor: %d\n", deskr_fpryw);
		printf("Otworzylem.\n");

		/*
		klient odczytuje z FIFO prywatnego
		przeslana przez serwer odpowiedz
		*/		
		printf("## Odczytuje z FIFO prywatnego wynik... "); 
		ile_odczytano = read(deskr_fpryw, &wynik, MAX);
		printf("Odczytalem.\n");

		printf("Liczba malych liter we wprowadzonym tekscie: %d\n", wynik); 	
		
		close(deskr_fpubl);
		close(deskr_fpryw);
		exit(0);
	}
}

void obsluga_bledu_mkfifo(int jaki_wynik, char nazwa_fifo_pryw[]) {
        if(jaki_wynik == 0) {
                printf("## Utworzono FIFO prywatne: %s\n", nazwa_fifo_pryw);
        }
        else if(jaki_wynik < 0) {
                switch(errno) {
                        case EROFS:
                                printf("## System plikow jedynie do odczytu.",
                                        "Nie mozna utworzyc FIFO.\n");
                        case ENOSPC:
                                printf("## Brak miejsca na dysku.",
                                        "Nie mozna utworzyc FIFO.\n");
                        case EACCES:
                                printf("## Jeden z katalogow podanych w sciezce",
                                        "nie ma ustawionych uprawnien do przeszukiwania\n");
                        case EEXIST:
                                printf("## FIFO prywatne juz istnieje\n");
                }
        }
}
0
while(TRUE) { /// [1]
                pid = fork();
                if(pid == 0) {
                        proces_potomny();
                }
                else if(pid == -1) {
                        printf("Blad\n");
                        exit(1);
                }
                                
                liczba_potomkow++;
                while(liczba_potomkow) { ///[2]
                        nr_proc = waitpid(-1, NULL, WNOHANG);
                        if(nr_proc == -1) {
                                printf("Blad waitpid()!\n");
                        }
                        else if(nr_proc == 0) {
                                break;
                        }
                        else {
                                liczba_potomkow--;
                        }
                }
        }

zakladajc ze petla numer [2] sie NIE zablokuje na pewien czas, petla numer jeden bedzie z maksymalna mozliwa szybkoscia tworzyc nowe procesy -- po co tworzyc je w ciemno, w nieskonczonosc? powinno to byc bardziej inteligentne - tylko tyle procesow ile jest potrzebne

a petla ta nigdy sie nie zablokuje, poniewaz w waitpid podales NOHANG, czyli sprawdzamteraz-nieblokuje-jadedalej.

wiec:

  • albo calkiem zmieniasz logike tworzenia nowych procesow z tworzenie-na-chama na np. utrzymywanie stalej ilosci np. 10 procesow roboczych
  • albo rezygnujesz z maxspeeda petli numer [1] i ja jakos spowalniasz
  • albo zamiast w/w spowalniania petli - zatrzymujesz ja calkiem az nadejdzie potrzeba stworzenia nowego procesu
  • albo (..)

wiekszosc z tych rzeczy da sie osiagnac modyfikujac petle [2]

1 użytkowników online, w tym zalogowanych: 0, gości: 1