-
Notifications
You must be signed in to change notification settings - Fork 0
chat : client : sendMessage.c
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.
Made with ❤️ by Owanesh and MatteoMauro | MIT ©