Skip to content
This repository was archived by the owner on Nov 4, 2023. It is now read-only.

chat : server : main_server.c

sᴀʟᴠᴀᴛᴏʀᴇ ʙ edited this page Jul 23, 2017 · 1 revision

Il server non fa uso di multiprogrammazione, in quanto l’unica soluzione efficiente sarebbe stata l’utilizzo di thread che permettessero la condivisione (controllata e sicura) dei dati (come ad esempio la lista dei clients connessi). Al contrario, l’utilizzo di fork() non avrebbe consentito una vera esecuzione parallela in quanto:

  • per comunicare padre e figli avrebbero dovuto creare pipe anonime (che avrebbe aggiunto carico aggiuntivo al sistema), infatti i dati presenti nella memoria heap (come ad esempio la lista dei clients) sarebbero stati duplicati e le modifiche dei figli non avrebbero influenzato il padre direttamente;
  • premesso l’utilizzo di pipe anonime, il processo padre non avrebbe potuto comunque avanzare verso la lettura del prossimo comando sulla server_pipe, perché avrebbe dovuto attendere i risultati del processo figlio operativo sulla pipe anonima associata. Date queste premesse, il server è stato realizzato puramente sequenziale nell’esecuzione.
// puntatore alla testa della lista dei client
Client *head = NULL;
//definizione delle variabile extern dichiarate in main_client.h
int server_pipe = 0;
// buffer per ogni comando letto da server_pipe
char* command = NULL;
char* server_pipe_path = "./server_pipe";

void switchCommand(char* cmd);
void handler_termination();

int main(void) {
	if (access(server_pipe_path, F_OK) != -1) {
// se il file server_pipe esiste, allora un processo server è già operativo
printf(
"[ERROR] Esiste già un processo server operativo. Impossibile avviarne più di uno contemporaneamente.");
	} else {
		// il processo puo' essere correttamente avviato
 		start();
		signal(SIGINT, handler_termination);
		printf("\n\n");
		while (1) {
			int isCmd = readCommand(&command);
			if (isCmd){
				printf("\n%s\n", command);
				switchCommand(command);
			}
		}
	}
}

Il server è responsabile di creare le pipe del programma, che verranno create nella cartella in cui si trova l’eseguibile, pertanto per funzionare il programma richiede che sia l’eseguibile “chat_server” che l’eseguibile “chat_client” siano mantenuti nella stessa directory. Il path di “server_pipe” è dichiarato nel modulo “server.h” ed è definito (inizializzato a “./server_pipe”) in questo modulo.

Il primo passo prevede di verificare attraverso la funzione access() se server_pipe è già presente nella cartella di esecuzione corrente, in caso affermativo ciò implicherebbe che un processo server è già operativo e si annulla il programma nuovo. La funzione start() si occupa di inizializzare il tutte le risorse necessarie al funzionamento, come ad esempio la creazione della server_pipe.

Dopo aver impostato l’handler di terminazione (l’unico modo per poter terminare correttamente il processo liberando tutte le risorse è ^C) si esegue un ciclo infinito di letture dalla server_pipe. La lista dei clients connessi è gestita come una lista unidirezionale il cui puntatore “head” è dichiarato in main_server.c.

void switchCommand(char* cmd) {
	char keyword[20];
	getFirstField(keyword, cmd);
	if (strcmp(keyword, "LIST") == 0) {
		// "LIST <pid>"
		printf("\nLIST cmd request ");
		writeClientList(head, cmd);
	} else if (strcmp(keyword, "CONNECT") == 0) {
		// "CONNECT <pid>"
		printf("\nCONNECT cmd request ");
		acceptConnection(&head, cmd);
	} else if (strcmp(keyword, "MSG") == 0) {
		// "MSG <pid_destinatari> $<pid_mittente> <messaggio>"
		printf("\nMSG cmd request ");
		deliverMessage(head, cmd);
	} else if (strcmp(keyword, "DISCONNECT") == 0) {
		// "DISCONNECT <pid>"
		printf("\nDISCONNECT cmd request ");
		disconnect(&head, cmd);
	} else {
		printf("\"<%s>\": Comando non riconosciuto", cmd);
	}
}

L’individuazione dell’operazione da eseguire viene presa analizzando il primo campo di ogni comando letto (keyword) ed in base alla corrispondenza si esegue il ramo if associato.

void handler_termination() {
	/*
	 * 1) Libera la memoria di ogni nodo della lista e chiude/elimina le pipe
	 * 2) Chiude ed elimina la connessione con server_pipe
	 * 3) libera command
	 */
	Client* ptr;
	while (head != NULL) {
		ptr = head;
		close(ptr->pipe);
		char* temp = malloc(sizeof(char) * 50);
		getClientPipePath(ptr->pid, temp);
		unlink(temp);
		free(temp);
		head = head->next;
		free(ptr);
	}
	close(server_pipe);
	unlink(server_pipe_path);
	free(command);
	printf("\nIl server e' terminato con successo.");
	exit(EXIT_SUCCESS);
}

La funzione handler è stata inserita in “main_server.c” a causa dell’impossibilità di passare degli argomenti, in particolare in questo contesto era necessario accedere al puntatore della lista per poter liberare la memoria di ogni nodo. Infine si elimina dal file system ogni pipe FIFO creata durante la sessione di esecuzione.

Clone this wiki locally