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

chat : client : sendMessage.c

sᴀʟᴠᴀᴛᴏʀᴇ ʙ edited this page Jul 23, 2017 · 2 revisions
void sendMessage() {
	/*
	 * Connessione a server_pipe e client_pipe gia' effettuata e reperibile in main_client.h
	 * 1) acquisire il messaggio da parte dell'utente
	 * 2) verificare che sia non troppo lungo e che ci siano caratteri
	 * 3) se il messaggio è valido, richiedere uno o più destinatari
	 * 4) impacchettare il messaggio come da protocollo
	 *    -> "MSG <pid_destinatari> $<pid_mittente> <messaggio>"
	 * 5) invia il messaggio su server_pipe e libera la memoria occupata
	 */
	//
	char* message = malloc(sizeof(char) * MAX_LENGTH_MSG);
	if (message == NULL)
		exit(EXIT_FAILURE);
	char** receivers = NULL;
	int result = getMessage(message);
	if (result == OK) {
		result = getReceivers(&receivers);
		if (result != 0) {
			//result contiene il numero di destinatari
			buildMessage(&message, receivers, result);
			//il messaggio è pronto per essere inviato al server
			write(fdServerPipe, message, strlen(message) + 1); 
			//+1 perche’ strlen non conta '\0'
		} else if (result == 0) {
			printf("Messaggio non inviato.\n");
		}
		// free di tutta la memoria allocata
		if (receivers != NULL) {
			int i = 0;
			for (; i < result; i++) {
				free(*(receivers + i));
			}
			free(receivers);
		}
	}
	if (message != NULL)
		free(message);
}

Come vincolo è stato deciso di imporre un massimo di 4096 caratteri per messaggio (specificato in MAX_LENGTH_MSG). Ogni campo del comando viene individuato e gestito separatamente in più funzioni, per poi essere ricompattati dalla funzione buildMessage(). In seguito si elencano tali funzioni.

int getMessage(char* message) {
	int result;
	printf("Inserisci messaggio (MAX %d caratteri).\n", MAX_LENGTH_MSG);
	result = readLine(message, sizeof(char[MAX_LENGTH_MSG]));
	if (result == TOO_LONG) {
		printf("Messaggio troppo lungo. Messaggio non inviato.\n");
	} else if (result == NO_INPUT) {
		printf("Errore di lettura.\n");
	} else if (result == INVALID) {
		printf("Nessun carattere inserito. Messaggio non inviato.\n");
	}
	return result;
}
/*
 * Acquisisce una linea di testo da stdin verificando che non sia:
 * TOO_LONG = la stringa è troppo lunga
 * INVALID = la stringa non contiene caratteri
 * NO_INPUT = errore di I/O
 */
int readLine(char *buff, size_t sz) {
	int ch, extra;
	if (fgets(buff, sz, stdin) == NULL)
		return NO_INPUT;
	char* temp = buff;
//controllo che ci sia almeno un carattere e non solo spazi
	while (*temp == ' ') {
		temp++;
	}
	if (*temp == '\n')
		return INVALID;
	/*se la stringa fosse troppo lunga non ci sarebbe carattere di newline
	 è necessario rimuovere le digitazioni presente nel buffer input
	 per non influenzare le prossime richieste*/
	if (buff[strlen(buff) - 1] != '\n') {
		extra = 0;
		while (((ch = getchar()) != '\n') && (ch != EOF))
			extra = 1;
		return (extra == 1) ? TOO_LONG : OK;
	}
// rimozione del carattere newline
	buff[strlen(buff) - 1] = '\0';
	return OK;
}

La funzione readLine() è stata realizzata sulla falsa riga di getLine() della libreria utilities.c, dato che non era possibile riutilizzare fedelmente quest’ultima: in questo contesto è consentito non inserire alcun carattere, con l’effetto di annullare l’operazione di invio di un messaggio (la stessa cosa accade nel tentativo di invio di un messaggio troppo lungo).

/*
 * Acquisisce dall'utente un determinato numero di pid,
 * termina quando l'utente digita \n oppure annulla l'invio del messaggio digitando 0+invio
 * return > 0 -> il client ha inserito i destinatari desiderati
 * return 0 -> il client non vuole più inviare il messaggio e annulla l'operazione
 */
int getReceivers(char*** receivers) {
	int count = 0, pid;
	do {
		printf(
"Inserisci PID del processo destinatario (0 per annullare, INVIO per terminare): ");
		pid = getValue();
		if (pid == getpid())
			printf("Non puoi inserire il tuo stesso pid.\nRiprovare: ");
		else if (pid != -1 && pid != 0) {
			count++;
			//riallocazione di count puntatori
			*receivers = realloc(*receivers, sizeof(char*) * count);
			if (*receivers == NULL)
				exit(EXIT_FAILURE);
			//allocazione e inserimento dell'ultimo pid inserito
			int digits = countDigits(pid) + 1; //+1 per il '\0'
			*(*receivers + count - 1) = malloc(sizeof(char) * digits);
			if (*(*receivers + count - 1) == NULL)
				exit(EXIT_FAILURE);
			sprintf(*(*receivers + count - 1), "%d", pid);
		} else if (pid == -1) {
			count = 0; //azzerando count il messaggio non verrà inviato
		}
	} while (pid != -1 && pid != 0);
	return count;
}

La funzione getReceivers() si occupa di richiedere uno per volta i pid destinatari, terminando normalmente quando l’utente digita una riga vuota oppure annullando l’operazione se l’utente digita 0 (che non è ovviamente un pid valido in questo contesto dato che nessun processo può avere un pid uguale a zero).

Non essendo noti il numero di destinatari è stato necessario realizzare una matrice dinamica: ad ogni nuovo pid correttamente inserito (attraverso la funzione getValue() ) si rialloca un puntatore ad un pid_t, a questo punto si converte in stringa il pid numerico ricevuto e si alloca settando opportunamente tale puntatore. Dato che realloc() permette di allocare un blocco contiguo, anche tutti i puntatori sono contigui, pertanto sfruttando l’aritmetica dei puntatori si può accedere all’ultimo sommando all’indirizzo base “receivers” il valore “count-1” (dove count è il numero di clients destinatari inseriti). Infine si restituisce il numero di clients destinatari richiesti.

	/*
	 * return 0 -> il client ha inserito i destinatari desiderati
	 * return -1 -> il client non vuole più inviare il messaggio e annulla l'operazione
	 */
	int getValue() {
	    int value;
	    int validSyntax = 0;
	    char buffer[100];
	    do {
	        if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
	            //errore di lettura, uscita forzata
	            return -1;
	        }
	        char* bufferTrimmed = trim(buffer);
	        //trim restituisce NULL se l'utente ha digitato solo "{' '}\n"
	        if (bufferTrimmed == NULL)
	            //terminazione
	            return 0;
	        else {
	            // un carattere iniziale resituirebbe 0
	            validSyntax = sscanf(bufferTrimmed, "%d", &value)
	                    && bufferTrimmed[0] != ' ';
	        }
	        if (validSyntax != 1)
	            printf("Input sintatticamente non valido. Riprovare: ");
	    } while (!validSyntax);
	    // se value==0 allora l'utente vuole annullare l'operazione e restituisco -1
	    // altrimenti restituisco value che e' il pid del destinatario
	    return value == 0 ? -1 : value;
	}

Anche la funzione getValue() è analoga alla controparte di utilities.c, ma in questo caso è stata modificata per consentire l’inserimento di una riga vuota, necessaria per richiedere la terminazione dell’inserimento dei pid destinatari; i possibili valori che può restituire sono:

  • numero maggiore di 0: pid destinatario;
  • 0: terminazione;
  • -1: annullamento dell’operazione.
/*
 * Inserisce in "message" la stringa opportunamente formattata
 * MSG <pid_dest1> ... <pid_destN> $<pid_mittente> <message>
 */
void buildMessage(char** message, char** receivers, int count) {
	/*
	 * 1) calcolo la dimensione del messaggio finale
	 * 2) copio "MSG " in un variabile d'appoggio "temp"
	 * 3) concateno tutti i destinatari separati da uno spazio
	 * 4) concateno il pid mittente
	 * 5) concateno il messaggio
	 * 6) rialloco lo spazio in message
	 * 7) copio il contenuto di temp in message
	 */
	// "MSG " -> 4 caratteri
	int dimension_message = 4, i;
	for (i = 0; i < count; i++) {
		dimension_message += strlen(*(receivers + i)) + 1;	//+1 per lo spazio
	}
	dimension_message += (countDigits(getpid()) + 2); //+2  per '$' + spazio
	dimension_message += strlen(*message) + 1; //+1 per '\0'
	char* temp = malloc(sizeof(char) * dimension_message);
	if (temp == NULL)
		exit(EXIT_FAILURE);
	//concateno i dati
	strcpy(temp, "MSG ");
	for (i = 0; i < count; i++) {
		strcat(temp, *(receivers + i));
		strcat(temp, " ");
	}
	char* mypid = malloc(sizeof(char) * (countDigits(getpid()) + 3)); //+3 per '$' 									     //iniziale, ' ' e '\0' finali
	if (mypid == NULL)
		exit(EXIT_FAILURE);
	sprintf(mypid, "$%d ", getpid());
	strcat(temp, mypid);
	strcat(temp, *message);
	*message = realloc(*message, sizeof(char) * (strlen(temp) + 1));
	strcpy(*message, temp);
	free(temp);
	free(mypid);
}

Impacchetta il messaggio come da protocollo nella forma “MSG <pid_destinatari> $<pid_mittente> <messaggio>”. La dimensione del pacchetto finale viene opportunamente calcolata per occupare meno memoria possibile.

Notare come il client 2570 abbia tentato di inviare un messaggio a sé stesso per poi essere opportunamente avvertito dell’errore. Nel prossimo paragrafo si analizza la ricezione di tali messaggi a seguito di un segnale.

Clone this wiki locally