From 4fcb5f2e5d49bbf712945669b1de480d46595767 Mon Sep 17 00:00:00 2001 From: Raymaekers Luca Date: Wed, 30 Oct 2024 11:32:33 +0100 Subject: Refactor server code into functions - disconnect(): for disconnecting a client and deinitializing it - sendToOthers(): for sending a message to all other clients - initClient(): for initializing a client --- chatty.c | 9 +-- server.c | 233 ++++++++++++++++++++++++++++++++++++--------------------------- 2 files changed, 137 insertions(+), 105 deletions(-) diff --git a/chatty.c b/chatty.c index bd131a5..00f3f95 100644 --- a/chatty.c +++ b/chatty.c @@ -44,10 +44,11 @@ popup(u32 fg, u32 bg, char* text) tb_print(global.width / 2 - len / 2, global.height / 2, fg, bg, text); } -// Takes as paramter `struct sockaddr_in*` and uses it to connect to the server. -// When the server sends a disconnect message this function must be called with the fds struct as -// paramter. To indicate that the server is offline the fds[FDS_SERVER] is set to -1. When online -// it is set to a non-zero value. +// Connect to *address_ptr of type `struct sockaddr_in*`. If it failed wait for TIMEOUT_RECONNECT +// seconds. +// This function is meant to be run by a thread. +// An offline server means fds[FDS_SERVER] is set to -1. When online +// it is set to with the appropriate file descriptor. // Returns NULL. void* thread_reconnect(void* address_ptr) diff --git a/server.c b/server.c index 18ce0f9..5c60244 100644 --- a/server.c +++ b/server.c @@ -23,12 +23,128 @@ enum { FDS_STDIN = 0, // Has information on clients // For each pollfd in fds there should be a matching client in clients -// clients[i - FDS_CLIENTS] <=> fds[i] +// clients[i] <=> fds[i] typedef struct { u8 author[AUTHOR_LEN]; // matches author property on other message types Bool initialized; // boolean } Client; +// Send anyMessage to all clients in fds from fdsArena except for fds[i]. +void +sendToOthers(Arena* fdsArena, struct pollfd* fds, u32 i, HeaderMessage* header, void* anyMessage) +{ + s32 nsend; + for (u32 j = FDS_CLIENTS; j < FDS_SIZE; j++) { + if (fds[j].fd == fds[i].fd) continue; + if (fds[j].fd == -1) continue; + + // send header + u32 nsend_total = 0; + nsend = send(fds[j].fd, header, sizeof(*header), 0); + assert(nsend != -1); + assert(nsend == sizeof(*header)); + nsend_total += nsend; + + // send message + switch (header->type) { + case HEADER_TYPE_PRESENCE: { + PresenceMessage* message = (PresenceMessage*)anyMessage; + nsend = send(fds[j].fd, message, sizeof(*message), 0); + assert(nsend != -1); + assert(nsend == sizeof(*message)); + fprintf(stdout, " Notifying(%d->%d).\n", fds[i].fd, fds[j].fd); + break; + } + case HEADER_TYPE_TEXT: { + TextMessage* message = (TextMessage*)anyMessage; + nsend = send(fds[j].fd, message, TEXTMESSAGE_SIZE, 0); + assert(nsend != -1); + assert(nsend == TEXTMESSAGE_SIZE); + nsend_total += nsend; + nsend = send(fds[j].fd, &message->text, message->len * sizeof(*message->text), 0); + assert(nsend != -1); + assert(nsend == (message->len * sizeof(*message->text))); + nsend_total += nsend; + break; + } + default: + fprintf(stdout, " Cannot retransmit %s\n", headerTypeString(header->type)); + } + + fprintf(stdout, " Retransmitted(%d->%d) %d bytes.\n", fds[i].fd, fds[j].fd, nsend_total); + } +} + +void +disconnect(Arena* fdsArena, struct pollfd* fds, u32 i, Client* client) +{ + fprintf(stdout, "Disconnected(%d). \n", fds[i].fd); + shutdown(fds[i].fd, SHUT_RDWR); + close(fds[i].fd); // send close to client + + // Send disconnection to other connected clients + HeaderMessage header = HEADER_PRESENCEMESSAGE; + PresenceMessage message = { + .type = PRESENCE_TYPE_DISCONNECTED + }; + memcpy(message.author, client->author, AUTHOR_LEN); + sendToOthers(fdsArena, fds, i, &header, &message); + + fds[i].fd = -1; // ignore in the future + client->initialized = False; // deinitialize client +} + +// Initialize a client that connects for the first time or reconnects. +// Receive HeaderMessage and PresenceMessage from fd and set client with the data from +// PresenceMessage. +// Notify fds in fdsArena. +// TODO: handle wrong messages +void +initClient(Arena* fdsArena, struct pollfd* fds, s32 fd, Client* client) +{ + s32 nrecv = 0; + s32 nsend = 0; + + fprintf(stdout, " Adding to clients(%d).\n", fd); + + HeaderMessage header; + nrecv = recv(fd, &header, sizeof(header), 0); + assert(nrecv != -1); + assert(nrecv == sizeof(header)); + if (header.type != HEADER_TYPE_PRESENCE) { + // reject connection + close(fd); + fprintf(stdout, " Got wrong header(%d).\n", fd); + return; + } + fprintf(stdout, " Got header(%d).\n", fd); + + PresenceMessage message; + nrecv = recv(fd, &message, sizeof(message), 0); + assert(nrecv != -1); + assert(nrecv == sizeof(message)); + fprintf(stdout, " Got presence message(%d).\n", fd); + + // Copy author from PresenceMessage. + memcpy(client->author, message.author, AUTHOR_LEN); + + // Notify other clients from this new one + // Reuse header and message + for (u32 j = FDS_CLIENTS; j < FDS_SIZE; j++) { + if (fds[j].fd == fd) + continue; + if (fds[j].fd == -1) + continue; + fprintf(stdout, " Notifying(%d->%d).\n", fd, fds[j].fd); + nsend = send(fds[j].fd, &header, sizeof(header), 0); + assert(nsend != -1); + assert(nsend == sizeof(header)); + nsend = send(fds[j].fd, &message, sizeof(message), 0); + assert(nsend != -1); + assert(nsend == sizeof(message)); + } +} + int main(void) { @@ -59,7 +175,6 @@ main(void) Arena* msgsArena = ArenaAlloc(Megabytes(128)); // storing received messages // NOTE: sent messages? s32 nrecv = 0; // number of bytes received - s32 nsend = 0; // number of bytes sent Arena* clientsArena = ArenaAlloc(MAX_CONNECTIONS * sizeof(Client)); Arena* fdsArena = ArenaAlloc(MAX_CONNECTIONS * sizeof(struct pollfd)); @@ -96,20 +211,21 @@ main(void) assert(clientfd > serverfd); fprintf(stdout, "New connection(%d).\n", clientfd); - // fill up a hole + // If there is a slot in fds with fds[found].fd == -1 use it instead, otherwise allocate + // some space on the arena. u8 found; for (found = FDS_CLIENTS; found < FDS_SIZE; found++) if (fds[found].fd == -1) break; - if (found == FDS_SIZE) { + if (found == MAX_CONNECTIONS) { + // TODO: reject connection + close(clientfd); + fprintf(stdout, "Max clients reached."); + } else if (found == FDS_SIZE) { // no more space, allocate struct pollfd* pollfd = ArenaPush(fdsArena, sizeof(*pollfd)); pollfd->fd = clientfd; pollfd->events = POLLIN; - } else if (found == MAX_CONNECTIONS) { - // TODO: reject connection - close(clientfd); - fprintf(stdout, "Max clients reached."); } else { // hole found fds[found].fd = clientfd; @@ -119,58 +235,18 @@ main(void) } // Check for messages from clients - for (u32 i = FDS_CLIENTS; i < (FDS_SIZE); i++) { + for (u32 i = FDS_CLIENTS; i < FDS_SIZE; i++) { if (!(fds[i].revents & POLLIN)) continue; assert(fds[i].fd != -1); - fprintf(stdout, "Message(%d).\n", fds[i].fd); - // If this is the first message from the client it must be a presence message indicated - // it connected. - Client* client = clients + i - FDS_CLIENTS; + Client* client = clients + i; + + // Initialize the client if this is the first time if (!client->initialized) { - fprintf(stdout, " Adding to clients(%d).\n", fds[i].fd); - // Wait for PresenceMessage from new client to get author information - HeaderMessage header; - // TODO: handle wrong message, disconnection, etc. - nrecv = recv(clientfd, &header, sizeof(header), 0); - assert(nrecv != -1); - assert(nrecv == sizeof(header)); - if (header.type != HEADER_TYPE_PRESENCE) { - // TODO: reject connection - close(clientfd); - continue; - } - fprintf(stdout, " Got header(%d).\n", fds[i].fd); - - PresenceMessage message; - // TODO: handle wrong message - nrecv = recv(clientfd, &message, sizeof(message), 0); - assert(nrecv != -1); - assert(nrecv == sizeof(message)); - fprintf(stdout, " Got presence message(%d).\n", fds[i].fd); - - memcpy(client->author, message.author, AUTHOR_LEN); + initClient(fdsArena, fds, fds[i].fd, client); client->initialized = True; - fprintf(stdout, " Added to clients(%d): %s\n", fds[i].fd, client->author); - - // Notify other clients from this new one - // Reuse header and message - for (u32 j = FDS_CLIENTS; j < (FDS_SIZE); j++) { - if (fds[j].fd == fds[i].fd) - continue; - if (fds[j].fd == -1) - continue; - fprintf(stdout, " Notifying (%d)\n", fds[j].fd); - - nsend = send(fds[j].fd, &header, sizeof(header), 0); - assert(nsend != -1); - assert(nsend == sizeof(header)); - nsend = send(fds[j].fd, &message, sizeof(message), 0); - assert(nsend != -1); - assert(nsend == sizeof(message)); - } continue; } @@ -180,31 +256,7 @@ main(void) assert(nrecv != -1); if (nrecv == 0) { - fprintf(stdout, "Disconnected(%d). \n", fds[i].fd); - shutdown(fds[i].fd, SHUT_RDWR); - close(fds[i].fd); // send close to client - fds[i].fd = -1; // ignore in the future - clients[i - FDS_CLIENTS].initialized = False; // deinitialize client - // - // Send disconnection to other connected clients - HeaderMessage header = HEADER_PRESENCEMESSAGE; - PresenceMessage message = { - .type = PRESENCE_TYPE_DISCONNECTED - }; - memcpy(message.author, clients[i - FDS_CLIENTS].author, AUTHOR_LEN); - for (u32 j = FDS_CLIENTS; j < FDS_SIZE; j++) { - if (fds[j].fd == fds[i].fd) - continue; - if (fds[j].fd == -1) - continue; - nsend = send(fds[j].fd, &header, sizeof(header), 0); - assert(nsend != -1); - assert(nsend == sizeof(header)); - nsend = send(fds[j].fd, &message, sizeof(message), 0); - assert(nsend != -1); - assert(nsend == sizeof(message)); - } - + disconnect(fdsArena, fds, i, (clients + i)); continue; } @@ -218,30 +270,9 @@ main(void) fprintf(stderr, " Received(%d): %d bytes -> ", fds[i].fd, nrecv); printTextMessage(message, 0); - HeaderMessage header = HEADER_TEXTMESSAGE; // Send message to all other clients - for (u32 j = FDS_CLIENTS; j < FDS_SIZE; j++) { - if (fds[j].fd == fds[i].fd) continue; - if (fds[j].fd == -1) continue; - - // NOTE: I wonder if this is more expensive than constructing a buffer and sending - // that - u32 nsend_total = 0; - nsend = send(fds[j].fd, &header, sizeof(header), 0); - assert(nsend != 1); - assert(nsend == sizeof(header)); - nsend_total += nsend; - nsend = send(fds[j].fd, message, TEXTMESSAGE_SIZE, 0); - assert(nsend != -1); - assert(nsend == TEXTMESSAGE_SIZE); - nsend_total += nsend; - nsend = send(fds[j].fd, &message->text, message->len * sizeof(*message->text), 0); - assert(nsend != -1); - assert(nsend == (message->len * sizeof(*message->text))); - nsend_total += nsend; - - fprintf(stdout, " Retransmitted(%d->%d) %d bytes.\n", fds[i].fd, fds[j].fd, nsend_total); - } + sendToOthers(fdsArena, fds, i, &header, message); + break; default: fprintf(stdout, " Got unhandled message type '%s' from client %d", headerTypeString(header.type), fds[i].fd); -- cgit v1.2.3