Per fare il forwarding di un messaggio il server necessita di:
- Gestire una coda che contiene indirizzo e socket descriptor di tutti i client con cui è attiva una connessione;
- Un mutex per accedere alla coda in quanto questa è condivisa da tutti i thread;
Di seguito la prima versione della funzione eseguita dal thread ricevente nel client (link)
void recv_handler(int* pointer_sd) {
int sd = * pointer_sd;
char recvbuff[MAXLEN], nomeClient[30];
int option;
memset(recvbuff, 0, MAXLEN);
while(1){
// Ricevo opzione che mi indica il tipo di messaggio (da server o da altro client)
recv(sd, &option, sizeof(int), 0);
int msgType = ntohl(option);
// Messaggio inviato da un client
if(msgType == 1){
recv(sd, nomeClient, 30, 0); // ricevo nome del mittente
recv(sd, recvbuff, MAXLEN, 0); // ricevo messaggio
printf(COLOR_CYAN);
printf("%s: ", nomeClient);
printf(COLOR_RESET);
printf("%s", recvbuff);
}
// Messaggio dal server: indica che c'è stato l'ingresso di un nuovo client nel gruppo
else if(msgType == 2){
recv(sd, nomeClient, 30, 0); // ricevo nome del mittente
printf(COLOR_GREEN "=== %s si è unito alla chat ===" COLOR_RESET, nomeClient); printf(" \n");
}
// Messaggio dal server: indica che c'è stato l'uscita di un client dal gruppo
else if(msgType == 3){
recv(sd, nomeClient, 30, 0); // ricevo nome del mittente
printf(COLOR_RED "=== %s ha abbandonato ===" COLOR_RESET, nomeClient); printf(" \n");
}
}
}
Si può osservare che il client per stampare un messaggio deve ricevere:
- Numero che indica la tipologia del messaggio (messaggio normale, join, leave);
- Nome del client;
- Contenuto del messaggio (nel caso di messaggio normale);
Se considero una situazione con n client ed un messaggio "normale" da inviare: il server dovrà inviare 3*(n-1) pacchetti, mentre ogni client dovrà ricevere tre pacchetti.
Per risolvere questo problema di traffico elevato tra le socket , si è pensato di definire uno pseudo-protocollo over TCP.
Il formato dei messaggi che verranno scambiati tra le parti è formato dai seguenti campi:
- messageType: consiste in un enum che indica il tipo di messaggio;
- sender: conterrà il nome del client;
- message: conterrà il contenuto del messaggio da trasferire (se il messaggio è "normale");
#define MAX_MESSAGE_LENGTH 100
#define MAX_SENDER_NAME_LENGTH 32
#define SIZE_OF_MESSAGE_PROTOCOL sizeof(messageProtocol)
typedef enum {
MESSAGE_TYPE_JOIN,
MESSAGE_TYPE_LEAVE,
MESSAGE_TYPE_SEND,
} messageType;
typedef struct {
messageType typeMessage;
char sender[MAX_SENDER_NAME_LENGTH];
char message[MAX_MESSAGE_LENGTH];
} messageProtocol;
Una cosa da prendere in considerazione è che la sizeof di una struttura non è sempre uguale alla somma delle sizeof di ogni membro di essa, questo perché la dimensione della struttura intera include anche le parti di padding aggiunte dal compilatore per allineare i dati in memoria.
Il risultato finale di queste valutazioni mi ha portato a scegliere come dimensione di un singolo messaggio 136byte, scelta che riduce al minimo lo spazio di padding inserito, permettendo comunque la possibilità di trasmettere un numero di caratteri adeguato ad ogni campo.
Analisi mediante debugger | Formato pacchetto |
---|---|