diff --git a/include/hashmap.h b/include/hashmap.h index 4a90775..6849d2d 100644 --- a/include/hashmap.h +++ b/include/hashmap.h @@ -1,6 +1,7 @@ #ifndef __HASHMAP_C__ #define __HASHMAP_C__ +#include #include #include #include @@ -8,11 +9,12 @@ /* Each process on Linux can have a maximum of 1024 open file descriptors */ #define HASHMAP_MAX_CLIENTS 1024 -struct hashmap { - int sockfd; - struct sockaddr_storage sas; - struct hashmap *next; -}; +typedef struct bucket { + int sockfd; /**< Client socket descriptor */ + struct sockaddr_storage sas; /**< Associated socket address */ + struct bucket *next; /**< Next node in case of collision */ + struct bucket *prev; /**< Previous node in case of collision */ +} bucket_t; /** * @brief Calculates the hash code of a given file descriptor @@ -25,35 +27,60 @@ int hash(int sockfd); /** * @brief Initializes the hash map * - * @param map[out] The hash map uninitialized + * @param bucket[out] The hash map uninitialized */ -void hm_init(struct hashmap *map); +void hm_init(bucket_t *bucket); /** * @brief Inserts a key in the hash map * - * @param map[out] The hash map + * @param bucket[out] The hash map * @param sockfd[in] The file descriptor (value) * @param sas[in] The sockaddr (value) */ -void hm_push(struct hashmap *map, int sockfd, struct sockaddr_storage *sas); +void hm_push(bucket_t *bucket, int sockfd, struct sockaddr_storage *sas); + +/** + * @brief Removes a key from the hash map + * + * @param bucket[out] The hash map + * @param sockfd[in] The key + */ +void hm_remove(bucket_t *bucket, int sockfd); /** * @brief Searches for a key in the hash map * - * @param map[in] The hash map + * @param bucket[in] The hash map * @param sockfd[in] The file descriptor (key) * @return struct hashmap* Returns NULL or the key pointer */ -struct hashmap *hm_lookup(struct hashmap *map, int sockfd); +bucket_t *hm_lookup(bucket_t *bucket, int sockfd); /** * @brief Cleans the hash map * - * @param map[out] The hash map + * @param bucket[out] The hash map */ -void hm_free(struct hashmap *map); +void hm_free(bucket_t *bucket); -void hm_insert(struct hashmap *map, int sockfd, struct sockaddr_storage *sas); +/** + * @brief Checks if a file descriptor is in the bucket array (not linked list) + * + * @param bucket[in] + * @param sockfd[in] + * @return true If the file descriptor is in the bucket array + * @return false If the file descriptor is not in the bucket array (check with hm_lookup()) + */ +bool hm_is_in_bucket_array(bucket_t *bucket, int sockfd); + +/** + * @brief This function will add a key even if it exists (use hm_push() instead) + * + * @param bucket + * @param sockfd + * @param sas + */ +void hm_insert(bucket_t *bucket, int sockfd, struct sockaddr_storage *sas); #endif \ No newline at end of file diff --git a/include/server.h b/include/server.h index 0e0deb7..cfe3afc 100644 --- a/include/server.h +++ b/include/server.h @@ -14,6 +14,8 @@ #include #include +#include "hashmap.h" + /* On which port the server will run */ #define PORT 3030 @@ -86,4 +88,11 @@ void setnonblocking(int sockfd); */ int handle_new_client(int sockfd, struct sockaddr_storage *their_sa, socklen_t *theirsa_size); +/** + * @brief Closes all the file descriptors opened + * + * @param map[in] The hash map + */ +void close_fds(bucket_t *bucket); + #endif diff --git a/src/hashmap.c b/src/hashmap.c index f7b85a6..99ff8ea 100644 --- a/src/hashmap.c +++ b/src/hashmap.c @@ -2,64 +2,84 @@ int hash(int sockfd) { return sockfd % HASHMAP_MAX_CLIENTS; } -void hm_init(struct hashmap *map) { +void hm_init(bucket_t *bucket) { /* Initialize everything to 0 for the struct, then -1 for fd and next to NULL */ - memset(map, 0, sizeof(struct hashmap) * HASHMAP_MAX_CLIENTS); + memset(bucket, 0, sizeof(bucket_t) * HASHMAP_MAX_CLIENTS); for (size_t i = 0; i < HASHMAP_MAX_CLIENTS; ++i) { - map[i].sockfd = -1; - map[i].next = NULL; + bucket[i].sockfd = -1; + bucket[i].next = NULL; + bucket[i].prev = NULL; } } -/* This function will add a key even if it exists (use hm_push() instead) */ -void hm_insert(struct hashmap *map, int sockfd, struct sockaddr_storage *sas) { +void hm_insert(bucket_t *bucket, int sockfd, struct sockaddr_storage *sas) { int index = hash(sockfd); - if (map[index].sockfd == -1) { + if (bucket[index].sockfd == -1) { /* Current slot is empty */ - map[index].sockfd = sockfd; - map[index].sas = *sas; - map[index].next = NULL; + bucket[index].sockfd = sockfd; + bucket[index].sas = *sas; } else { /* Append the new key to the head (not the first element because it belongs to the array) of the linked * list */ - struct hashmap *p = &map[index]; - struct hashmap *new = malloc(sizeof(struct hashmap)); + bucket_t *p = &bucket[index]; + bucket_t *new = malloc(sizeof(bucket_t)); new->sockfd = sockfd; new->sas = *sas; new->next = p->next; + new->prev = p; p->next = new; } } -struct hashmap *hm_lookup(struct hashmap *map, int sockfd) { +bucket_t *hm_lookup(bucket_t *bucket, int sockfd) { int index = hash(sockfd); - if (map[index].sockfd != sockfd) { - struct hashmap *p; - for (p = map[index].next; p != NULL && p->sockfd != sockfd; p = p->next); + if (bucket[index].sockfd != sockfd) { + bucket_t *p; + for (p = bucket[index].next; p != NULL && p->sockfd != sockfd; p = p->next); return p; } else { - return &map[index]; + return &bucket[index]; } return NULL; } -void hm_push(struct hashmap *map, int sockfd, struct sockaddr_storage *sas) { - if (hm_lookup(map, sockfd) == NULL) { - hm_insert(map, sockfd, sas); +void hm_push(bucket_t *bucket, int sockfd, struct sockaddr_storage *sas) { + if (hm_lookup(bucket, sockfd) == NULL) { + hm_insert(bucket, sockfd, sas); } } -void hm_free(struct hashmap *map) { - struct hashmap *p, *next; +void hm_remove(bucket_t *bucket, int sockfd) { + if (hm_is_in_bucket_array(bucket, sockfd)) { + /* Instead of doing this I could copy the memory of the next node into the head */ + int index = hash(sockfd); + bucket[index].sockfd = -1; + + return; + } + + /* Key not in the bucket array, let's search in the linked list */ + bucket_t *p = hm_lookup(bucket, sockfd); + if (p == NULL) return; + p->prev->next = p->next; + if (p->next != NULL) { + /* If there's a next node update the previous node */ + p->next->prev = p->prev; + } + free(p); +} + +void hm_free(bucket_t *bucket) { + bucket_t *p, *next; for (size_t i = 0; i < HASHMAP_MAX_CLIENTS; ++i) { - if (map[i].next != NULL) { + if (bucket[i].next != NULL) { /* Free the malloc */ - p = map[i].next; + p = bucket[i].next; next = p->next; do { free(p); @@ -69,3 +89,10 @@ void hm_free(struct hashmap *map) { } } } + +bool hm_is_in_bucket_array(bucket_t *bucket, int sockfd) { + int index = hash(sockfd); + if (bucket[index].sockfd == sockfd) return true; + + return false; +} diff --git a/src/server.c b/src/server.c index c64df49..57a8e89 100644 --- a/src/server.c +++ b/src/server.c @@ -41,10 +41,13 @@ int start_server(const char *hostname, const char *service) { void setup_hints(struct addrinfo *hints, size_t len, const char *hostname) { memset(hints, 0, len); - hints->ai_family = AF_UNSPEC; // IPv4 or IPv6 - hints->ai_socktype = SOCK_STREAM; // TCP + /* IPv4 or IPv6 */ + hints->ai_family = AF_UNSPEC; + /* TCP */ + hints->ai_socktype = SOCK_STREAM; if (hostname == NULL) { - hints->ai_flags = AI_PASSIVE; // fill in IP for me + /* Fill in IP for me */ + hints->ai_flags = AI_PASSIVE; } } @@ -52,7 +55,7 @@ void handle_clients(int sockfd) { struct sockaddr_storage their_sa; socklen_t theirsa_size = sizeof their_sa; - struct hashmap clients[HASHMAP_MAX_CLIENTS]; + bucket_t clients[HASHMAP_MAX_CLIENTS]; hm_init(clients); int epfd = epoll_create1(0); @@ -73,7 +76,7 @@ void handle_clients(int sockfd) { for (int i = 0; i < nfds; ++i) { if (revents[i].data.fd == sockfd) { - // new client + /* New client */ client_fd = handle_new_client(sockfd, &their_sa, &theirsa_size); setnonblocking(client_fd); @@ -83,14 +86,14 @@ void handle_clients(int sockfd) { int bytes_sent = send(client_fd, msg, msg_len, 0); fprintf(stdout, "[server] Sent %d bytes\n", bytes_sent); } else { - // incoming data + /* Incoming data */ client_fd = revents[i].data.fd; int bytes_read = recv(client_fd, data, sizeof data, 0); if (bytes_read == 0) { - // client disconnected + /* Client disconnected */ char ip[INET_ADDRSTRLEN]; - struct hashmap *client = hm_lookup(clients, client_fd); + bucket_t *client = hm_lookup(clients, client_fd); get_client_ip(&client->sas, ip); fprintf(stdout, BLUE "[server] Client (%s) disconnected\n" RESET, ip); @@ -112,13 +115,7 @@ void handle_clients(int sockfd) { free(revents); close(epfd); - - /* Close all the file descriptors */ - for (size_t i = 0; i < HASHMAP_MAX_CLIENTS; ++i) { - close(clients[i].sockfd); - /* TODO: close collisions */ - } - + close_fds(clients); hm_free(clients); } @@ -127,6 +124,7 @@ void epoll_ctl_add(int epfd, int sockfd, uint32_t events) { event.events = events; event.data.fd = sockfd; int status = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event); + if (status != 0) { fprintf(stderr, RED BOLD "[server] epoll_ctl_add(): %s\n" RESET, strerror(errno)); exit(EXIT_FAILURE); @@ -135,6 +133,7 @@ void epoll_ctl_add(int epfd, int sockfd, uint32_t events) { void epoll_ctl_del(int epfd, int sockfd) { int status = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL); + if (status != 0) { fprintf(stdout, RED BOLD "[server] epoll_ctl_del(): %s\n" RESET, strerror(errno)); exit(EXIT_FAILURE); @@ -143,6 +142,7 @@ void epoll_ctl_del(int epfd, int sockfd) { void setnonblocking(int sockfd) { int status = fcntl(sockfd, F_SETFL, O_NONBLOCK); + if (status == -1) { fprintf(stderr, RED BOLD "[server] fcntl(): %s\n" RESET, gai_strerror(status)); exit(EXIT_FAILURE); @@ -161,3 +161,21 @@ int handle_new_client(int sockfd, struct sockaddr_storage *their_sa, socklen_t * return client_fd; } + +void close_fds(bucket_t *bucket) { + bucket_t *p, *next; + + for (size_t i = 0; i < HASHMAP_MAX_CLIENTS; ++i) { + close(bucket[i].sockfd); + if (bucket[i].next != NULL) { + /* Close the fds */ + p = bucket[i].next; + next = p->next; + do { + close(p->sockfd); + p = next; + next = p != NULL ? p->next : NULL; + } while (p != NULL); + } + } +}