From 947bce15a97544e1b93a5956e6efe6d25f1844f6 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 7 Jan 2025 02:00:39 +0000 Subject: [PATCH 01/41] Add IPv6 support to server --- client.c | 20 +++++++++++++------- server.c | 22 ++++++++++++---------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/client.c b/client.c index cafc637..15e4170 100644 --- a/client.c +++ b/client.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -9,17 +8,24 @@ int main(int argc, char **argv) { int sock; - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) + if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { perror("client: socket()"); exit(1); } - struct sockaddr_in server_addr; - server_addr.sin_family = PF_INET; - server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - server_addr.sin_port = htons(8080); - if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) + struct sockaddr_in6 server_addr6; + memset(&server_addr6, 0, sizeof(server_addr6)); + server_addr6.sin6_family = PF_INET6; + server_addr6.sin6_port = htons(8080); + if (inet_pton(PF_INET6, "::1", &server_addr6.sin6_addr) <= 0) + { + perror("client: inet_pton()"); + close(sock); + exit(1); + } + + if (connect(sock, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) == -1) { perror("client: connect()"); close(sock); diff --git a/server.c b/server.c index c935a19..8384b57 100644 --- a/server.c +++ b/server.c @@ -1,23 +1,25 @@ -#include +#include #include #include +#include #include #include int main(int argc, char **argv) { int listen_sock; - if ((listen_sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) + if ((listen_sock = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { perror("server: socket()"); exit(1); } - struct sockaddr_in server_addr; - server_addr.sin_family = PF_INET; - server_addr.sin_addr.s_addr = INADDR_ANY; - server_addr.sin_port = htons(8080); - if (bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) + struct sockaddr_in6 server_addr6; + memset(&server_addr6, 0, sizeof(server_addr6)); + server_addr6.sin6_family = PF_INET6; + server_addr6.sin6_port = htons(8080); + server_addr6.sin6_addr = in6addr_any; + if (bind(listen_sock, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) == -1) { perror("server: bind()"); close(listen_sock); @@ -33,10 +35,10 @@ int main(int argc, char **argv) while (1) { - unsigned int client_len; - struct sockaddr_in client_addr; + struct sockaddr_in6 client_addr6; + socklen_t addr_len = sizeof(client_addr6); int conn_sock; - if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &client_len)) == -1) + if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr6, &addr_len)) == -1) { perror("server: accept()"); close(listen_sock); From 51e3ec694f34b658fbc4442c1cb5b0dcde35eaae Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 7 Jan 2025 08:39:01 +0000 Subject: [PATCH 02/41] Modify client to maintain connection after sending data --- client.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/client.c b/client.c index 15e4170..6e769ac 100644 --- a/client.c +++ b/client.c @@ -32,26 +32,29 @@ int main(int argc, char **argv) exit(1); } - char buf[256]; - fgets(buf, sizeof(buf), stdin); // Null-terminate the string - - size_t len = strlen(buf); - if (send(sock, buf, len, 0) == -1) + while (1) { - perror("client: send()"); - close(sock); - exit(1); + char buf[256]; + fgets(buf, sizeof(buf), stdin); // Null-terminate the string + + size_t len = strlen(buf); + if (send(sock, buf, len, 0) == -1) + { + perror("client: send()"); + close(sock); + exit(1); + } + + if (recv(sock, buf, sizeof(buf) - 1, 0) == -1) + { + perror("client: recv()"); + close(sock); + exit(1); + } + + printf("%s", buf); } - if (recv(sock, buf, sizeof(buf) - 1, 0) == -1) - { - perror("client: recv()"); - close(sock); - exit(1); - } - - printf("%s", buf); - close(sock); return 0; From c7bf378337566df4a990e61f572dc03fe154bace Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 7 Jan 2025 08:49:44 +0000 Subject: [PATCH 03/41] Handle server disconnection by closing client connection and exiting --- client.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 6e769ac..820eb0f 100644 --- a/client.c +++ b/client.c @@ -38,19 +38,33 @@ int main(int argc, char **argv) fgets(buf, sizeof(buf), stdin); // Null-terminate the string size_t len = strlen(buf); - if (send(sock, buf, len, 0) == -1) + ssize_t sent_bytes = send(sock, buf, len, 0); + if (sent_bytes == -1) { perror("client: send()"); close(sock); exit(1); } + if (sent_bytes == 0) + { + printf("Connection closed by server\n"); + close(sock); + exit(0); + } - if (recv(sock, buf, sizeof(buf) - 1, 0) == -1) + ssize_t received_bytes = recv(sock, buf, sizeof(buf) - 1, 0); + if (received_bytes == -1) { perror("client: recv()"); close(sock); exit(1); } + if (received_bytes == 0) + { + printf("Connection closed by server\n"); + close(sock); + exit(0); + } printf("%s", buf); } From bf46092c724da225fe86c2d47a3703be5c71348b Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 7 Jan 2025 08:51:44 +0000 Subject: [PATCH 04/41] Implement I/O multiplexing using select() --- server.c | 119 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 98 insertions(+), 21 deletions(-) diff --git a/server.c b/server.c index 8384b57..c3ef902 100644 --- a/server.c +++ b/server.c @@ -1,10 +1,15 @@ #include +#include +#include #include #include #include +#include #include #include +#define MAX_CLIENTS 30 + int main(int argc, char **argv) { int listen_sock; @@ -14,6 +19,13 @@ int main(int argc, char **argv) exit(1); } + if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) == -1) + { + perror("server: fcntl(listen_sock)"); + close(listen_sock); + exit(1); + } + struct sockaddr_in6 server_addr6; memset(&server_addr6, 0, sizeof(server_addr6)); server_addr6.sin6_family = PF_INET6; @@ -33,44 +45,109 @@ int main(int argc, char **argv) exit(1); } + fd_set read_fds; + int client_sockets[MAX_CLIENTS] = {0}; + int max_fd; while (1) { - struct sockaddr_in6 client_addr6; - socklen_t addr_len = sizeof(client_addr6); - int conn_sock; - if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr6, &addr_len)) == -1) + FD_ZERO(&read_fds); + FD_SET(listen_sock, &read_fds); + max_fd = listen_sock; + + for (int i = 0; i < MAX_CLIENTS; i++) { - perror("server: accept()"); + int fd = client_sockets[i]; + if (fd > 0) + FD_SET(fd, &read_fds); + if (fd > max_fd) + max_fd = fd; + } + + if (select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1) // no timeout + { + perror("server: select()"); close(listen_sock); exit(1); } - char buf[256]; - ssize_t received_bytes; - while ((received_bytes = recv(conn_sock, buf, sizeof(buf) - 1, 0)) > 0) + if (FD_ISSET(listen_sock, &read_fds)) { - buf[received_bytes] = '\0'; // Null-terminate the string - if (send(conn_sock, buf, received_bytes, 0) == -1) + struct sockaddr_in6 client_addr6; + socklen_t addr_len = sizeof(client_addr6); + int conn_sock; + if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr6, &addr_len)) == -1) + { + if (errno != EAGAIN) + { + perror("server: accept()"); + close(listen_sock); + exit(1); + } + } + + char client_ip[INET6_ADDRSTRLEN]; + inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); + printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); + + for (int i = 0; i < MAX_CLIENTS; i++) { - perror("server: send()"); + if (client_sockets[i] == 0) + { + client_sockets[i] = conn_sock; + break; + } + } + } + + for (int i = 0; i < MAX_CLIENTS; i++) + { + int conn_sock = client_sockets[i]; + if (!FD_ISSET(conn_sock, &read_fds)) + continue; + + if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) + { + perror("server: fcntl(conn_sock)"); close(conn_sock); close(listen_sock); exit(1); } - } - if (received_bytes == -1) - { - perror("server: recv()"); - close(conn_sock); - close(listen_sock); - exit(1); - } + char buf[256]; + ssize_t received_bytes; + while ((received_bytes = recv(conn_sock, buf, sizeof(buf) - 1, 0)) > 0) + { + printf("received_bytes: %ld\n", received_bytes); + buf[received_bytes] = '\0'; // Null-terminate the string + if (send(conn_sock, buf, received_bytes, 0) == -1) + { + perror("server: send()"); + close(conn_sock); + close(listen_sock); + exit(1); + } + } - close(conn_sock); + if (received_bytes == -1) + { + if (errno != EAGAIN) + { + perror("server: recv()"); + close(conn_sock); + close(listen_sock); + exit(1); + } + } + if (received_bytes == 0) + { + printf("Connection closed\n"); + close(conn_sock); + client_sockets[i] = 0; + } + } } - close(listen_sock); + // close(listen_sock); return 0; } From 1835e32f90bd9fe0bf1f5a654ed47bfb66ab6d9f Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 7 Jan 2025 09:14:56 +0000 Subject: [PATCH 05/41] Close socket when max_clients limit is exceeded --- server.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/server.c b/server.c index c3ef902..1e895d2 100644 --- a/server.c +++ b/server.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -85,18 +86,28 @@ int main(int argc, char **argv) } } - char client_ip[INET6_ADDRSTRLEN]; - inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); - printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); - + bool is_client_added = false; for (int i = 0; i < MAX_CLIENTS; i++) { if (client_sockets[i] == 0) { client_sockets[i] = conn_sock; + is_client_added = true; break; } } + + if (is_client_added) + { + char client_ip[INET6_ADDRSTRLEN]; + inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); + printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); + } + else + { + printf("No more room for clients\n"); + close(conn_sock); + } } for (int i = 0; i < MAX_CLIENTS; i++) From cb52c2f09b3598fbc87849f15649dc5d8d3f0695 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 10 Jan 2025 16:07:00 +0000 Subject: [PATCH 06/41] Modify server's main function to accept port as an argument --- server.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/server.c b/server.c index 1e895d2..ee764a2 100644 --- a/server.c +++ b/server.c @@ -11,8 +11,24 @@ #define MAX_CLIENTS 30 +long parse_port(const char *port_str); + int main(int argc, char **argv) { + // Check if the port number is provided + if (argc != 2) + { + printf("Usage: %s \n", argv[0]); + exit(1); + } + + // Initialize the error number + errno = 0; + + // Parse the port number + long port = parse_port(argv[1]); + printf("Port: %ld\n", port); + int listen_sock; if ((listen_sock = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { @@ -162,3 +178,20 @@ int main(int argc, char **argv) return 0; } + +long parse_port(const char *port_str) +{ + char *endptr; + long port = strtol(port_str, &endptr, 10); + if (endptr == port_str || *endptr != '\0') + { + printf("server: cannot convert %s to a port number\n", port_str); + exit(1); + } + else if (errno == ERANGE || port <= 0 || port > 65535) + { + printf("server: port number [%s] out of range\n", port_str); + exit(1); + } + return port; +} From f5f3534b7234ea86e6ab0be4fabc8fcda798d56d Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 10 Jan 2025 16:27:02 +0000 Subject: [PATCH 07/41] Extract function declarations to utils.h --- .gitignore | 1 + Makefile | 33 +++++++++++++++++++++------------ server.c | 23 +++-------------------- utils.c | 20 ++++++++++++++++++++ utils.h | 6 ++++++ 5 files changed, 51 insertions(+), 32 deletions(-) create mode 100644 utils.c create mode 100644 utils.h diff --git a/.gitignore b/.gitignore index 9d1f796..b202a83 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ a.out client server +*.o diff --git a/Makefile b/Makefile index a8a7eeb..58d30ab 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,24 @@ -.PHONY: build-server -build-server: - gcc -Wall -Wextra -Wpedantic server.c -o server +CC = gcc +CFLAGS = -Wall -Wextra -Wpedantic -O2 -.PHONY: run-server -run-server: build-server - ./server +.PHONY: all clean server client utils -.PHONY: build-client -build-client: - gcc -Wall -Wextra -Wpedantic client.c -o client +all: server client -.PHONY: run-client -run-client:build-client - ./client +server: server.o utils.o + $(CC) $(CFLAGS) -o server server.o utils.o + +client: client.o utils.o + $(CC) $(CFLAGS) -o client client.o utils.o + +server.o: server.c + $(CC) $(CFLAGS) -c server.c + +client.o: client.c + $(CC) $(CFLAGS) -c client.c + +utils.o: utils.c + $(CC) $(CFLAGS) -c utils.c + +clean: + rm -f server client *.o diff --git a/server.c b/server.c index ee764a2..aec864d 100644 --- a/server.c +++ b/server.c @@ -9,9 +9,9 @@ #include #include -#define MAX_CLIENTS 30 +#include "utils.h" -long parse_port(const char *port_str); +#define MAX_CLIENTS 30 int main(int argc, char **argv) { @@ -46,7 +46,7 @@ int main(int argc, char **argv) struct sockaddr_in6 server_addr6; memset(&server_addr6, 0, sizeof(server_addr6)); server_addr6.sin6_family = PF_INET6; - server_addr6.sin6_port = htons(8080); + server_addr6.sin6_port = htons(port); server_addr6.sin6_addr = in6addr_any; if (bind(listen_sock, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) == -1) { @@ -178,20 +178,3 @@ int main(int argc, char **argv) return 0; } - -long parse_port(const char *port_str) -{ - char *endptr; - long port = strtol(port_str, &endptr, 10); - if (endptr == port_str || *endptr != '\0') - { - printf("server: cannot convert %s to a port number\n", port_str); - exit(1); - } - else if (errno == ERANGE || port <= 0 || port > 65535) - { - printf("server: port number [%s] out of range\n", port_str); - exit(1); - } - return port; -} diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..6459682 --- /dev/null +++ b/utils.c @@ -0,0 +1,20 @@ +#include +#include +#include + +long parse_port(const char *port_str) +{ + char *endptr; + long port = strtol(port_str, &endptr, 10); + if (endptr == port_str || *endptr != '\0') + { + printf("server: cannot convert %s to a port number\n", port_str); + exit(1); + } + else if (errno == ERANGE || port <= 0 || port > 65535) + { + printf("server: port number [%s] out of range\n", port_str); + exit(1); + } + return port; +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..e9d0aca --- /dev/null +++ b/utils.h @@ -0,0 +1,6 @@ +#ifndef UTILS_H +#define UTILS_H + +long parse_port(const char *port_str); + +#endif From 36f673c0a7991aaa92eebf5203f41d645b677625 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 10 Jan 2025 16:36:36 +0000 Subject: [PATCH 08/41] Modify Client to use parse_port() from utils.h --- client.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/client.c b/client.c index 820eb0f..1598320 100644 --- a/client.c +++ b/client.c @@ -1,12 +1,29 @@ #include +#include #include #include #include #include #include +#include "utils.h" + int main(int argc, char **argv) { + // Check if the IP address and port number are provided + if (argc != 3) + { + printf("Usage: %s \n", argv[0]); + exit(1); + } + + // Initialize the error number + errno = 0; + + // Parse the port number + long port = parse_port(argv[2]); + printf("Port: %ld\n", port); + int sock; if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { @@ -17,7 +34,7 @@ int main(int argc, char **argv) struct sockaddr_in6 server_addr6; memset(&server_addr6, 0, sizeof(server_addr6)); server_addr6.sin6_family = PF_INET6; - server_addr6.sin6_port = htons(8080); + server_addr6.sin6_port = htons(port); if (inet_pton(PF_INET6, "::1", &server_addr6.sin6_addr) <= 0) { perror("client: inet_pton()"); From c0af5394917defbe06237f1336ab81a957a15151 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 14 Jan 2025 02:45:04 +0000 Subject: [PATCH 09/41] Address review comments --- client.c | 4 ++-- server.c | 39 ++++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/client.c b/client.c index 1598320..705b6fb 100644 --- a/client.c +++ b/client.c @@ -62,7 +62,7 @@ int main(int argc, char **argv) close(sock); exit(1); } - if (sent_bytes == 0) + else if (sent_bytes == 0) { printf("Connection closed by server\n"); close(sock); @@ -76,7 +76,7 @@ int main(int argc, char **argv) close(sock); exit(1); } - if (received_bytes == 0) + else if (received_bytes == 0) { printf("Connection closed by server\n"); close(sock); diff --git a/server.c b/server.c index aec864d..952b3b1 100644 --- a/server.c +++ b/server.c @@ -63,18 +63,20 @@ int main(int argc, char **argv) } fd_set read_fds; - int client_sockets[MAX_CLIENTS] = {0}; + int client_sockets[MAX_CLIENTS]; + for (int i = 0; i < MAX_CLIENTS; i++) + client_sockets[i] = -1; int max_fd; + FD_ZERO(&read_fds); while (1) { - FD_ZERO(&read_fds); FD_SET(listen_sock, &read_fds); max_fd = listen_sock; for (int i = 0; i < MAX_CLIENTS; i++) { int fd = client_sockets[i]; - if (fd > 0) + if (fd >= 0) FD_SET(fd, &read_fds); if (fd > max_fd) max_fd = fd; @@ -94,7 +96,11 @@ int main(int argc, char **argv) int conn_sock; if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr6, &addr_len)) == -1) { - if (errno != EAGAIN) + if (errno == EAGAIN) + { + continue; + } + else { perror("server: accept()"); close(listen_sock); @@ -102,10 +108,19 @@ int main(int argc, char **argv) } } + // Set the connection socket to non-blocking + if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) + { + perror("server: fcntl(conn_sock)"); + close(conn_sock); + close(listen_sock); + exit(1); + } + bool is_client_added = false; for (int i = 0; i < MAX_CLIENTS; i++) { - if (client_sockets[i] == 0) + if (client_sockets[i] == -1) { client_sockets[i] = conn_sock; is_client_added = true; @@ -129,17 +144,11 @@ int main(int argc, char **argv) for (int i = 0; i < MAX_CLIENTS; i++) { int conn_sock = client_sockets[i]; + if (conn_sock == -1) + continue; if (!FD_ISSET(conn_sock, &read_fds)) continue; - if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) - { - perror("server: fcntl(conn_sock)"); - close(conn_sock); - close(listen_sock); - exit(1); - } - char buf[256]; ssize_t received_bytes; while ((received_bytes = recv(conn_sock, buf, sizeof(buf) - 1, 0)) > 0) @@ -165,11 +174,11 @@ int main(int argc, char **argv) exit(1); } } - if (received_bytes == 0) + else if (received_bytes == 0) { printf("Connection closed\n"); close(conn_sock); - client_sockets[i] = 0; + client_sockets[i] = -1; } } } From e63117e7b45f07bb339f79077d6cf675448e1708 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 14 Jan 2025 04:56:02 +0000 Subject: [PATCH 10/41] Extract client handling logic into handle_client() function --- server.c | 67 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/server.c b/server.c index 952b3b1..d4d6c02 100644 --- a/server.c +++ b/server.c @@ -13,6 +13,41 @@ #define MAX_CLIENTS 30 +void handle_client(int client_sock, int *client_sockets, int index) +{ + char buf[256]; + ssize_t received_bytes; + while ((received_bytes = recv(client_sock, buf, sizeof(buf) - 1, 0)) > 0) + { + printf("received_bytes: %ld\n", received_bytes); + buf[received_bytes] = '\0'; // Null-terminate the string + if (send(client_sock, buf, received_bytes, 0) == -1) + { + perror("server: send()"); + close(client_sock); + client_sockets[index] = -1; + exit(1); + } + } + + if (received_bytes == -1) + { + if (errno != EAGAIN) + { + perror("server: recv()"); + close(client_sock); + client_sockets[index] = -1; + exit(1); + } + } + else if (received_bytes == 0) + { + printf("Connection closed\n"); + close(client_sock); + client_sockets[index] = -1; + } +} + int main(int argc, char **argv) { // Check if the port number is provided @@ -149,37 +184,7 @@ int main(int argc, char **argv) if (!FD_ISSET(conn_sock, &read_fds)) continue; - char buf[256]; - ssize_t received_bytes; - while ((received_bytes = recv(conn_sock, buf, sizeof(buf) - 1, 0)) > 0) - { - printf("received_bytes: %ld\n", received_bytes); - buf[received_bytes] = '\0'; // Null-terminate the string - if (send(conn_sock, buf, received_bytes, 0) == -1) - { - perror("server: send()"); - close(conn_sock); - close(listen_sock); - exit(1); - } - } - - if (received_bytes == -1) - { - if (errno != EAGAIN) - { - perror("server: recv()"); - close(conn_sock); - close(listen_sock); - exit(1); - } - } - else if (received_bytes == 0) - { - printf("Connection closed\n"); - close(conn_sock); - client_sockets[i] = -1; - } + handle_client(conn_sock, client_sockets, i); } } From 71f16b26c55a7958d8bd144c4026de593bddbb98 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 14 Jan 2025 04:57:39 +0000 Subject: [PATCH 11/41] Define BUFFER_SIZE --- client.c | 4 +++- server.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 705b6fb..a788f69 100644 --- a/client.c +++ b/client.c @@ -8,6 +8,8 @@ #include "utils.h" +#define BUFFER_SIZE 256 + int main(int argc, char **argv) { // Check if the IP address and port number are provided @@ -51,7 +53,7 @@ int main(int argc, char **argv) while (1) { - char buf[256]; + char buf[BUFFER_SIZE]; fgets(buf, sizeof(buf), stdin); // Null-terminate the string size_t len = strlen(buf); diff --git a/server.c b/server.c index d4d6c02..8b7d897 100644 --- a/server.c +++ b/server.c @@ -11,11 +11,12 @@ #include "utils.h" +#define BUFFER_SIZE 256 #define MAX_CLIENTS 30 void handle_client(int client_sock, int *client_sockets, int index) { - char buf[256]; + char buf[BUFFER_SIZE]; ssize_t received_bytes; while ((received_bytes = recv(client_sock, buf, sizeof(buf) - 1, 0)) > 0) { From 6d6bd01ac37fc64664732069eb01c02d6f134559 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 14 Jan 2025 07:22:36 +0000 Subject: [PATCH 12/41] Use epoll() instead of select() --- server.c | 161 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 63 deletions(-) diff --git a/server.c b/server.c index 8b7d897..f624b05 100644 --- a/server.c +++ b/server.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #define BUFFER_SIZE 256 #define MAX_CLIENTS 30 +#define MAX_EVENTS 10 void handle_client(int client_sock, int *client_sockets, int index) { @@ -98,98 +100,131 @@ int main(int argc, char **argv) exit(1); } - fd_set read_fds; + // Create an epoll instance + int epoll_fd = epoll_create1(0); + if (epoll_fd == -1) + { + perror("server: epoll_create1()"); + close(listen_sock); + exit(1); + } + + struct epoll_event event; + event.events = EPOLLIN; + event.data.fd = listen_sock; + + // Add the listen socket to the epoll instance + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &event) == -1) + { + perror("server: epoll_ctl()"); + close(listen_sock); + close(epoll_fd); + exit(1); + } + + struct epoll_event events[MAX_EVENTS]; int client_sockets[MAX_CLIENTS]; for (int i = 0; i < MAX_CLIENTS; i++) client_sockets[i] = -1; - int max_fd; - FD_ZERO(&read_fds); + while (1) { - FD_SET(listen_sock, &read_fds); - max_fd = listen_sock; - - for (int i = 0; i < MAX_CLIENTS; i++) + // Wait for events indefinitely + int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); + if (nfds == -1) { - int fd = client_sockets[i]; - if (fd >= 0) - FD_SET(fd, &read_fds); - if (fd > max_fd) - max_fd = fd; - } - - if (select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1) // no timeout - { - perror("server: select()"); + perror("server: epoll_wait()"); close(listen_sock); + close(epoll_fd); exit(1); } - if (FD_ISSET(listen_sock, &read_fds)) + for (int i = 0; i < nfds; i++) { - struct sockaddr_in6 client_addr6; - socklen_t addr_len = sizeof(client_addr6); - int conn_sock; - if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr6, &addr_len)) == -1) + // Check if the event is for the listen socket + if (events[i].data.fd == listen_sock) { - if (errno == EAGAIN) + struct sockaddr_in6 client_addr6; + socklen_t addr_len = sizeof(client_addr6); + int conn_sock; + if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr6, &addr_len)) == -1) { - continue; + if (errno == EAGAIN) + { + continue; + } + else + { + perror("server: accept()"); + close(listen_sock); + close(epoll_fd); + exit(1); + } } - else + + // Set the connection socket to non-blocking + if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) { - perror("server: accept()"); + perror("server: fcntl(conn_sock)"); + close(conn_sock); close(listen_sock); + close(epoll_fd); exit(1); } - } - // Set the connection socket to non-blocking - if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) - { - perror("server: fcntl(conn_sock)"); - close(conn_sock); - close(listen_sock); - exit(1); - } - - bool is_client_added = false; - for (int i = 0; i < MAX_CLIENTS; i++) - { - if (client_sockets[i] == -1) + bool is_client_added = false; + for (int j = 0; j < MAX_CLIENTS; j++) { - client_sockets[i] = conn_sock; - is_client_added = true; - break; + if (client_sockets[j] == -1) + { + client_sockets[j] = conn_sock; + is_client_added = true; + + event.events = EPOLLIN | EPOLLET; + event.data.fd = conn_sock; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &event) == -1) + { + perror("server: epoll_ctl()"); + close(conn_sock); + close(listen_sock); + close(epoll_fd); + exit(1); + } + + break; + } } - } - if (is_client_added) - { - char client_ip[INET6_ADDRSTRLEN]; - inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); - printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); + if (is_client_added) + { + char client_ip[INET6_ADDRSTRLEN]; + inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); + printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); + } + else + { + printf("No more room for clients\n"); + close(conn_sock); + } } + // Check if the event is for a client socket else { - printf("No more room for clients\n"); - close(conn_sock); + int conn_sock = events[i].data.fd; + for (int j = 0; j < MAX_CLIENTS; j++) + { + if (client_sockets[j] == conn_sock) + { + handle_client(conn_sock, client_sockets, j); + break; + } + } } } - - for (int i = 0; i < MAX_CLIENTS; i++) - { - int conn_sock = client_sockets[i]; - if (conn_sock == -1) - continue; - if (!FD_ISSET(conn_sock, &read_fds)) - continue; - - handle_client(conn_sock, client_sockets, i); - } } - // close(listen_sock); + close(listen_sock); + close(epoll_fd); return 0; } From 1aa9a74254488561c79da4a1fa84fe236be167bc Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 14 Jan 2025 07:51:21 +0000 Subject: [PATCH 13/41] Use stack to manage client sockets efficiently --- server.c | 64 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/server.c b/server.c index f624b05..dc54af1 100644 --- a/server.c +++ b/server.c @@ -101,8 +101,8 @@ int main(int argc, char **argv) } // Create an epoll instance - int epoll_fd = epoll_create1(0); - if (epoll_fd == -1) + int epoll_fd; + if ((epoll_fd = epoll_create1(0)) == -1) { perror("server: epoll_create1()"); close(listen_sock); @@ -123,9 +123,17 @@ int main(int argc, char **argv) } struct epoll_event events[MAX_EVENTS]; + // Manage the client sockets using stack int client_sockets[MAX_CLIENTS]; + int free_indices[MAX_CLIENTS]; + int free_index_top = MAX_CLIENTS - 1; + + // Initialize the client sockets and free indices for (int i = 0; i < MAX_CLIENTS; i++) + { client_sockets[i] = -1; + free_indices[i] = i; + } while (1) { @@ -172,50 +180,46 @@ int main(int argc, char **argv) exit(1); } - bool is_client_added = false; - for (int j = 0; j < MAX_CLIENTS; j++) + // Fulfilled the maximum number of clients + if (free_index_top == -1) { - if (client_sockets[j] == -1) - { - client_sockets[j] = conn_sock; - is_client_added = true; - - event.events = EPOLLIN | EPOLLET; - event.data.fd = conn_sock; - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &event) == -1) - { - perror("server: epoll_ctl()"); - close(conn_sock); - close(listen_sock); - close(epoll_fd); - exit(1); - } - - break; - } + printf("No more room for clients\n"); + close(conn_sock); + continue; } - if (is_client_added) - { - char client_ip[INET6_ADDRSTRLEN]; - inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); - printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); - } - else + int index = free_indices[free_index_top--]; + client_sockets[index] = conn_sock; + + event.events = EPOLLIN | EPOLLET; + event.data.fd = conn_sock; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &event) == -1) { - printf("No more room for clients\n"); + perror("server: epoll_ctl()"); close(conn_sock); + close(listen_sock); + close(epoll_fd); + exit(1); } + + char client_ip[INET6_ADDRSTRLEN]; + inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); + printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); } // Check if the event is for a client socket else { int conn_sock = events[i].data.fd; + // TODO: Manage the client socket using hash table for (int j = 0; j < MAX_CLIENTS; j++) { if (client_sockets[j] == conn_sock) { handle_client(conn_sock, client_sockets, j); + if (client_sockets[j] == -1) + { + free_indices[++free_index_top] = j; + } break; } } From d72645407c216c7d47280417627f83c94615d5fa Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 14 Jan 2025 08:30:23 +0000 Subject: [PATCH 14/41] Extract new connection handling logic into handle_new_connection() function --- server.c | 113 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 53 deletions(-) diff --git a/server.c b/server.c index dc54af1..d3c02f6 100644 --- a/server.c +++ b/server.c @@ -16,6 +16,65 @@ #define MAX_CLIENTS 30 #define MAX_EVENTS 10 +void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top) +{ + struct sockaddr_in6 client_addr6; + socklen_t addr_len = sizeof(client_addr6); + int conn_sock; + + if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr6, &addr_len)) == -1) + { + if (errno == EAGAIN) + { + return; + } + else + { + perror("server: accept()"); + close(listen_sock); + close(epoll_fd); + exit(1); + } + } + + // Set the connection socket to non-blocking mode + if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) + { + perror("server: fcntl(conn_sock)"); + close(conn_sock); + close(listen_sock); + close(epoll_fd); + exit(1); + } + + // Fulfilled the maximum number of clients + if (*free_index_top == -1) + { + printf("No more room for clients\n"); + close(conn_sock); + return; + } + + int index = free_indices[(*free_index_top)--]; + client_sockets[index] = conn_sock; + + struct epoll_event event; + event.events = EPOLLIN | EPOLLET; + event.data.fd = conn_sock; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &event) == -1) + { + perror("server: epoll_ctl()"); + close(conn_sock); + close(listen_sock); + close(epoll_fd); + exit(1); + } + + char client_ip[INET6_ADDRSTRLEN]; + inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); + printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); +} + void handle_client(int client_sock, int *client_sockets, int index) { char buf[BUFFER_SIZE]; @@ -152,59 +211,7 @@ int main(int argc, char **argv) // Check if the event is for the listen socket if (events[i].data.fd == listen_sock) { - struct sockaddr_in6 client_addr6; - socklen_t addr_len = sizeof(client_addr6); - int conn_sock; - if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr6, &addr_len)) == -1) - { - if (errno == EAGAIN) - { - continue; - } - else - { - perror("server: accept()"); - close(listen_sock); - close(epoll_fd); - exit(1); - } - } - - // Set the connection socket to non-blocking - if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) - { - perror("server: fcntl(conn_sock)"); - close(conn_sock); - close(listen_sock); - close(epoll_fd); - exit(1); - } - - // Fulfilled the maximum number of clients - if (free_index_top == -1) - { - printf("No more room for clients\n"); - close(conn_sock); - continue; - } - - int index = free_indices[free_index_top--]; - client_sockets[index] = conn_sock; - - event.events = EPOLLIN | EPOLLET; - event.data.fd = conn_sock; - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &event) == -1) - { - perror("server: epoll_ctl()"); - close(conn_sock); - close(listen_sock); - close(epoll_fd); - exit(1); - } - - char client_ip[INET6_ADDRSTRLEN]; - inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); - printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); + handle_new_connection(listen_sock, epoll_fd, client_sockets, free_indices, &free_index_top); } // Check if the event is for a client socket else From 3b1365dea0cabd1b5c77bdfce22683d28583994d Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 14 Jan 2025 09:51:58 +0000 Subject: [PATCH 15/41] Declare function signature before main --- server.c | 189 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 96 insertions(+), 93 deletions(-) diff --git a/server.c b/server.c index d3c02f6..4c87d00 100644 --- a/server.c +++ b/server.c @@ -16,99 +16,8 @@ #define MAX_CLIENTS 30 #define MAX_EVENTS 10 -void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top) -{ - struct sockaddr_in6 client_addr6; - socklen_t addr_len = sizeof(client_addr6); - int conn_sock; - - if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr6, &addr_len)) == -1) - { - if (errno == EAGAIN) - { - return; - } - else - { - perror("server: accept()"); - close(listen_sock); - close(epoll_fd); - exit(1); - } - } - - // Set the connection socket to non-blocking mode - if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) - { - perror("server: fcntl(conn_sock)"); - close(conn_sock); - close(listen_sock); - close(epoll_fd); - exit(1); - } - - // Fulfilled the maximum number of clients - if (*free_index_top == -1) - { - printf("No more room for clients\n"); - close(conn_sock); - return; - } - - int index = free_indices[(*free_index_top)--]; - client_sockets[index] = conn_sock; - - struct epoll_event event; - event.events = EPOLLIN | EPOLLET; - event.data.fd = conn_sock; - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &event) == -1) - { - perror("server: epoll_ctl()"); - close(conn_sock); - close(listen_sock); - close(epoll_fd); - exit(1); - } - - char client_ip[INET6_ADDRSTRLEN]; - inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); - printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); -} - -void handle_client(int client_sock, int *client_sockets, int index) -{ - char buf[BUFFER_SIZE]; - ssize_t received_bytes; - while ((received_bytes = recv(client_sock, buf, sizeof(buf) - 1, 0)) > 0) - { - printf("received_bytes: %ld\n", received_bytes); - buf[received_bytes] = '\0'; // Null-terminate the string - if (send(client_sock, buf, received_bytes, 0) == -1) - { - perror("server: send()"); - close(client_sock); - client_sockets[index] = -1; - exit(1); - } - } - - if (received_bytes == -1) - { - if (errno != EAGAIN) - { - perror("server: recv()"); - close(client_sock); - client_sockets[index] = -1; - exit(1); - } - } - else if (received_bytes == 0) - { - printf("Connection closed\n"); - close(client_sock); - client_sockets[index] = -1; - } -} +void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top); +void handle_client(int client_sock, int *client_sockets, int index); int main(int argc, char **argv) { @@ -239,3 +148,97 @@ int main(int argc, char **argv) return 0; } + +void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top) +{ + struct sockaddr_in6 client_addr6; + socklen_t addr_len = sizeof(client_addr6); + int conn_sock; + + if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr6, &addr_len)) == -1) + { + if (errno == EAGAIN) + { + return; + } + else + { + perror("server: accept()"); + close(listen_sock); + close(epoll_fd); + exit(1); + } + } + + // Set the connection socket to non-blocking mode + if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) + { + perror("server: fcntl(conn_sock)"); + close(conn_sock); + close(listen_sock); + close(epoll_fd); + exit(1); + } + + // Fulfilled the maximum number of clients + if (*free_index_top == -1) + { + printf("No more room for clients\n"); + close(conn_sock); + return; + } + + int index = free_indices[(*free_index_top)--]; + client_sockets[index] = conn_sock; + + struct epoll_event event; + event.events = EPOLLIN | EPOLLET; + event.data.fd = conn_sock; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &event) == -1) + { + perror("server: epoll_ctl()"); + close(conn_sock); + close(listen_sock); + close(epoll_fd); + exit(1); + } + + char client_ip[INET6_ADDRSTRLEN]; + inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); + printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); +} + +void handle_client(int client_sock, int *client_sockets, int index) +{ + char buf[BUFFER_SIZE]; + ssize_t received_bytes; + while ((received_bytes = recv(client_sock, buf, sizeof(buf) - 1, 0)) > 0) + { + printf("received_bytes: %ld\n", received_bytes); + buf[received_bytes] = '\0'; // Null-terminate the string + if (send(client_sock, buf, received_bytes, 0) == -1) + { + perror("server: send()"); + close(client_sock); + client_sockets[index] = -1; + exit(1); + } + } + + if (received_bytes == -1) + { + if (errno != EAGAIN) + { + perror("server: recv()"); + close(client_sock); + client_sockets[index] = -1; + exit(1); + } + } + else if (received_bytes == 0) + { + printf("Connection closed\n"); + close(client_sock); + client_sockets[index] = -1; + } +} From 3554dc51e9656615477d9c27cb83885eb7c3ba81 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Tue, 14 Jan 2025 11:05:55 +0000 Subject: [PATCH 16/41] Add support for both IPv4 and IPv6 --- server.c | 179 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 140 insertions(+), 39 deletions(-) diff --git a/server.c b/server.c index 4c87d00..1f5382e 100644 --- a/server.c +++ b/server.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "utils.h" @@ -35,59 +36,143 @@ int main(int argc, char **argv) long port = parse_port(argv[1]); printf("Port: %ld\n", port); - int listen_sock; - if ((listen_sock = socket(PF_INET6, SOCK_STREAM, 0)) == -1) - { - perror("server: socket()"); - exit(1); - } + struct addrinfo hints, *res, *res0; + int listen_sock_v4 = -1; + int listen_sock_v6 = -1; + int yes = 1; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; // Allow IPv4 or IPv6 + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; // For wildcard IP address - if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) == -1) + if (getaddrinfo(NULL, argv[1], &hints, &res0) != 0) { - perror("server: fcntl(listen_sock)"); - close(listen_sock); + perror("server: getaddrinfo()"); exit(1); } - struct sockaddr_in6 server_addr6; - memset(&server_addr6, 0, sizeof(server_addr6)); - server_addr6.sin6_family = PF_INET6; - server_addr6.sin6_port = htons(port); - server_addr6.sin6_addr = in6addr_any; - if (bind(listen_sock, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) == -1) + for (res = res0; res != NULL; res = res->ai_next) { - perror("server: bind()"); - close(listen_sock); - exit(1); + int listen_sock; + if ((listen_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) + { + perror("server: socket()"); + continue; + } + + // Set the socket option to reuse the address + setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + + if (res->ai_family == PF_INET6) + { + // Set the socket option to allow only IPv6 connections + if (setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(int)) == -1) + { + perror("server: setsockopt()"); + close(listen_sock); + continue; + }; + } + + if (bind(listen_sock, res->ai_addr, res->ai_addrlen) == -1) + { + perror("server: bind()"); + close(listen_sock); + continue; + } + + if (listen(listen_sock, 5) == -1) + { + perror("server: listen()"); + close(listen_sock); + exit(1); + } + + if (res->ai_family == PF_INET) + { + listen_sock_v4 = listen_sock; + } + else if (res->ai_family == PF_INET6) + { + listen_sock_v6 = listen_sock; + } } - if (listen(listen_sock, 0) == -1) + freeaddrinfo(res0); + + if (listen_sock_v4 == -1 && listen_sock_v6 == -1) { - perror("server: listen()"); - close(listen_sock); + printf("server: failed to bind any sockets\n"); exit(1); } + printf("listen_sock_v4: %d\n", listen_sock_v4); + printf("listen_sock_v6: %d\n", listen_sock_v6); + // Create an epoll instance int epoll_fd; if ((epoll_fd = epoll_create1(0)) == -1) { perror("server: epoll_create1()"); - close(listen_sock); + if (listen_sock_v4 != -1) + close(listen_sock_v4); + if (listen_sock_v6 != -1) + close(listen_sock_v6); exit(1); } struct epoll_event event; - event.events = EPOLLIN; - event.data.fd = listen_sock; + if (listen_sock_v4 != -1) + { + if (fcntl(listen_sock_v4, F_SETFL, O_NONBLOCK) == -1) + { + perror("server: fcntl(listen_sock_v4)"); + close(listen_sock_v4); + if (listen_sock_v6 != -1) + close(listen_sock_v6); + close(epoll_fd); + exit(1); + } + event.events = EPOLLIN; + event.data.fd = listen_sock_v4; - // Add the listen socket to the epoll instance - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &event) == -1) + // Add the listen socket to the epoll instance + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock_v4, &event) == -1) + { + perror("server: epoll_ctl(listen_sock_v4)"); + close(listen_sock_v4); + if (listen_sock_v6 != -1) + close(listen_sock_v6); + close(epoll_fd); + exit(1); + } + } + + if (listen_sock_v6 != -1) { - perror("server: epoll_ctl()"); - close(listen_sock); - close(epoll_fd); - exit(1); + if (fcntl(listen_sock_v6, F_SETFL, O_NONBLOCK) == -1) + { + perror("server: fcntl(listen_sock_v6)"); + close(listen_sock_v6); + if (listen_sock_v4 != -1) + close(listen_sock_v4); + close(epoll_fd); + exit(1); + } + event.events = EPOLLIN; + event.data.fd = listen_sock_v6; + + // Add the listen socket to the epoll instance + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock_v6, &event) == -1) + { + perror("server: epoll_ctl(listen_sock_v6)"); + close(listen_sock_v6); + if (listen_sock_v4 != -1) + close(listen_sock_v4); + close(epoll_fd); + exit(1); + } } struct epoll_event events[MAX_EVENTS]; @@ -110,7 +195,10 @@ int main(int argc, char **argv) if (nfds == -1) { perror("server: epoll_wait()"); - close(listen_sock); + if (listen_sock_v4 != -1) + close(listen_sock_v4); + if (listen_sock_v6 != -1) + close(listen_sock_v6); close(epoll_fd); exit(1); } @@ -118,9 +206,9 @@ int main(int argc, char **argv) for (int i = 0; i < nfds; i++) { // Check if the event is for the listen socket - if (events[i].data.fd == listen_sock) + if (events[i].data.fd == listen_sock_v4 || events[i].data.fd == listen_sock_v6) { - handle_new_connection(listen_sock, epoll_fd, client_sockets, free_indices, &free_index_top); + handle_new_connection(events[i].data.fd, epoll_fd, client_sockets, free_indices, &free_index_top); } // Check if the event is for a client socket else @@ -143,7 +231,10 @@ int main(int argc, char **argv) } } - close(listen_sock); + if (listen_sock_v4 != -1) + close(listen_sock_v4); + if (listen_sock_v6 != -1) + close(listen_sock_v6); close(epoll_fd); return 0; @@ -151,11 +242,11 @@ int main(int argc, char **argv) void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top) { - struct sockaddr_in6 client_addr6; - socklen_t addr_len = sizeof(client_addr6); + struct sockaddr_storage client_addr; + socklen_t addr_len = sizeof(client_addr); int conn_sock; - if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr6, &addr_len)) == -1) + if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &addr_len)) == -1) { if (errno == EAGAIN) { @@ -204,8 +295,18 @@ void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, i } char client_ip[INET6_ADDRSTRLEN]; - inet_ntop(PF_INET6, &client_addr6.sin6_addr, client_ip, sizeof(client_ip)); - printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6.sin6_port)); + if (client_addr.ss_family == PF_INET) + { + struct sockaddr_in *client_addr4 = (struct sockaddr_in *)&client_addr; + inet_ntop(PF_INET, &client_addr4->sin_addr, client_ip, sizeof(client_ip)); + printf("Connection from %s, %d\n", client_ip, ntohs(client_addr4->sin_port)); + } + else if (client_addr.ss_family == PF_INET6) + { + struct sockaddr_in6 *client_addr6 = (struct sockaddr_in6 *)&client_addr; + inet_ntop(PF_INET6, &client_addr6->sin6_addr, client_ip, sizeof(client_ip)); + printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6->sin6_port)); + } } void handle_client(int client_sock, int *client_sockets, int index) From 435b1d0be609785e42e1a95d430733c66cba9f3d Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Wed, 15 Jan 2025 08:51:20 +0000 Subject: [PATCH 17/41] Modify client to connect both IPv4 and IPv6 address --- client.c | 69 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/client.c b/client.c index a788f69..d0a3c82 100644 --- a/client.c +++ b/client.c @@ -19,38 +19,71 @@ int main(int argc, char **argv) exit(1); } - // Initialize the error number - errno = 0; + // Parse the IP address + struct sockaddr_in server_addr4; + memset(&server_addr4, 0, sizeof(server_addr4)); + int is_ipv4 = inet_pton(PF_INET, argv[1], &server_addr4.sin_addr); + + struct sockaddr_in6 server_addr6; + memset(&server_addr6, 0, sizeof(server_addr6)); + int is_ipv6 = inet_pton(PF_INET6, argv[1], &server_addr6.sin6_addr); + + if (is_ipv4 <= 0 && is_ipv6 <= 0) + { + printf("Invalid IP address: %s\n", argv[1]); + exit(1); + } // Parse the port number long port = parse_port(argv[2]); printf("Port: %ld\n", port); + // Create a socket and connect to the server int sock; - if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) == -1) + if (is_ipv4 == 1) { - perror("client: socket()"); - exit(1); - } + if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) + { + perror("client: socket()"); + exit(1); + } - struct sockaddr_in6 server_addr6; - memset(&server_addr6, 0, sizeof(server_addr6)); - server_addr6.sin6_family = PF_INET6; - server_addr6.sin6_port = htons(port); - if (inet_pton(PF_INET6, "::1", &server_addr6.sin6_addr) <= 0) - { - perror("client: inet_pton()"); - close(sock); - exit(1); + server_addr4.sin_family = PF_INET; + server_addr4.sin_port = htons(port); + + if (connect(sock, (struct sockaddr *)&server_addr4, sizeof(server_addr4)) == -1) + { + perror("client: connect() using IPv4"); + close(sock); + exit(1); + } } + else if (is_ipv6 == 1) + { + if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) == -1) + { + perror("client: socket() using IPv6"); + exit(1); + } + + server_addr6.sin6_family = PF_INET6; + server_addr6.sin6_port = htons(port); - if (connect(sock, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) == -1) + if (connect(sock, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) == -1) + { + perror("client: connect() using IPv6"); + close(sock); + exit(1); + } + } + else { - perror("client: connect()"); - close(sock); + puts("Reached the unreachable"); exit(1); } + printf("Connected to %s, %ld\n", argv[1], port); + while (1) { char buf[BUFFER_SIZE]; From 43d73edebe65dd00f39d0278f58a317e2ca68d81 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Wed, 15 Jan 2025 09:13:38 +0000 Subject: [PATCH 18/41] Extract socket creation and connection handling logic into create_connected_socket() function --- client.c | 105 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/client.c b/client.c index d0a3c82..dc23b59 100644 --- a/client.c +++ b/client.c @@ -10,6 +10,8 @@ #define BUFFER_SIZE 256 +int create_connected_socket(const char *ip, const char *port); + int main(int argc, char **argv) { // Check if the IP address and port number are provided @@ -19,23 +21,68 @@ int main(int argc, char **argv) exit(1); } + // Create a connected socket + int sock = create_connected_socket(argv[1], argv[2]); + + while (1) + { + char buf[BUFFER_SIZE]; + fgets(buf, sizeof(buf), stdin); // Null-terminate the string + + size_t len = strlen(buf); + ssize_t sent_bytes = send(sock, buf, len, 0); + if (sent_bytes == -1) + { + perror("client: send()"); + close(sock); + exit(1); + } + else if (sent_bytes == 0) + { + printf("Connection closed by server\n"); + close(sock); + exit(0); + } + + ssize_t received_bytes = recv(sock, buf, sizeof(buf) - 1, 0); + if (received_bytes == -1) + { + perror("client: recv()"); + close(sock); + exit(1); + } + else if (received_bytes == 0) + { + printf("Connection closed by server\n"); + close(sock); + exit(0); + } + + printf("%s", buf); + } + + close(sock); + + return 0; +} + +int create_connected_socket(const char *ip, const char *port_str) +{ // Parse the IP address struct sockaddr_in server_addr4; - memset(&server_addr4, 0, sizeof(server_addr4)); - int is_ipv4 = inet_pton(PF_INET, argv[1], &server_addr4.sin_addr); - struct sockaddr_in6 server_addr6; + memset(&server_addr4, 0, sizeof(server_addr4)); memset(&server_addr6, 0, sizeof(server_addr6)); - int is_ipv6 = inet_pton(PF_INET6, argv[1], &server_addr6.sin6_addr); - + int is_ipv4 = inet_pton(PF_INET, ip, &server_addr4.sin_addr); + int is_ipv6 = inet_pton(PF_INET6, ip, &server_addr6.sin6_addr); if (is_ipv4 <= 0 && is_ipv6 <= 0) { - printf("Invalid IP address: %s\n", argv[1]); + printf("Invalid IP address: %s\n", ip); exit(1); } // Parse the port number - long port = parse_port(argv[2]); + long port = parse_port(port_str); printf("Port: %ld\n", port); // Create a socket and connect to the server @@ -82,46 +129,6 @@ int main(int argc, char **argv) exit(1); } - printf("Connected to %s, %ld\n", argv[1], port); - - while (1) - { - char buf[BUFFER_SIZE]; - fgets(buf, sizeof(buf), stdin); // Null-terminate the string - - size_t len = strlen(buf); - ssize_t sent_bytes = send(sock, buf, len, 0); - if (sent_bytes == -1) - { - perror("client: send()"); - close(sock); - exit(1); - } - else if (sent_bytes == 0) - { - printf("Connection closed by server\n"); - close(sock); - exit(0); - } - - ssize_t received_bytes = recv(sock, buf, sizeof(buf) - 1, 0); - if (received_bytes == -1) - { - perror("client: recv()"); - close(sock); - exit(1); - } - else if (received_bytes == 0) - { - printf("Connection closed by server\n"); - close(sock); - exit(0); - } - - printf("%s", buf); - } - - close(sock); - - return 0; + printf("Connected to %s, %ld\n", ip, port); + return sock; } From 388a516d77c4295d9be522ea580ca10ab21efff1 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Thu, 16 Jan 2025 06:27:29 +0000 Subject: [PATCH 19/41] Modify parse_port() to return int type --- client.c | 29 +++++++++++++++++++---------- server.c | 9 +++++++-- utils.c | 15 +++++---------- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/client.c b/client.c index dc23b59..14b8d76 100644 --- a/client.c +++ b/client.c @@ -22,7 +22,12 @@ int main(int argc, char **argv) } // Create a connected socket - int sock = create_connected_socket(argv[1], argv[2]); + int sock; + if ((sock = create_connected_socket(argv[1], argv[2])) == -1) + { + puts("Failed to create a connected socket\n"); + exit(1); + } while (1) { @@ -78,12 +83,16 @@ int create_connected_socket(const char *ip, const char *port_str) if (is_ipv4 <= 0 && is_ipv6 <= 0) { printf("Invalid IP address: %s\n", ip); - exit(1); + return -1; } // Parse the port number - long port = parse_port(port_str); - printf("Port: %ld\n", port); + int port; + if ((port = parse_port(port_str)) == -1) + { + printf("Invalid port number: %s\n", port_str); + return -1; + } // Create a socket and connect to the server int sock; @@ -92,7 +101,7 @@ int create_connected_socket(const char *ip, const char *port_str) if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("client: socket()"); - exit(1); + return -1; } server_addr4.sin_family = PF_INET; @@ -102,7 +111,7 @@ int create_connected_socket(const char *ip, const char *port_str) { perror("client: connect() using IPv4"); close(sock); - exit(1); + return -1; } } else if (is_ipv6 == 1) @@ -110,7 +119,7 @@ int create_connected_socket(const char *ip, const char *port_str) if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { perror("client: socket() using IPv6"); - exit(1); + return -1; } server_addr6.sin6_family = PF_INET6; @@ -120,15 +129,15 @@ int create_connected_socket(const char *ip, const char *port_str) { perror("client: connect() using IPv6"); close(sock); - exit(1); + return -1; } } else { puts("Reached the unreachable"); - exit(1); + return -1; } - printf("Connected to %s, %ld\n", ip, port); + printf("Connected to %s, %d\n", ip, port); return sock; } diff --git a/server.c b/server.c index 1f5382e..0842bb0 100644 --- a/server.c +++ b/server.c @@ -33,8 +33,13 @@ int main(int argc, char **argv) errno = 0; // Parse the port number - long port = parse_port(argv[1]); - printf("Port: %ld\n", port); + int port; + if ((port = parse_port(argv[1])) == -1) + { + printf("Invalid port number: %s\n", argv[1]); + exit(1); + } + printf("Port: %d\n", port); struct addrinfo hints, *res, *res0; int listen_sock_v4 = -1; diff --git a/utils.c b/utils.c index 6459682..9c59ebe 100644 --- a/utils.c +++ b/utils.c @@ -2,19 +2,14 @@ #include #include -long parse_port(const char *port_str) +int parse_port(const char *port_str) { char *endptr; + errno = 0; long port = strtol(port_str, &endptr, 10); - if (endptr == port_str || *endptr != '\0') + if (endptr == port_str || *endptr != '\0' || errno == ERANGE || port <= 0 || port > 65535) { - printf("server: cannot convert %s to a port number\n", port_str); - exit(1); + return -1; } - else if (errno == ERANGE || port <= 0 || port > 65535) - { - printf("server: port number [%s] out of range\n", port_str); - exit(1); - } - return port; + return (int)port; } From 1667b66f23db69f082871bcd732fe6a0300b3715 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Thu, 16 Jan 2025 06:29:16 +0000 Subject: [PATCH 20/41] Use BUFFER_SIZE instead of sizeof(buf) --- client.c | 4 ++-- server.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client.c b/client.c index 14b8d76..e61b339 100644 --- a/client.c +++ b/client.c @@ -32,7 +32,7 @@ int main(int argc, char **argv) while (1) { char buf[BUFFER_SIZE]; - fgets(buf, sizeof(buf), stdin); // Null-terminate the string + fgets(buf, BUFFER_SIZE, stdin); // Null-terminate the string size_t len = strlen(buf); ssize_t sent_bytes = send(sock, buf, len, 0); @@ -49,7 +49,7 @@ int main(int argc, char **argv) exit(0); } - ssize_t received_bytes = recv(sock, buf, sizeof(buf) - 1, 0); + ssize_t received_bytes = recv(sock, buf, BUFFER_SIZE - 1, 0); if (received_bytes == -1) { perror("client: recv()"); diff --git a/server.c b/server.c index 0842bb0..b5a31f1 100644 --- a/server.c +++ b/server.c @@ -318,7 +318,7 @@ void handle_client(int client_sock, int *client_sockets, int index) { char buf[BUFFER_SIZE]; ssize_t received_bytes; - while ((received_bytes = recv(client_sock, buf, sizeof(buf) - 1, 0)) > 0) + while ((received_bytes = recv(client_sock, buf, BUFFER_SIZE - 1, 0)) > 0) { printf("received_bytes: %ld\n", received_bytes); buf[received_bytes] = '\0'; // Null-terminate the string From a4982b732a0d00c6f897b8b3566b5b95385b1f4f Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Thu, 16 Jan 2025 06:31:47 +0000 Subject: [PATCH 21/41] Fix some minors --- client.c | 8 +------- server.c | 6 +++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/client.c b/client.c index e61b339..758e765 100644 --- a/client.c +++ b/client.c @@ -42,12 +42,6 @@ int main(int argc, char **argv) close(sock); exit(1); } - else if (sent_bytes == 0) - { - printf("Connection closed by server\n"); - close(sock); - exit(0); - } ssize_t received_bytes = recv(sock, buf, BUFFER_SIZE - 1, 0); if (received_bytes == -1) @@ -58,7 +52,7 @@ int main(int argc, char **argv) } else if (received_bytes == 0) { - printf("Connection closed by server\n"); + puts("Connection closed by server\n"); close(sock); exit(0); } diff --git a/server.c b/server.c index b5a31f1..caaf694 100644 --- a/server.c +++ b/server.c @@ -108,7 +108,7 @@ int main(int argc, char **argv) if (listen_sock_v4 == -1 && listen_sock_v6 == -1) { - printf("server: failed to bind any sockets\n"); + puts("server: failed to bind any sockets\n"); exit(1); } @@ -279,7 +279,7 @@ void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, i // Fulfilled the maximum number of clients if (*free_index_top == -1) { - printf("No more room for clients\n"); + puts("No more room for clients\n"); close(conn_sock); return; } @@ -343,7 +343,7 @@ void handle_client(int client_sock, int *client_sockets, int index) } else if (received_bytes == 0) { - printf("Connection closed\n"); + puts("Connection closed\n"); close(client_sock); client_sockets[index] = -1; } From 4dc05d0956d3c6ec37d8e0dd3d78f4d588fb6b40 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Thu, 16 Jan 2025 07:04:43 +0000 Subject: [PATCH 22/41] Modify handle_new_connection() to return int value --- server.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/server.c b/server.c index caaf694..0848526 100644 --- a/server.c +++ b/server.c @@ -17,7 +17,7 @@ #define MAX_CLIENTS 30 #define MAX_EVENTS 10 -void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top); +int handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top); void handle_client(int client_sock, int *client_sockets, int index); int main(int argc, char **argv) @@ -213,7 +213,12 @@ int main(int argc, char **argv) // Check if the event is for the listen socket if (events[i].data.fd == listen_sock_v4 || events[i].data.fd == listen_sock_v6) { - handle_new_connection(events[i].data.fd, epoll_fd, client_sockets, free_indices, &free_index_top); + int res = handle_new_connection(events[i].data.fd, epoll_fd, client_sockets, free_indices, &free_index_top); + if (res == -1) + { + close(events[i].data.fd); + close(epoll_fd); + } } // Check if the event is for a client socket else @@ -245,7 +250,7 @@ int main(int argc, char **argv) return 0; } -void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top) +int handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top) { struct sockaddr_storage client_addr; socklen_t addr_len = sizeof(client_addr); @@ -255,14 +260,12 @@ void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, i { if (errno == EAGAIN) { - return; + return 0; } else { perror("server: accept()"); - close(listen_sock); - close(epoll_fd); - exit(1); + return -1; } } @@ -271,9 +274,7 @@ void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, i { perror("server: fcntl(conn_sock)"); close(conn_sock); - close(listen_sock); - close(epoll_fd); - exit(1); + return -1; } // Fulfilled the maximum number of clients @@ -281,7 +282,7 @@ void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, i { puts("No more room for clients\n"); close(conn_sock); - return; + return 0; } int index = free_indices[(*free_index_top)--]; @@ -294,9 +295,7 @@ void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, i { perror("server: epoll_ctl()"); close(conn_sock); - close(listen_sock); - close(epoll_fd); - exit(1); + return -1; } char client_ip[INET6_ADDRSTRLEN]; @@ -312,6 +311,8 @@ void handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, i inet_ntop(PF_INET6, &client_addr6->sin6_addr, client_ip, sizeof(client_ip)); printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6->sin6_port)); } + + return 0; } void handle_client(int client_sock, int *client_sockets, int index) From f333a3055be66109234df5f648859c2af726bd38 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Thu, 16 Jan 2025 07:17:13 +0000 Subject: [PATCH 23/41] Modify handle_client() to return int value --- server.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/server.c b/server.c index 0848526..b19c56b 100644 --- a/server.c +++ b/server.c @@ -18,7 +18,7 @@ #define MAX_EVENTS 10 int handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top); -void handle_client(int client_sock, int *client_sockets, int index); +int handle_client(int client_sock); int main(int argc, char **argv) { @@ -229,9 +229,19 @@ int main(int argc, char **argv) { if (client_sockets[j] == conn_sock) { - handle_client(conn_sock, client_sockets, j); - if (client_sockets[j] == -1) + int res = handle_client(conn_sock); + if (res == -1) { + close(conn_sock); + client_sockets[j] = -1; + free_indices[++free_index_top] = j; + close(epoll_fd); + exit(1); + } + else if (res == 0) + { + close(conn_sock); + client_sockets[j] = -1; free_indices[++free_index_top] = j; } break; @@ -315,7 +325,7 @@ int handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, in return 0; } -void handle_client(int client_sock, int *client_sockets, int index) +int handle_client(int client_sock) { char buf[BUFFER_SIZE]; ssize_t received_bytes; @@ -326,9 +336,7 @@ void handle_client(int client_sock, int *client_sockets, int index) if (send(client_sock, buf, received_bytes, 0) == -1) { perror("server: send()"); - close(client_sock); - client_sockets[index] = -1; - exit(1); + return -1; } } @@ -337,15 +345,14 @@ void handle_client(int client_sock, int *client_sockets, int index) if (errno != EAGAIN) { perror("server: recv()"); - close(client_sock); - client_sockets[index] = -1; - exit(1); + return -1; } } else if (received_bytes == 0) { puts("Connection closed\n"); - close(client_sock); - client_sockets[index] = -1; + return 0; } + + return 1; } From fabe42c62facf6a21906b31ce961c66c8ae27959 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Thu, 16 Jan 2025 07:35:45 +0000 Subject: [PATCH 24/41] Extract socket creation and listening handling logic into create_listen_sockets() function --- server.c | 147 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 79 insertions(+), 68 deletions(-) diff --git a/server.c b/server.c index b19c56b..e4a607f 100644 --- a/server.c +++ b/server.c @@ -17,6 +17,7 @@ #define MAX_CLIENTS 30 #define MAX_EVENTS 10 +int create_listen_sockets(const char *port_str, int *listen_sock_v4, int *listen_sock_v6); int handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top); int handle_client(int client_sock); @@ -39,81 +40,17 @@ int main(int argc, char **argv) printf("Invalid port number: %s\n", argv[1]); exit(1); } - printf("Port: %d\n", port); - struct addrinfo hints, *res, *res0; + // Create listen sockets int listen_sock_v4 = -1; int listen_sock_v6 = -1; - int yes = 1; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; // Allow IPv4 or IPv6 - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; // For wildcard IP address - - if (getaddrinfo(NULL, argv[1], &hints, &res0) != 0) + if (create_listen_sockets(argv[1], &listen_sock_v4, &listen_sock_v6) == -1) { - perror("server: getaddrinfo()"); + puts("Failed to create listen sockets\n"); exit(1); } - for (res = res0; res != NULL; res = res->ai_next) - { - int listen_sock; - if ((listen_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) - { - perror("server: socket()"); - continue; - } - - // Set the socket option to reuse the address - setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); - - if (res->ai_family == PF_INET6) - { - // Set the socket option to allow only IPv6 connections - if (setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(int)) == -1) - { - perror("server: setsockopt()"); - close(listen_sock); - continue; - }; - } - - if (bind(listen_sock, res->ai_addr, res->ai_addrlen) == -1) - { - perror("server: bind()"); - close(listen_sock); - continue; - } - - if (listen(listen_sock, 5) == -1) - { - perror("server: listen()"); - close(listen_sock); - exit(1); - } - - if (res->ai_family == PF_INET) - { - listen_sock_v4 = listen_sock; - } - else if (res->ai_family == PF_INET6) - { - listen_sock_v6 = listen_sock; - } - } - - freeaddrinfo(res0); - - if (listen_sock_v4 == -1 && listen_sock_v6 == -1) - { - puts("server: failed to bind any sockets\n"); - exit(1); - } - - printf("listen_sock_v4: %d\n", listen_sock_v4); - printf("listen_sock_v6: %d\n", listen_sock_v6); + printf("Listening on port %s\n", argv[1]); // Create an epoll instance int epoll_fd; @@ -260,6 +197,80 @@ int main(int argc, char **argv) return 0; } +int create_listen_sockets(const char *port_str, int *listen_sock_v4, int *listen_sock_v6) +{ + struct addrinfo hints, *res, *res0; + int yes = 1; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; // Allow IPv4 or IPv6 + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; // For wildcard IP address + + if (getaddrinfo(NULL, port_str, &hints, &res0) != 0) + { + perror("server: getaddrinfo()"); + return -1; + } + + for (res = res0; res != NULL; res = res->ai_next) + { + int listen_sock; + if ((listen_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) + { + perror("server: socket()"); + continue; + } + + // Set the socket option to reuse the address + setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + + if (res->ai_family == PF_INET6) + { + // Set the socket option to allow only IPv6 connections + if (setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(int)) == -1) + { + perror("server: setsockopt()"); + close(listen_sock); + continue; + }; + } + + if (bind(listen_sock, res->ai_addr, res->ai_addrlen) == -1) + { + perror("server: bind()"); + close(listen_sock); + continue; + } + + if (listen(listen_sock, 5) == -1) + { + perror("server: listen()"); + close(listen_sock); + return -1; + } + + if (res->ai_family == PF_INET) + { + *listen_sock_v4 = listen_sock; + } + else if (res->ai_family == PF_INET6) + { + *listen_sock_v6 = listen_sock; + } + } + + freeaddrinfo(res0); + + if (*listen_sock_v4 == -1 && *listen_sock_v6 == -1) + { + puts("server: failed to bind any sockets\n"); + return -1; + } + + return 0; +} + int handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top) { struct sockaddr_storage client_addr; From 0504445cd1f26ffea208e8f1924e5202792bb79f Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Thu, 16 Jan 2025 08:38:54 +0000 Subject: [PATCH 25/41] Manage client_sockets using SocketManager --- server.c | 31 +++++++++---------------------- utils.c | 37 +++++++++++++++++++++++++++++++++++++ utils.h | 15 ++++++++++++++- 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/server.c b/server.c index e4a607f..a024e61 100644 --- a/server.c +++ b/server.c @@ -18,7 +18,7 @@ #define MAX_EVENTS 10 int create_listen_sockets(const char *port_str, int *listen_sock_v4, int *listen_sock_v6); -int handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top); +int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_socket_manager); int handle_client(int client_sock); int main(int argc, char **argv) @@ -119,16 +119,8 @@ int main(int argc, char **argv) struct epoll_event events[MAX_EVENTS]; // Manage the client sockets using stack - int client_sockets[MAX_CLIENTS]; - int free_indices[MAX_CLIENTS]; - int free_index_top = MAX_CLIENTS - 1; - - // Initialize the client sockets and free indices - for (int i = 0; i < MAX_CLIENTS; i++) - { - client_sockets[i] = -1; - free_indices[i] = i; - } + SocketManager client_socket_manager; + init_socket_manager(&client_socket_manager); while (1) { @@ -150,7 +142,7 @@ int main(int argc, char **argv) // Check if the event is for the listen socket if (events[i].data.fd == listen_sock_v4 || events[i].data.fd == listen_sock_v6) { - int res = handle_new_connection(events[i].data.fd, epoll_fd, client_sockets, free_indices, &free_index_top); + int res = handle_new_connection(events[i].data.fd, epoll_fd, &client_socket_manager); if (res == -1) { close(events[i].data.fd); @@ -164,22 +156,20 @@ int main(int argc, char **argv) // TODO: Manage the client socket using hash table for (int j = 0; j < MAX_CLIENTS; j++) { - if (client_sockets[j] == conn_sock) + if (client_socket_manager.sockets[j] == conn_sock) { int res = handle_client(conn_sock); if (res == -1) { + remove_socket(&client_socket_manager, conn_sock); close(conn_sock); - client_sockets[j] = -1; - free_indices[++free_index_top] = j; close(epoll_fd); exit(1); } else if (res == 0) { + remove_socket(&client_socket_manager, conn_sock); close(conn_sock); - client_sockets[j] = -1; - free_indices[++free_index_top] = j; } break; } @@ -271,7 +261,7 @@ int create_listen_sockets(const char *port_str, int *listen_sock_v4, int *listen return 0; } -int handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, int *free_indices, int *free_index_top) +int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_socket_manager) { struct sockaddr_storage client_addr; socklen_t addr_len = sizeof(client_addr); @@ -299,16 +289,13 @@ int handle_new_connection(int listen_sock, int epoll_fd, int *client_sockets, in } // Fulfilled the maximum number of clients - if (*free_index_top == -1) + if (add_socket(client_socket_manager, conn_sock) == -1) { puts("No more room for clients\n"); close(conn_sock); return 0; } - int index = free_indices[(*free_index_top)--]; - client_sockets[index] = conn_sock; - struct epoll_event event; event.events = EPOLLIN | EPOLLET; event.data.fd = conn_sock; diff --git a/utils.c b/utils.c index 9c59ebe..5b6a145 100644 --- a/utils.c +++ b/utils.c @@ -2,6 +2,43 @@ #include #include +#include "utils.h" + +void init_socket_manager(SocketManager *manager) +{ + for (int i = 0; i < MAX_SOCKETS; i++) + { + manager->sockets[i] = -1; + manager->free_indices[i] = i; + } + manager->top = MAX_SOCKETS - 1; +} + +int add_socket(SocketManager *manager, int sock) +{ + if (manager->top < 0) + { + return -1; + } + int index = manager->free_indices[manager->top--]; + manager->sockets[index] = sock; + return index; +} + +int remove_socket(SocketManager *manager, int sock) +{ + for (int i = 0; i < MAX_SOCKETS; i++) + { + if (manager->sockets[i] == sock) + { + manager->sockets[i] = -1; + manager->free_indices[++manager->top] = i; + return 0; + } + } + return -1; +} + int parse_port(const char *port_str) { char *endptr; diff --git a/utils.h b/utils.h index e9d0aca..6a66053 100644 --- a/utils.h +++ b/utils.h @@ -1,6 +1,19 @@ #ifndef UTILS_H #define UTILS_H -long parse_port(const char *port_str); +#define MAX_SOCKETS 30 + +typedef struct +{ + int sockets[MAX_SOCKETS]; + int free_indices[MAX_SOCKETS]; + int top; +} SocketManager; + +void init_socket_manager(SocketManager *manager); +int add_socket(SocketManager *manager, int sock); +int remove_socket(SocketManager *manager, int sock); + +int parse_port(const char *port_str); #endif From 94607c9c86c0f7fbc79bb57f5e9e25a28725c09a Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Thu, 16 Jan 2025 09:08:09 +0000 Subject: [PATCH 26/41] Refactor to support more than two listen_sockets and manage them using Socket Manager --- server.c | 84 +++++++++++++++----------------------------------------- utils.c | 25 +++++++++++++++++ utils.h | 6 +++- 3 files changed, 52 insertions(+), 63 deletions(-) diff --git a/server.c b/server.c index a024e61..ccdc9a5 100644 --- a/server.c +++ b/server.c @@ -17,7 +17,7 @@ #define MAX_CLIENTS 30 #define MAX_EVENTS 10 -int create_listen_sockets(const char *port_str, int *listen_sock_v4, int *listen_sock_v6); +int create_listen_sockets(const char *port_str, SocketManager *listen_socket_manager); int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_socket_manager); int handle_client(int client_sock); @@ -42,9 +42,9 @@ int main(int argc, char **argv) } // Create listen sockets - int listen_sock_v4 = -1; - int listen_sock_v6 = -1; - if (create_listen_sockets(argv[1], &listen_sock_v4, &listen_sock_v6) == -1) + SocketManager listen_socket_manager; + init_socket_manager(&listen_socket_manager); + if (create_listen_sockets(argv[1], &listen_socket_manager) == -1) { puts("Failed to create listen sockets\n"); exit(1); @@ -57,61 +57,34 @@ int main(int argc, char **argv) if ((epoll_fd = epoll_create1(0)) == -1) { perror("server: epoll_create1()"); - if (listen_sock_v4 != -1) - close(listen_sock_v4); - if (listen_sock_v6 != -1) - close(listen_sock_v6); + close_all_sockets(&listen_socket_manager); exit(1); } struct epoll_event event; - if (listen_sock_v4 != -1) + for (int i = 0; i < MAX_SOCKETS; i++) { - if (fcntl(listen_sock_v4, F_SETFL, O_NONBLOCK) == -1) + int listen_sock = listen_socket_manager.sockets[i]; + if (listen_sock == -1) { - perror("server: fcntl(listen_sock_v4)"); - close(listen_sock_v4); - if (listen_sock_v6 != -1) - close(listen_sock_v6); - close(epoll_fd); - exit(1); - } - event.events = EPOLLIN; - event.data.fd = listen_sock_v4; - - // Add the listen socket to the epoll instance - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock_v4, &event) == -1) - { - perror("server: epoll_ctl(listen_sock_v4)"); - close(listen_sock_v4); - if (listen_sock_v6 != -1) - close(listen_sock_v6); - close(epoll_fd); - exit(1); + continue; } - } - if (listen_sock_v6 != -1) - { - if (fcntl(listen_sock_v6, F_SETFL, O_NONBLOCK) == -1) + if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) == -1) { - perror("server: fcntl(listen_sock_v6)"); - close(listen_sock_v6); - if (listen_sock_v4 != -1) - close(listen_sock_v4); + perror("server: fcntl()"); + close_all_sockets(&listen_socket_manager); close(epoll_fd); exit(1); } event.events = EPOLLIN; - event.data.fd = listen_sock_v6; + event.data.fd = listen_sock; // Add the listen socket to the epoll instance - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock_v6, &event) == -1) + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &event) == -1) { - perror("server: epoll_ctl(listen_sock_v6)"); - close(listen_sock_v6); - if (listen_sock_v4 != -1) - close(listen_sock_v4); + perror("server: epoll_ctl()"); + close_all_sockets(&listen_socket_manager); close(epoll_fd); exit(1); } @@ -129,10 +102,7 @@ int main(int argc, char **argv) if (nfds == -1) { perror("server: epoll_wait()"); - if (listen_sock_v4 != -1) - close(listen_sock_v4); - if (listen_sock_v6 != -1) - close(listen_sock_v6); + close_all_sockets(&listen_socket_manager); close(epoll_fd); exit(1); } @@ -140,7 +110,7 @@ int main(int argc, char **argv) for (int i = 0; i < nfds; i++) { // Check if the event is for the listen socket - if (events[i].data.fd == listen_sock_v4 || events[i].data.fd == listen_sock_v6) + if (contains(listen_socket_manager.sockets, MAX_SOCKETS, events[i].data.fd)) { int res = handle_new_connection(events[i].data.fd, epoll_fd, &client_socket_manager); if (res == -1) @@ -178,16 +148,13 @@ int main(int argc, char **argv) } } - if (listen_sock_v4 != -1) - close(listen_sock_v4); - if (listen_sock_v6 != -1) - close(listen_sock_v6); + close_all_sockets(&listen_socket_manager); close(epoll_fd); return 0; } -int create_listen_sockets(const char *port_str, int *listen_sock_v4, int *listen_sock_v6) +int create_listen_sockets(const char *port_str, SocketManager *listen_socket_manager) { struct addrinfo hints, *res, *res0; int yes = 1; @@ -240,19 +207,12 @@ int create_listen_sockets(const char *port_str, int *listen_sock_v4, int *listen return -1; } - if (res->ai_family == PF_INET) - { - *listen_sock_v4 = listen_sock; - } - else if (res->ai_family == PF_INET6) - { - *listen_sock_v6 = listen_sock; - } + add_socket(listen_socket_manager, listen_sock); } freeaddrinfo(res0); - if (*listen_sock_v4 == -1 && *listen_sock_v6 == -1) + if (listen_socket_manager->top == MAX_SOCKETS - 1) { puts("server: failed to bind any sockets\n"); return -1; diff --git a/utils.c b/utils.c index 5b6a145..b7bda91 100644 --- a/utils.c +++ b/utils.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -39,6 +40,18 @@ int remove_socket(SocketManager *manager, int sock) return -1; } +int close_all_sockets(SocketManager *manager) +{ + for (int i = 0; i < MAX_SOCKETS; i++) + { + if (manager->sockets[i] != -1) + { + close(manager->sockets[i]); + } + } + return 0; +} + int parse_port(const char *port_str) { char *endptr; @@ -50,3 +63,15 @@ int parse_port(const char *port_str) } return (int)port; } + +bool contains(int *array, int size, int value) +{ + for (int i = 0; i < size; i++) + { + if (array[i] == value) + { + return true; + } + } + return false; +} diff --git a/utils.h b/utils.h index 6a66053..d49167c 100644 --- a/utils.h +++ b/utils.h @@ -1,3 +1,5 @@ +#include + #ifndef UTILS_H #define UTILS_H @@ -5,6 +7,7 @@ typedef struct { + // TODO: dynamically allocate the array int sockets[MAX_SOCKETS]; int free_indices[MAX_SOCKETS]; int top; @@ -13,7 +16,8 @@ typedef struct void init_socket_manager(SocketManager *manager); int add_socket(SocketManager *manager, int sock); int remove_socket(SocketManager *manager, int sock); +int close_all_sockets(SocketManager *manager); int parse_port(const char *port_str); - +bool contains(int *array, int size, int value); #endif From 78d925d8f98ceec8c318e5a1f3239a73d1f5b164 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Thu, 16 Jan 2025 09:11:21 +0000 Subject: [PATCH 27/41] Use contains for simplicity --- server.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/server.c b/server.c index ccdc9a5..30457a5 100644 --- a/server.c +++ b/server.c @@ -124,24 +124,20 @@ int main(int argc, char **argv) { int conn_sock = events[i].data.fd; // TODO: Manage the client socket using hash table - for (int j = 0; j < MAX_CLIENTS; j++) + if (contains(client_socket_manager.sockets, MAX_CLIENTS, conn_sock)) { - if (client_socket_manager.sockets[j] == conn_sock) + int res = handle_client(conn_sock); + if (res == -1) { - int res = handle_client(conn_sock); - if (res == -1) - { - remove_socket(&client_socket_manager, conn_sock); - close(conn_sock); - close(epoll_fd); - exit(1); - } - else if (res == 0) - { - remove_socket(&client_socket_manager, conn_sock); - close(conn_sock); - } - break; + remove_socket(&client_socket_manager, conn_sock); + close(conn_sock); + close(epoll_fd); + exit(1); + } + else if (res == 0) + { + remove_socket(&client_socket_manager, conn_sock); + close(conn_sock); } } } From adcb7f9ded52c27542ca52922631d64626d917e3 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Thu, 16 Jan 2025 09:24:05 +0000 Subject: [PATCH 28/41] Remove redundant parts from Makefile --- Makefile | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 58d30ab..df72073 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CC = gcc CFLAGS = -Wall -Wextra -Wpedantic -O2 -.PHONY: all clean server client utils +.PHONY: all clean all: server client @@ -11,14 +11,8 @@ server: server.o utils.o client: client.o utils.o $(CC) $(CFLAGS) -o client client.o utils.o -server.o: server.c - $(CC) $(CFLAGS) -c server.c - -client.o: client.c - $(CC) $(CFLAGS) -c client.c - -utils.o: utils.c - $(CC) $(CFLAGS) -c utils.c +%.o: %.c + $(CC) $(CFLAGS) -c $< clean: rm -f server client *.o From a6865bf5482b8ef56284647795710b4386829e48 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Thu, 16 Jan 2025 09:39:38 +0000 Subject: [PATCH 29/41] Fix some minors --- client.c | 2 +- server.c | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/client.c b/client.c index 758e765..d481170 100644 --- a/client.c +++ b/client.c @@ -54,7 +54,7 @@ int main(int argc, char **argv) { puts("Connection closed by server\n"); close(sock); - exit(0); + exit(1); } printf("%s", buf); diff --git a/server.c b/server.c index 30457a5..23570d9 100644 --- a/server.c +++ b/server.c @@ -70,7 +70,16 @@ int main(int argc, char **argv) continue; } - if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) == -1) + // Set the listen socket to non-blocking mode + int flags = fcntl(listen_sock, F_GETFL, 0); + if (flags == -1) + { + perror("server: fcntl()"); + close_all_sockets(&listen_socket_manager); + close(epoll_fd); + exit(1); + } + if (fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK) == -1) { perror("server: fcntl()"); close_all_sockets(&listen_socket_manager); @@ -225,7 +234,7 @@ int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_s if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &addr_len)) == -1) { - if (errno == EAGAIN) + if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } @@ -237,7 +246,14 @@ int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_s } // Set the connection socket to non-blocking mode - if (fcntl(conn_sock, F_SETFL, O_NONBLOCK) == -1) + int flags = fcntl(conn_sock, F_GETFL, 0); + if (flags == -1) + { + perror("server: fcntl(conn_sock)"); + close(conn_sock); + return -1; + } + if (fcntl(conn_sock, F_SETFL, flags | O_NONBLOCK) == -1) { perror("server: fcntl(conn_sock)"); close(conn_sock); @@ -296,7 +312,11 @@ int handle_client(int client_sock) if (received_bytes == -1) { - if (errno != EAGAIN) + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + return 1; + } + else { perror("server: recv()"); return -1; From 506020c7f4b18ff5eae31ba7684abcff5f808d41 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 01:21:48 +0000 Subject: [PATCH 30/41] Organize error handlings --- server.c | 77 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/server.c b/server.c index 23570d9..446c90a 100644 --- a/server.c +++ b/server.c @@ -30,12 +30,8 @@ int main(int argc, char **argv) exit(1); } - // Initialize the error number - errno = 0; - // Parse the port number - int port; - if ((port = parse_port(argv[1])) == -1) + if (parse_port(argv[1]) == -1) { printf("Invalid port number: %s\n", argv[1]); exit(1); @@ -100,7 +96,6 @@ int main(int argc, char **argv) } struct epoll_event events[MAX_EVENTS]; - // Manage the client sockets using stack SocketManager client_socket_manager; init_socket_manager(&client_socket_manager); @@ -121,35 +116,39 @@ int main(int argc, char **argv) // Check if the event is for the listen socket if (contains(listen_socket_manager.sockets, MAX_SOCKETS, events[i].data.fd)) { - int res = handle_new_connection(events[i].data.fd, epoll_fd, &client_socket_manager); - if (res == -1) + int listen_sock = events[i].data.fd; + if ((handle_new_connection(listen_sock, epoll_fd, &client_socket_manager)) == -1) { - close(events[i].data.fd); + close(listen_sock); close(epoll_fd); } } // Check if the event is for a client socket - else + else if (contains(client_socket_manager.sockets, MAX_SOCKETS, events[i].data.fd)) { - int conn_sock = events[i].data.fd; - // TODO: Manage the client socket using hash table - if (contains(client_socket_manager.sockets, MAX_CLIENTS, conn_sock)) + int client_sock = events[i].data.fd; + int result = handle_client(client_sock); + if (result == -1) + { + remove_socket(&client_socket_manager, client_sock); + close(client_sock); + close(epoll_fd); + exit(1); + } + else if (result == 0) { - int res = handle_client(conn_sock); - if (res == -1) - { - remove_socket(&client_socket_manager, conn_sock); - close(conn_sock); - close(epoll_fd); - exit(1); - } - else if (res == 0) - { - remove_socket(&client_socket_manager, conn_sock); - close(conn_sock); - } + remove_socket(&client_socket_manager, client_sock); + close(client_sock); } } + // Unreachable + else + { + puts("Unknown socket\n"); + close(events[i].data.fd); + close(epoll_fd); + exit(1); + } } } @@ -169,9 +168,10 @@ int create_listen_sockets(const char *port_str, SocketManager *listen_socket_man hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; // For wildcard IP address - if (getaddrinfo(NULL, port_str, &hints, &res0) != 0) + int getaddrinfo_result; + if ((getaddrinfo_result = getaddrinfo(NULL, port_str, &hints, &res0)) != 0) { - perror("server: getaddrinfo()"); + printf("server: getaddrinfo(): %s(%d)", gai_strerror(getaddrinfo_result), getaddrinfo_result); return -1; } @@ -185,14 +185,19 @@ int create_listen_sockets(const char *port_str, SocketManager *listen_socket_man } // Set the socket option to reuse the address - setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) + { + perror("server: setsockopt(SOL_SOCKET, SO_REUSEADDR)"); + close(listen_sock); + continue; + } if (res->ai_family == PF_INET6) { // Set the socket option to allow only IPv6 connections if (setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(int)) == -1) { - perror("server: setsockopt()"); + perror("server: setsockopt(IPPROTO_IPV6, IPV6_V6ONLY)"); close(listen_sock); continue; }; @@ -282,13 +287,21 @@ int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_s if (client_addr.ss_family == PF_INET) { struct sockaddr_in *client_addr4 = (struct sockaddr_in *)&client_addr; - inet_ntop(PF_INET, &client_addr4->sin_addr, client_ip, sizeof(client_ip)); + if (inet_ntop(PF_INET, &client_addr4->sin_addr, client_ip, sizeof(client_ip)) == NULL) + { + perror("server: inet_ntop(PF_INET)"); + return -1; + } printf("Connection from %s, %d\n", client_ip, ntohs(client_addr4->sin_port)); } else if (client_addr.ss_family == PF_INET6) { struct sockaddr_in6 *client_addr6 = (struct sockaddr_in6 *)&client_addr; - inet_ntop(PF_INET6, &client_addr6->sin6_addr, client_ip, sizeof(client_ip)); + if (inet_ntop(PF_INET6, &client_addr6->sin6_addr, client_ip, sizeof(client_ip)) == NULL) + { + perror("server: inet_ntop(PF_INET6)"); + return -1; + } printf("Connection from %s, %d\n", client_ip, ntohs(client_addr6->sin6_port)); } From 8d4013a5a562d202615fbd814a24c0ef0e79d924 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 01:35:53 +0000 Subject: [PATCH 31/41] Add detailed documentation comments for all functions --- client.c | 11 +++++++++++ server.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/client.c b/client.c index d481170..104c57c 100644 --- a/client.c +++ b/client.c @@ -65,6 +65,17 @@ int main(int argc, char **argv) return 0; } +/** + * @brief Create a connected socket to the specified IP address and port. + * + * This function creates a socket, connects it to the specified IP address and port, + * and returns the connected socket file descriptor. + * + * @param[in] ip The IP address to connect to. + * @param[in] port The port number to connect to. + * @return The file descriptor of the connected socket. + * @retval -1 An error occurred during the process. + */ int create_connected_socket(const char *ip, const char *port_str) { // Parse the IP address diff --git a/server.c b/server.c index 446c90a..60ada54 100644 --- a/server.c +++ b/server.c @@ -158,6 +158,18 @@ int main(int argc, char **argv) return 0; } +/** + * @brief Create and bind listening sockets for the server. + * + * This function creates and binds listening sockets for both IPv4 and IPv6, sets the sockets + * to reuse the address, and adds them to the provided socket manager. + * + * @param[in] port_str The port number as a string. + * @param[in] listen_socket_manager The socket manager for listening sockets. + * @return The status of the socket creation and binding process. + * @retval 0 The sockets were successfully created and bound. + * @retval -1 An error occurred during the process. + */ int create_listen_sockets(const char *port_str, SocketManager *listen_socket_manager) { struct addrinfo hints, *res, *res0; @@ -231,6 +243,20 @@ int create_listen_sockets(const char *port_str, SocketManager *listen_socket_man return 0; } +/** + * @brief Handle a new connection on the listening socket. + * + * This function accepts a new connection on the listening socket, sets the new connection + * socket to non-blocking mode, adds it to the epoll instance, and registers it with the + * client socket manager. + * + * @param[in] listen_sock The file descriptor of the listening socket. + * @param[in] epoll_fd The file descriptor of the epoll instance. + * @param[in] client_socket_manager The socket manager for client sockets. + * @return The status of the new connection. + * @retval 0 The connection was successfully accepted and registered. + * @retval -1 An error occurred during the process. + */ int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_socket_manager) { struct sockaddr_storage client_addr; @@ -308,6 +334,19 @@ int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_s return 0; } +/** + * @brief Handle the client socket + * + * This function handles communication with a connected client socket. It reads data from the client, + * processes it, and sends a response back to the client. The function continues to read and send data + * until the client disconnects or an error occurs. + * + * @param[in] client_sock The file descriptor of the client socket. + * @return The status of the client connection. + * @retval 1 The client is still connected. + * @retval 0 The client has disconnected. + * @retval -1 An error occurred during communication. + */ int handle_client(int client_sock) { char buf[BUFFER_SIZE]; From 1e1e22e671a2020845b545e5ee9544c1740920e0 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 01:55:06 +0000 Subject: [PATCH 32/41] Extract listening socket registration logic into add_listen_sockets_to_epoll() function --- server.c | 91 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/server.c b/server.c index 60ada54..2b815d6 100644 --- a/server.c +++ b/server.c @@ -18,6 +18,7 @@ #define MAX_EVENTS 10 int create_listen_sockets(const char *port_str, SocketManager *listen_socket_manager); +int add_listen_sockets_to_epoll(int epoll_fd, SocketManager *listen_socket_manager); int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_socket_manager); int handle_client(int client_sock); @@ -57,44 +58,16 @@ int main(int argc, char **argv) exit(1); } - struct epoll_event event; - for (int i = 0; i < MAX_SOCKETS; i++) + // Add the listen sockets to the epoll instance + if (add_listen_sockets_to_epoll(epoll_fd, &listen_socket_manager) == -1) { - int listen_sock = listen_socket_manager.sockets[i]; - if (listen_sock == -1) - { - continue; - } - - // Set the listen socket to non-blocking mode - int flags = fcntl(listen_sock, F_GETFL, 0); - if (flags == -1) - { - perror("server: fcntl()"); - close_all_sockets(&listen_socket_manager); - close(epoll_fd); - exit(1); - } - if (fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK) == -1) - { - perror("server: fcntl()"); - close_all_sockets(&listen_socket_manager); - close(epoll_fd); - exit(1); - } - event.events = EPOLLIN; - event.data.fd = listen_sock; - - // Add the listen socket to the epoll instance - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &event) == -1) - { - perror("server: epoll_ctl()"); - close_all_sockets(&listen_socket_manager); - close(epoll_fd); - exit(1); - } + close_all_sockets(&listen_socket_manager); + close(epoll_fd); + exit(1); } + puts("Monitoring for events..."); + struct epoll_event events[MAX_EVENTS]; SocketManager client_socket_manager; init_socket_manager(&client_socket_manager); @@ -243,6 +216,54 @@ int create_listen_sockets(const char *port_str, SocketManager *listen_socket_man return 0; } +/** + * @brief Add the listening sockets to the epoll instance. + * + * This function sets the listening sockets to non-blocking mode and adds them to the epoll instance. + * + * @param[in] epoll_fd The file descriptor of the epoll instance. + * @param[in] listen_socket_manager The socket manager for listening sockets. + * @return The status of the process. + * @retval 0 The sockets were successfully added to the epoll instance. + * @retval -1 An error occurred during the process. + */ +int add_listen_sockets_to_epoll(int epoll_fd, SocketManager *listen_socket_manager) +{ + struct epoll_event event; + for (int i = 0; i < MAX_SOCKETS; i++) + { + int listen_sock = listen_socket_manager->sockets[i]; + if (listen_sock == -1) + { + continue; + } + + // Set the listen socket to non-blocking mode + int flags = fcntl(listen_sock, F_GETFL, 0); + if (flags == -1) + { + perror("server: fcntl()"); + return -1; + } + if (fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK) == -1) + { + perror("server: fcntl()"); + return -1; + } + event.events = EPOLLIN; + event.data.fd = listen_sock; + + // Add the listen socket to the epoll instance + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &event) == -1) + { + perror("server: epoll_ctl()"); + return -1; + } + } + + return 0; +} + /** * @brief Handle a new connection on the listening socket. * From 442fe0a88ac878988038db535ae1608dfdb8056a Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 02:00:45 +0000 Subject: [PATCH 33/41] Modify server error handling not to exit 1 when error occuring on client side --- server.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/server.c b/server.c index 2b815d6..d8ffbe1 100644 --- a/server.c +++ b/server.c @@ -100,15 +100,7 @@ int main(int argc, char **argv) else if (contains(client_socket_manager.sockets, MAX_SOCKETS, events[i].data.fd)) { int client_sock = events[i].data.fd; - int result = handle_client(client_sock); - if (result == -1) - { - remove_socket(&client_socket_manager, client_sock); - close(client_sock); - close(epoll_fd); - exit(1); - } - else if (result == 0) + if (handle_client(client_sock) <= 0) { remove_socket(&client_socket_manager, client_sock); close(client_sock); From 03074e444e2dab018a7755ab54f7a2926fceb8d8 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 02:15:18 +0000 Subject: [PATCH 34/41] Refactor SocketManager to dynamically manage the maximum size --- server.c | 13 +++++++------ utils.c | 16 +++++++++++----- utils.h | 10 ++++------ 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/server.c b/server.c index d8ffbe1..22acb72 100644 --- a/server.c +++ b/server.c @@ -14,6 +14,7 @@ #include "utils.h" #define BUFFER_SIZE 256 +#define MAX_LISTENS 20 #define MAX_CLIENTS 30 #define MAX_EVENTS 10 @@ -40,7 +41,7 @@ int main(int argc, char **argv) // Create listen sockets SocketManager listen_socket_manager; - init_socket_manager(&listen_socket_manager); + init_socket_manager(&listen_socket_manager, MAX_LISTENS); if (create_listen_sockets(argv[1], &listen_socket_manager) == -1) { puts("Failed to create listen sockets\n"); @@ -70,7 +71,7 @@ int main(int argc, char **argv) struct epoll_event events[MAX_EVENTS]; SocketManager client_socket_manager; - init_socket_manager(&client_socket_manager); + init_socket_manager(&client_socket_manager, MAX_CLIENTS); while (1) { @@ -87,7 +88,7 @@ int main(int argc, char **argv) for (int i = 0; i < nfds; i++) { // Check if the event is for the listen socket - if (contains(listen_socket_manager.sockets, MAX_SOCKETS, events[i].data.fd)) + if (contains(listen_socket_manager.sockets, listen_socket_manager.max_size, events[i].data.fd)) { int listen_sock = events[i].data.fd; if ((handle_new_connection(listen_sock, epoll_fd, &client_socket_manager)) == -1) @@ -97,7 +98,7 @@ int main(int argc, char **argv) } } // Check if the event is for a client socket - else if (contains(client_socket_manager.sockets, MAX_SOCKETS, events[i].data.fd)) + else if (contains(client_socket_manager.sockets, client_socket_manager.max_size, events[i].data.fd)) { int client_sock = events[i].data.fd; if (handle_client(client_sock) <= 0) @@ -199,7 +200,7 @@ int create_listen_sockets(const char *port_str, SocketManager *listen_socket_man freeaddrinfo(res0); - if (listen_socket_manager->top == MAX_SOCKETS - 1) + if (listen_socket_manager->top == listen_socket_manager->max_size - 1) { puts("server: failed to bind any sockets\n"); return -1; @@ -222,7 +223,7 @@ int create_listen_sockets(const char *port_str, SocketManager *listen_socket_man int add_listen_sockets_to_epoll(int epoll_fd, SocketManager *listen_socket_manager) { struct epoll_event event; - for (int i = 0; i < MAX_SOCKETS; i++) + for (int i = 0; i < listen_socket_manager->max_size; i++) { int listen_sock = listen_socket_manager->sockets[i]; if (listen_sock == -1) diff --git a/utils.c b/utils.c index b7bda91..4936f08 100644 --- a/utils.c +++ b/utils.c @@ -5,14 +5,18 @@ #include "utils.h" -void init_socket_manager(SocketManager *manager) +void init_socket_manager(SocketManager *manager, int max_size) { - for (int i = 0; i < MAX_SOCKETS; i++) + manager->sockets = (int *)malloc(max_size * sizeof(int)); + manager->free_indices = (int *)malloc(max_size * sizeof(int)); + manager->max_size = max_size; + manager->top = max_size - 1; + + for (int i = 0; i < max_size; i++) { manager->sockets[i] = -1; manager->free_indices[i] = i; } - manager->top = MAX_SOCKETS - 1; } int add_socket(SocketManager *manager, int sock) @@ -28,7 +32,7 @@ int add_socket(SocketManager *manager, int sock) int remove_socket(SocketManager *manager, int sock) { - for (int i = 0; i < MAX_SOCKETS; i++) + for (int i = 0; i < manager->max_size; i++) { if (manager->sockets[i] == sock) { @@ -42,13 +46,15 @@ int remove_socket(SocketManager *manager, int sock) int close_all_sockets(SocketManager *manager) { - for (int i = 0; i < MAX_SOCKETS; i++) + for (int i = 0; i < manager->max_size; i++) { if (manager->sockets[i] != -1) { close(manager->sockets[i]); } } + free(manager->sockets); + free(manager->free_indices); return 0; } diff --git a/utils.h b/utils.h index d49167c..7fdb486 100644 --- a/utils.h +++ b/utils.h @@ -3,17 +3,15 @@ #ifndef UTILS_H #define UTILS_H -#define MAX_SOCKETS 30 - typedef struct { - // TODO: dynamically allocate the array - int sockets[MAX_SOCKETS]; - int free_indices[MAX_SOCKETS]; + int *sockets; + int *free_indices; + int max_size; int top; } SocketManager; -void init_socket_manager(SocketManager *manager); +void init_socket_manager(SocketManager *manager, int max_size); int add_socket(SocketManager *manager, int sock); int remove_socket(SocketManager *manager, int sock); int close_all_sockets(SocketManager *manager); From 634519e6f3bb956d15788a950e644496c8da62cb Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 03:11:22 +0000 Subject: [PATCH 35/41] Refactor error handling to use goto for common cleanup --- server.c | 75 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/server.c b/server.c index 22acb72..78228c0 100644 --- a/server.c +++ b/server.c @@ -18,6 +18,7 @@ #define MAX_CLIENTS 30 #define MAX_EVENTS 10 +void cleanup(int epoll_fd, SocketManager *listen_socket_manager, SocketManager *client_socket_manager); int create_listen_sockets(const char *port_str, SocketManager *listen_socket_manager); int add_listen_sockets_to_epoll(int epoll_fd, SocketManager *listen_socket_manager); int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_socket_manager); @@ -25,54 +26,59 @@ int handle_client(int client_sock); int main(int argc, char **argv) { + // Declare variables that need to be cleaned up later + int epoll_fd = -1; + SocketManager listen_socket_manager; + SocketManager client_socket_manager; + int exit_code = 0; + // Check if the port number is provided if (argc != 2) { printf("Usage: %s \n", argv[0]); - exit(1); + exit_code = 1; + goto cleanup; } // Parse the port number if (parse_port(argv[1]) == -1) { printf("Invalid port number: %s\n", argv[1]); - exit(1); + exit_code = 1; + goto cleanup; } // Create listen sockets - SocketManager listen_socket_manager; init_socket_manager(&listen_socket_manager, MAX_LISTENS); if (create_listen_sockets(argv[1], &listen_socket_manager) == -1) { puts("Failed to create listen sockets\n"); - exit(1); + exit_code = 1; + goto cleanup; } printf("Listening on port %s\n", argv[1]); // Create an epoll instance - int epoll_fd; if ((epoll_fd = epoll_create1(0)) == -1) { perror("server: epoll_create1()"); - close_all_sockets(&listen_socket_manager); - exit(1); + exit_code = 1; + goto cleanup; } // Add the listen sockets to the epoll instance if (add_listen_sockets_to_epoll(epoll_fd, &listen_socket_manager) == -1) { - close_all_sockets(&listen_socket_manager); - close(epoll_fd); - exit(1); + puts("Failed to add listen sockets to epoll\n"); + exit_code = 1; + goto cleanup; } puts("Monitoring for events..."); struct epoll_event events[MAX_EVENTS]; - SocketManager client_socket_manager; init_socket_manager(&client_socket_manager, MAX_CLIENTS); - while (1) { // Wait for events indefinitely @@ -80,9 +86,8 @@ int main(int argc, char **argv) if (nfds == -1) { perror("server: epoll_wait()"); - close_all_sockets(&listen_socket_manager); - close(epoll_fd); - exit(1); + exit_code = 1; + goto cleanup; } for (int i = 0; i < nfds; i++) @@ -93,8 +98,8 @@ int main(int argc, char **argv) int listen_sock = events[i].data.fd; if ((handle_new_connection(listen_sock, epoll_fd, &client_socket_manager)) == -1) { - close(listen_sock); - close(epoll_fd); + exit_code = 1; + goto cleanup; } } // Check if the event is for a client socket @@ -103,6 +108,7 @@ int main(int argc, char **argv) int client_sock = events[i].data.fd; if (handle_client(client_sock) <= 0) { + // The server itself does not exit even if the processing with the client ends abnormally. remove_socket(&client_socket_manager, client_sock); close(client_sock); } @@ -112,18 +118,47 @@ int main(int argc, char **argv) { puts("Unknown socket\n"); close(events[i].data.fd); - close(epoll_fd); - exit(1); + exit_code = 1; + goto cleanup; } } } +cleanup: + close_all_sockets(&client_socket_manager); close_all_sockets(&listen_socket_manager); - close(epoll_fd); + if (epoll_fd != -1) + { + close(epoll_fd); + } + if (exit_code != 0) + { + exit(exit_code); + } return 0; } +/** + * @brief Clean up the resources used by the server. + * + * This function closes all the client and listen sockets and the epoll instance. + * + * @param[in] epoll_fd The file descriptor of the epoll instance. + * @param[in] listen_socket_manager The socket manager for listening sockets. + * @param[in] client_socket_manager The socket manager for client sockets. + * @return void + */ +void cleanup(int epoll_fd, SocketManager *listen_socket_manager, SocketManager *client_socket_manager) +{ + close_all_sockets(client_socket_manager); + close_all_sockets(listen_socket_manager); + if (epoll_fd != -1) + { + close(epoll_fd); + } +} + /** * @brief Create and bind listening sockets for the server. * From ccf3478ae66cdd5ad393f7543c6cef03c8cc504b Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 05:52:31 +0000 Subject: [PATCH 36/41] Modify handle_client to execute recv() or send() only once per EPOLLIN or EPOLLOUT event --- server.c | 147 ++++++++++++++++++++++++++++--------------------------- utils.c | 40 +++++++-------- utils.h | 16 ++++-- 3 files changed, 108 insertions(+), 95 deletions(-) diff --git a/server.c b/server.c index 78228c0..391726e 100644 --- a/server.c +++ b/server.c @@ -21,8 +21,8 @@ void cleanup(int epoll_fd, SocketManager *listen_socket_manager, SocketManager *client_socket_manager); int create_listen_sockets(const char *port_str, SocketManager *listen_socket_manager); int add_listen_sockets_to_epoll(int epoll_fd, SocketManager *listen_socket_manager); -int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_socket_manager); -int handle_client(int client_sock); +int handle_new_connection(int listen_socket_fd, int epoll_fd, SocketManager *client_socket_manager); +int handle_client(SocketData *client_socket_data, struct epoll_event event); int main(int argc, char **argv) { @@ -92,45 +92,36 @@ int main(int argc, char **argv) for (int i = 0; i < nfds; i++) { + SocketData *socket_data; // Check if the event is for the listen socket - if (contains(listen_socket_manager.sockets, listen_socket_manager.max_size, events[i].data.fd)) + if ((socket_data = find_socket(&listen_socket_manager, events[i].data.fd)) != NULL) { - int listen_sock = events[i].data.fd; - if ((handle_new_connection(listen_sock, epoll_fd, &client_socket_manager)) == -1) + if ((handle_new_connection(socket_data->socket_fd, epoll_fd, &client_socket_manager)) == -1) { exit_code = 1; goto cleanup; } } // Check if the event is for a client socket - else if (contains(client_socket_manager.sockets, client_socket_manager.max_size, events[i].data.fd)) + else if ((socket_data = find_socket(&client_socket_manager, events[i].data.fd)) != NULL) { - int client_sock = events[i].data.fd; - if (handle_client(client_sock) <= 0) + if (handle_client(socket_data, events[i]) <= 0) { // The server itself does not exit even if the processing with the client ends abnormally. - remove_socket(&client_socket_manager, client_sock); - close(client_sock); + remove_socket(&client_socket_manager, socket_data->socket_fd); + close(socket_data->socket_fd); } } - // Unreachable - else - { - puts("Unknown socket\n"); - close(events[i].data.fd); - exit_code = 1; - goto cleanup; - } } } cleanup: - close_all_sockets(&client_socket_manager); - close_all_sockets(&listen_socket_manager); if (epoll_fd != -1) { close(epoll_fd); } + close_all_sockets(&client_socket_manager); + close_all_sockets(&listen_socket_manager); if (exit_code != 0) { @@ -190,47 +181,47 @@ int create_listen_sockets(const char *port_str, SocketManager *listen_socket_man for (res = res0; res != NULL; res = res->ai_next) { - int listen_sock; - if ((listen_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) + int listen_socket_fd; + if ((listen_socket_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) { perror("server: socket()"); continue; } // Set the socket option to reuse the address - if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) + if (setsockopt(listen_socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("server: setsockopt(SOL_SOCKET, SO_REUSEADDR)"); - close(listen_sock); + close(listen_socket_fd); continue; } if (res->ai_family == PF_INET6) { // Set the socket option to allow only IPv6 connections - if (setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(int)) == -1) + if (setsockopt(listen_socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(int)) == -1) { perror("server: setsockopt(IPPROTO_IPV6, IPV6_V6ONLY)"); - close(listen_sock); + close(listen_socket_fd); continue; }; } - if (bind(listen_sock, res->ai_addr, res->ai_addrlen) == -1) + if (bind(listen_socket_fd, res->ai_addr, res->ai_addrlen) == -1) { perror("server: bind()"); - close(listen_sock); + close(listen_socket_fd); continue; } - if (listen(listen_sock, 5) == -1) + if (listen(listen_socket_fd, 5) == -1) { perror("server: listen()"); - close(listen_sock); + close(listen_socket_fd); return -1; } - add_socket(listen_socket_manager, listen_sock); + add_socket(listen_socket_manager, listen_socket_fd); } freeaddrinfo(res0); @@ -260,29 +251,29 @@ int add_listen_sockets_to_epoll(int epoll_fd, SocketManager *listen_socket_manag struct epoll_event event; for (int i = 0; i < listen_socket_manager->max_size; i++) { - int listen_sock = listen_socket_manager->sockets[i]; - if (listen_sock == -1) + int listen_socket_fd = listen_socket_manager->sockets[i].socket_fd; + if (listen_socket_fd == -1) { continue; } // Set the listen socket to non-blocking mode - int flags = fcntl(listen_sock, F_GETFL, 0); + int flags = fcntl(listen_socket_fd, F_GETFL, 0); if (flags == -1) { perror("server: fcntl()"); return -1; } - if (fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK) == -1) + if (fcntl(listen_socket_fd, F_SETFL, flags | O_NONBLOCK) == -1) { perror("server: fcntl()"); return -1; } event.events = EPOLLIN; - event.data.fd = listen_sock; + event.data.fd = listen_socket_fd; // Add the listen socket to the epoll instance - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &event) == -1) + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_socket_fd, &event) == -1) { perror("server: epoll_ctl()"); return -1; @@ -299,20 +290,20 @@ int add_listen_sockets_to_epoll(int epoll_fd, SocketManager *listen_socket_manag * socket to non-blocking mode, adds it to the epoll instance, and registers it with the * client socket manager. * - * @param[in] listen_sock The file descriptor of the listening socket. + * @param[in] listen_socket_fd The file descriptor of the listening socket. * @param[in] epoll_fd The file descriptor of the epoll instance. * @param[in] client_socket_manager The socket manager for client sockets. * @return The status of the new connection. * @retval 0 The connection was successfully accepted and registered. * @retval -1 An error occurred during the process. */ -int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_socket_manager) +int handle_new_connection(int listen_socket_fd, int epoll_fd, SocketManager *client_socket_manager) { struct sockaddr_storage client_addr; socklen_t addr_len = sizeof(client_addr); - int conn_sock; + int conn_socket_fd; - if ((conn_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &addr_len)) == -1) + if ((conn_socket_fd = accept(listen_socket_fd, (struct sockaddr *)&client_addr, &addr_len)) == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { @@ -326,35 +317,35 @@ int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_s } // Set the connection socket to non-blocking mode - int flags = fcntl(conn_sock, F_GETFL, 0); + int flags = fcntl(conn_socket_fd, F_GETFL, 0); if (flags == -1) { perror("server: fcntl(conn_sock)"); - close(conn_sock); + close(conn_socket_fd); return -1; } - if (fcntl(conn_sock, F_SETFL, flags | O_NONBLOCK) == -1) + if (fcntl(conn_socket_fd, F_SETFL, flags | O_NONBLOCK) == -1) { perror("server: fcntl(conn_sock)"); - close(conn_sock); + close(conn_socket_fd); return -1; } // Fulfilled the maximum number of clients - if (add_socket(client_socket_manager, conn_sock) == -1) + if (add_socket(client_socket_manager, conn_socket_fd) == -1) { puts("No more room for clients\n"); - close(conn_sock); + close(conn_socket_fd); return 0; } struct epoll_event event; - event.events = EPOLLIN | EPOLLET; - event.data.fd = conn_sock; - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &event) == -1) + event.events = EPOLLIN | EPOLLOUT; + event.data.fd = conn_socket_fd; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_socket_fd, &event) == -1) { perror("server: epoll_ctl()"); - close(conn_sock); + close(conn_socket_fd); return -1; } @@ -390,44 +381,58 @@ int handle_new_connection(int listen_sock, int epoll_fd, SocketManager *client_s * processes it, and sends a response back to the client. The function continues to read and send data * until the client disconnects or an error occurs. * - * @param[in] client_sock The file descriptor of the client socket. + * @param[in] client_socket_data The socket data for the client socket. + * @param[in] event The epoll event for the client socket. * @return The status of the client connection. * @retval 1 The client is still connected. * @retval 0 The client has disconnected. * @retval -1 An error occurred during communication. */ -int handle_client(int client_sock) +int handle_client(SocketData *client_socket_data, struct epoll_event event) { - char buf[BUFFER_SIZE]; - ssize_t received_bytes; - while ((received_bytes = recv(client_sock, buf, BUFFER_SIZE - 1, 0)) > 0) + // Check if the client socket is ready to read + if (event.events & EPOLLIN) { - printf("received_bytes: %ld\n", received_bytes); - buf[received_bytes] = '\0'; // Null-terminate the string - if (send(client_sock, buf, received_bytes, 0) == -1) + ssize_t received_bytes; + if ((received_bytes = recv(client_socket_data->socket_fd, client_socket_data->buffer, BUFFER_SIZE - 1, 0)) > 0) { - perror("server: send()"); - return -1; + printf("received_bytes: %ld\n", received_bytes); + client_socket_data->buffer[received_bytes] = '\0'; // Null-terminate the string + } + else if (received_bytes == 0) + { + puts("Connection closed\n"); + return 0; + } + else + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + return 1; + } + else + { + perror("server: recv()"); + return -1; + } } } - if (received_bytes == -1) + // Check if the client socket is ready to write + if (event.events & EPOLLOUT) { - if (errno == EAGAIN || errno == EWOULDBLOCK) + ssize_t sent_bytes; + if ((sent_bytes = send(client_socket_data->socket_fd, client_socket_data->buffer, strlen(client_socket_data->buffer), 0)) == -1) { - return 1; + perror("server: send()"); + return -1; } else { - perror("server: recv()"); - return -1; + memmove(client_socket_data->buffer, client_socket_data->buffer + sent_bytes, strlen(client_socket_data->buffer) - sent_bytes); + client_socket_data->buffer[strlen(client_socket_data->buffer) - sent_bytes] = '\0'; } } - else if (received_bytes == 0) - { - puts("Connection closed\n"); - return 0; - } return 1; } diff --git a/utils.c b/utils.c index 4936f08..161fa10 100644 --- a/utils.c +++ b/utils.c @@ -7,36 +7,48 @@ void init_socket_manager(SocketManager *manager, int max_size) { - manager->sockets = (int *)malloc(max_size * sizeof(int)); + manager->sockets = (SocketData *)malloc(max_size * sizeof(SocketData)); manager->free_indices = (int *)malloc(max_size * sizeof(int)); manager->max_size = max_size; manager->top = max_size - 1; for (int i = 0; i < max_size; i++) { - manager->sockets[i] = -1; + manager->sockets[i].socket_fd = -1; manager->free_indices[i] = i; } } -int add_socket(SocketManager *manager, int sock) +SocketData *find_socket(SocketManager *manager, int socket_fd) +{ + for (int i = 0; i < manager->max_size; i++) + { + if (manager->sockets[i].socket_fd == socket_fd) + { + return &manager->sockets[i]; + } + } + return NULL; +} + +int add_socket(SocketManager *manager, int socket_fd) { if (manager->top < 0) { return -1; } int index = manager->free_indices[manager->top--]; - manager->sockets[index] = sock; + manager->sockets[index].socket_fd = socket_fd; return index; } -int remove_socket(SocketManager *manager, int sock) +int remove_socket(SocketManager *manager, int socket_fd) { for (int i = 0; i < manager->max_size; i++) { - if (manager->sockets[i] == sock) + if (manager->sockets[i].socket_fd == socket_fd) { - manager->sockets[i] = -1; + manager->sockets[i].socket_fd = -1; manager->free_indices[++manager->top] = i; return 0; } @@ -48,7 +60,7 @@ int close_all_sockets(SocketManager *manager) { for (int i = 0; i < manager->max_size; i++) { - if (manager->sockets[i] != -1) + if (manager->sockets[i].socket_fd != -1) { close(manager->sockets[i]); } @@ -69,15 +81,3 @@ int parse_port(const char *port_str) } return (int)port; } - -bool contains(int *array, int size, int value) -{ - for (int i = 0; i < size; i++) - { - if (array[i] == value) - { - return true; - } - } - return false; -} diff --git a/utils.h b/utils.h index 7fdb486..3792211 100644 --- a/utils.h +++ b/utils.h @@ -3,19 +3,27 @@ #ifndef UTILS_H #define UTILS_H +#define BUFFER_SIZE 256 + +typedef struct +{ + int socket_fd; + char buffer[BUFFER_SIZE]; // TODO: Use dynamic memory allocation for the buffer +} SocketData; + typedef struct { - int *sockets; + SocketData *sockets; int *free_indices; int max_size; int top; } SocketManager; void init_socket_manager(SocketManager *manager, int max_size); -int add_socket(SocketManager *manager, int sock); -int remove_socket(SocketManager *manager, int sock); +SocketData *find_socket(SocketManager *manager, int socket_fd); +int add_socket(SocketManager *manager, int socket_fd); +int remove_socket(SocketManager *manager, int socket_fd); int close_all_sockets(SocketManager *manager); int parse_port(const char *port_str); -bool contains(int *array, int size, int value); #endif From 2e313ca5886d6ba4625cdef9fc95837e736a99c4 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 06:22:59 +0000 Subject: [PATCH 37/41] Modify client to ensure complete send and receive of data --- client.c | 83 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/client.c b/client.c index 104c57c..75b1792 100644 --- a/client.c +++ b/client.c @@ -22,8 +22,8 @@ int main(int argc, char **argv) } // Create a connected socket - int sock; - if ((sock = create_connected_socket(argv[1], argv[2])) == -1) + int socket_fd; + if ((socket_fd = create_connected_socket(argv[1], argv[2])) == -1) { puts("Failed to create a connected socket\n"); exit(1); @@ -32,35 +32,62 @@ int main(int argc, char **argv) while (1) { char buf[BUFFER_SIZE]; - fgets(buf, BUFFER_SIZE, stdin); // Null-terminate the string - - size_t len = strlen(buf); - ssize_t sent_bytes = send(sock, buf, len, 0); - if (sent_bytes == -1) + char *input = fgets(buf, BUFFER_SIZE, stdin); + if (input == NULL) { - perror("client: send()"); - close(sock); - exit(1); + // Check if fgets() reached EOF + if (feof(stdin)) + { + puts("EOF detected\n"); + break; + } + else + { + perror("client: fgets()"); + close(socket_fd); + exit(1); + } } - ssize_t received_bytes = recv(sock, buf, BUFFER_SIZE - 1, 0); - if (received_bytes == -1) + // Send the input to the server + size_t len = strlen(buf); + size_t total_sent = 0; + while (total_sent < len) { - perror("client: recv()"); - close(sock); - exit(1); + ssize_t sent_bytes = send(socket_fd, buf + total_sent, len - total_sent, 0); + if (sent_bytes == -1) + { + perror("client: send()"); + close(socket_fd); + exit(1); + } + total_sent += sent_bytes; } - else if (received_bytes == 0) + + // Receive the response from the server + size_t total_received = 0; + while (total_received < len) { - puts("Connection closed by server\n"); - close(sock); - exit(1); + ssize_t received_bytes = recv(socket_fd, buf + total_received, len - total_received, 0); + if (received_bytes == -1) + { + perror("client: recv()"); + close(socket_fd); + exit(1); + } + else if (received_bytes == 0) + { + puts("Connection closed by server\n"); + close(socket_fd); + exit(1); + } + total_received += received_bytes; } printf("%s", buf); } - close(sock); + close(socket_fd); return 0; } @@ -100,10 +127,10 @@ int create_connected_socket(const char *ip, const char *port_str) } // Create a socket and connect to the server - int sock; + int socket_fd; if (is_ipv4 == 1) { - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) + if ((socket_fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("client: socket()"); return -1; @@ -112,16 +139,16 @@ int create_connected_socket(const char *ip, const char *port_str) server_addr4.sin_family = PF_INET; server_addr4.sin_port = htons(port); - if (connect(sock, (struct sockaddr *)&server_addr4, sizeof(server_addr4)) == -1) + if (connect(socket_fd, (struct sockaddr *)&server_addr4, sizeof(server_addr4)) == -1) { perror("client: connect() using IPv4"); - close(sock); + close(socket_fd); return -1; } } else if (is_ipv6 == 1) { - if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) == -1) + if ((socket_fd = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { perror("client: socket() using IPv6"); return -1; @@ -130,10 +157,10 @@ int create_connected_socket(const char *ip, const char *port_str) server_addr6.sin6_family = PF_INET6; server_addr6.sin6_port = htons(port); - if (connect(sock, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) == -1) + if (connect(socket_fd, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) == -1) { perror("client: connect() using IPv6"); - close(sock); + close(socket_fd); return -1; } } @@ -144,5 +171,5 @@ int create_connected_socket(const char *ip, const char *port_str) } printf("Connected to %s, %d\n", ip, port); - return sock; + return socket_fd; } From 1632d0f683f2402a5645cbfba558e3835d209aa3 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 07:52:32 +0000 Subject: [PATCH 38/41] Use fwrite() instead of printf() --- client.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client.c b/client.c index 75b1792..7223ffa 100644 --- a/client.c +++ b/client.c @@ -32,8 +32,7 @@ int main(int argc, char **argv) while (1) { char buf[BUFFER_SIZE]; - char *input = fgets(buf, BUFFER_SIZE, stdin); - if (input == NULL) + if (fgets(buf, BUFFER_SIZE, stdin) == NULL) { // Check if fgets() reached EOF if (feof(stdin)) @@ -50,11 +49,11 @@ int main(int argc, char **argv) } // Send the input to the server - size_t len = strlen(buf); + size_t bytes_read = strlen(buf); size_t total_sent = 0; - while (total_sent < len) + while (total_sent < bytes_read) { - ssize_t sent_bytes = send(socket_fd, buf + total_sent, len - total_sent, 0); + ssize_t sent_bytes = send(socket_fd, buf + total_sent, bytes_read - total_sent, 0); if (sent_bytes == -1) { perror("client: send()"); @@ -66,9 +65,9 @@ int main(int argc, char **argv) // Receive the response from the server size_t total_received = 0; - while (total_received < len) + while (total_received < bytes_read) { - ssize_t received_bytes = recv(socket_fd, buf + total_received, len - total_received, 0); + ssize_t received_bytes = recv(socket_fd, buf + total_received, bytes_read - total_received, 0); if (received_bytes == -1) { perror("client: recv()"); @@ -84,7 +83,8 @@ int main(int argc, char **argv) total_received += received_bytes; } - printf("%s", buf); + // Print the received data + fwrite(buf, 1, total_received, stdout); } close(socket_fd); From 023217628aed0b186ff5c3371fb683ade0c3ffb8 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 08:26:05 +0000 Subject: [PATCH 39/41] Add error handling about EINTR --- client.c | 27 +++++++++++++++-- server.c | 90 +++++++++++++++++++++++++++++--------------------------- 2 files changed, 71 insertions(+), 46 deletions(-) diff --git a/client.c b/client.c index 7223ffa..03e2cb6 100644 --- a/client.c +++ b/client.c @@ -56,6 +56,10 @@ int main(int argc, char **argv) ssize_t sent_bytes = send(socket_fd, buf + total_sent, bytes_read - total_sent, 0); if (sent_bytes == -1) { + if (errno == EINTR) + { + continue; + } perror("client: send()"); close(socket_fd); exit(1); @@ -70,6 +74,10 @@ int main(int argc, char **argv) ssize_t received_bytes = recv(socket_fd, buf + total_received, bytes_read - total_received, 0); if (received_bytes == -1) { + if (errno == EINTR) + { + continue; + } perror("client: recv()"); close(socket_fd); exit(1); @@ -84,7 +92,12 @@ int main(int argc, char **argv) } // Print the received data - fwrite(buf, 1, total_received, stdout); + if (fwrite(buf, 1, total_received, stdout) != total_received) + { + perror("client: fwrite()"); + close(socket_fd); + exit(1); + } } close(socket_fd); @@ -139,8 +152,12 @@ int create_connected_socket(const char *ip, const char *port_str) server_addr4.sin_family = PF_INET; server_addr4.sin_port = htons(port); - if (connect(socket_fd, (struct sockaddr *)&server_addr4, sizeof(server_addr4)) == -1) + while (connect(socket_fd, (struct sockaddr *)&server_addr4, sizeof(server_addr4)) == -1) { + if (errno == EINTR) + { + continue; + } perror("client: connect() using IPv4"); close(socket_fd); return -1; @@ -157,8 +174,12 @@ int create_connected_socket(const char *ip, const char *port_str) server_addr6.sin6_family = PF_INET6; server_addr6.sin6_port = htons(port); - if (connect(socket_fd, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) == -1) + while (connect(socket_fd, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) == -1) { + if (errno == EINTR) + { + continue; + } perror("client: connect() using IPv6"); close(socket_fd); return -1; diff --git a/server.c b/server.c index 391726e..03475e0 100644 --- a/server.c +++ b/server.c @@ -18,7 +18,6 @@ #define MAX_CLIENTS 30 #define MAX_EVENTS 10 -void cleanup(int epoll_fd, SocketManager *listen_socket_manager, SocketManager *client_socket_manager); int create_listen_sockets(const char *port_str, SocketManager *listen_socket_manager); int add_listen_sockets_to_epoll(int epoll_fd, SocketManager *listen_socket_manager); int handle_new_connection(int listen_socket_fd, int epoll_fd, SocketManager *client_socket_manager); @@ -85,6 +84,10 @@ int main(int argc, char **argv) int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds == -1) { + if (errno == EINTR) + { + continue; + } perror("server: epoll_wait()"); exit_code = 1; goto cleanup; @@ -130,26 +133,6 @@ int main(int argc, char **argv) return 0; } -/** - * @brief Clean up the resources used by the server. - * - * This function closes all the client and listen sockets and the epoll instance. - * - * @param[in] epoll_fd The file descriptor of the epoll instance. - * @param[in] listen_socket_manager The socket manager for listening sockets. - * @param[in] client_socket_manager The socket manager for client sockets. - * @return void - */ -void cleanup(int epoll_fd, SocketManager *listen_socket_manager, SocketManager *client_socket_manager) -{ - close_all_sockets(client_socket_manager); - close_all_sockets(listen_socket_manager); - if (epoll_fd != -1) - { - close(epoll_fd); - } -} - /** * @brief Create and bind listening sockets for the server. * @@ -257,22 +240,32 @@ int add_listen_sockets_to_epoll(int epoll_fd, SocketManager *listen_socket_manag continue; } - // Set the listen socket to non-blocking mode - int flags = fcntl(listen_socket_fd, F_GETFL, 0); - if (flags == -1) + // Get the current flags for the listen socket + int flags; + while ((flags = fcntl(listen_socket_fd, F_GETFL, 0)) == -1) { + if (errno == EINTR) + { + continue; + } perror("server: fcntl()"); return -1; } - if (fcntl(listen_socket_fd, F_SETFL, flags | O_NONBLOCK) == -1) + + // Set the listen socket to non-blocking mode + while (fcntl(listen_socket_fd, F_SETFL, flags | O_NONBLOCK) == -1) { + if (errno == EINTR) + { + continue; + } perror("server: fcntl()"); return -1; } - event.events = EPOLLIN; - event.data.fd = listen_socket_fd; // Add the listen socket to the epoll instance + event.events = EPOLLIN; + event.data.fd = listen_socket_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_socket_fd, &event) == -1) { perror("server: epoll_ctl()"); @@ -305,7 +298,7 @@ int handle_new_connection(int listen_socket_fd, int epoll_fd, SocketManager *cli if ((conn_socket_fd = accept(listen_socket_fd, (struct sockaddr *)&client_addr, &addr_len)) == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { return 0; } @@ -316,16 +309,26 @@ int handle_new_connection(int listen_socket_fd, int epoll_fd, SocketManager *cli } } - // Set the connection socket to non-blocking mode - int flags = fcntl(conn_socket_fd, F_GETFL, 0); - if (flags == -1) + // Get the current flags for the connection socket + int flags; + while ((flags = fcntl(conn_socket_fd, F_GETFL, 0)) == -1) { + if (errno == EINTR) + { + continue; + } perror("server: fcntl(conn_sock)"); close(conn_socket_fd); return -1; } - if (fcntl(conn_socket_fd, F_SETFL, flags | O_NONBLOCK) == -1) + + // Set the connection socket to non-blocking mode + while (fcntl(conn_socket_fd, F_SETFL, flags | O_NONBLOCK) == -1) { + if (errno == EINTR) + { + continue; + } perror("server: fcntl(conn_sock)"); close(conn_socket_fd); return -1; @@ -406,15 +409,12 @@ int handle_client(SocketData *client_socket_data, struct epoll_event event) } else { - if (errno == EAGAIN || errno == EWOULDBLOCK) + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { return 1; } - else - { - perror("server: recv()"); - return -1; - } + perror("server: recv()"); + return -1; } } @@ -422,15 +422,19 @@ int handle_client(SocketData *client_socket_data, struct epoll_event event) if (event.events & EPOLLOUT) { ssize_t sent_bytes; - if ((sent_bytes = send(client_socket_data->socket_fd, client_socket_data->buffer, strlen(client_socket_data->buffer), 0)) == -1) + if ((sent_bytes = send(client_socket_data->socket_fd, client_socket_data->buffer, strlen(client_socket_data->buffer), 0)) >= 0) { - perror("server: send()"); - return -1; + memmove(client_socket_data->buffer, client_socket_data->buffer + sent_bytes, strlen(client_socket_data->buffer) - sent_bytes); + client_socket_data->buffer[strlen(client_socket_data->buffer) - sent_bytes] = '\0'; } else { - memmove(client_socket_data->buffer, client_socket_data->buffer + sent_bytes, strlen(client_socket_data->buffer) - sent_bytes); - client_socket_data->buffer[strlen(client_socket_data->buffer) - sent_bytes] = '\0'; + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + { + return 1; + } + perror("server: send()"); + return -1; } } From f7f6b1f9ea5e34d637b9afdf3bf149a348114a35 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 09:13:07 +0000 Subject: [PATCH 40/41] Implement close_with_retry() instead of close() to deal with EINTR --- client.c | 16 ++++++++-------- server.c | 22 +++++++++++----------- utils.c | 17 ++++++++++++++++- utils.h | 1 + 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/client.c b/client.c index 03e2cb6..2be325c 100644 --- a/client.c +++ b/client.c @@ -43,7 +43,7 @@ int main(int argc, char **argv) else { perror("client: fgets()"); - close(socket_fd); + close_with_retry(socket_fd); exit(1); } } @@ -61,7 +61,7 @@ int main(int argc, char **argv) continue; } perror("client: send()"); - close(socket_fd); + close_with_retry(socket_fd); exit(1); } total_sent += sent_bytes; @@ -79,13 +79,13 @@ int main(int argc, char **argv) continue; } perror("client: recv()"); - close(socket_fd); + close_with_retry(socket_fd); exit(1); } else if (received_bytes == 0) { puts("Connection closed by server\n"); - close(socket_fd); + close_with_retry(socket_fd); exit(1); } total_received += received_bytes; @@ -95,12 +95,12 @@ int main(int argc, char **argv) if (fwrite(buf, 1, total_received, stdout) != total_received) { perror("client: fwrite()"); - close(socket_fd); + close_with_retry(socket_fd); exit(1); } } - close(socket_fd); + close_with_retry(socket_fd); return 0; } @@ -159,7 +159,7 @@ int create_connected_socket(const char *ip, const char *port_str) continue; } perror("client: connect() using IPv4"); - close(socket_fd); + close_with_retry(socket_fd); return -1; } } @@ -181,7 +181,7 @@ int create_connected_socket(const char *ip, const char *port_str) continue; } perror("client: connect() using IPv6"); - close(socket_fd); + close_with_retry(socket_fd); return -1; } } diff --git a/server.c b/server.c index 03475e0..b2b9727 100644 --- a/server.c +++ b/server.c @@ -111,8 +111,8 @@ int main(int argc, char **argv) if (handle_client(socket_data, events[i]) <= 0) { // The server itself does not exit even if the processing with the client ends abnormally. + close_with_retry(socket_data->socket_fd); remove_socket(&client_socket_manager, socket_data->socket_fd); - close(socket_data->socket_fd); } } } @@ -121,7 +121,7 @@ int main(int argc, char **argv) cleanup: if (epoll_fd != -1) { - close(epoll_fd); + close_with_retry(epoll_fd); } close_all_sockets(&client_socket_manager); close_all_sockets(&listen_socket_manager); @@ -175,7 +175,7 @@ int create_listen_sockets(const char *port_str, SocketManager *listen_socket_man if (setsockopt(listen_socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("server: setsockopt(SOL_SOCKET, SO_REUSEADDR)"); - close(listen_socket_fd); + close_with_retry(listen_socket_fd); continue; } @@ -185,7 +185,7 @@ int create_listen_sockets(const char *port_str, SocketManager *listen_socket_man if (setsockopt(listen_socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(int)) == -1) { perror("server: setsockopt(IPPROTO_IPV6, IPV6_V6ONLY)"); - close(listen_socket_fd); + close_with_retry(listen_socket_fd); continue; }; } @@ -193,14 +193,14 @@ int create_listen_sockets(const char *port_str, SocketManager *listen_socket_man if (bind(listen_socket_fd, res->ai_addr, res->ai_addrlen) == -1) { perror("server: bind()"); - close(listen_socket_fd); + close_with_retry(listen_socket_fd); continue; } if (listen(listen_socket_fd, 5) == -1) { perror("server: listen()"); - close(listen_socket_fd); + close_with_retry(listen_socket_fd); return -1; } @@ -318,7 +318,7 @@ int handle_new_connection(int listen_socket_fd, int epoll_fd, SocketManager *cli continue; } perror("server: fcntl(conn_sock)"); - close(conn_socket_fd); + close_with_retry(conn_socket_fd); return -1; } @@ -330,7 +330,7 @@ int handle_new_connection(int listen_socket_fd, int epoll_fd, SocketManager *cli continue; } perror("server: fcntl(conn_sock)"); - close(conn_socket_fd); + close_with_retry(conn_socket_fd); return -1; } @@ -338,7 +338,7 @@ int handle_new_connection(int listen_socket_fd, int epoll_fd, SocketManager *cli if (add_socket(client_socket_manager, conn_socket_fd) == -1) { puts("No more room for clients\n"); - close(conn_socket_fd); + close_with_retry(conn_socket_fd); return 0; } @@ -348,7 +348,7 @@ int handle_new_connection(int listen_socket_fd, int epoll_fd, SocketManager *cli if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_socket_fd, &event) == -1) { perror("server: epoll_ctl()"); - close(conn_socket_fd); + close_with_retry(conn_socket_fd); return -1; } @@ -399,7 +399,7 @@ int handle_client(SocketData *client_socket_data, struct epoll_event event) ssize_t received_bytes; if ((received_bytes = recv(client_socket_data->socket_fd, client_socket_data->buffer, BUFFER_SIZE - 1, 0)) > 0) { - printf("received_bytes: %ld\n", received_bytes); + printf("received_bytes: %ld (fd: %d)\n", received_bytes, client_socket_data->socket_fd); client_socket_data->buffer[received_bytes] = '\0'; // Null-terminate the string } else if (received_bytes == 0) diff --git a/utils.c b/utils.c index 161fa10..a476f4d 100644 --- a/utils.c +++ b/utils.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "utils.h" @@ -62,7 +63,7 @@ int close_all_sockets(SocketManager *manager) { if (manager->sockets[i].socket_fd != -1) { - close(manager->sockets[i]); + close_with_retry(manager->sockets[i].socket_fd); } } free(manager->sockets); @@ -81,3 +82,17 @@ int parse_port(const char *port_str) } return (int)port; } + +int close_with_retry(int fd) +{ + while (close(fd) == -1) + { + if (errno == EINTR) + { + continue; + } + perror("close()"); + return -1; + } + return 0; +} diff --git a/utils.h b/utils.h index 3792211..f3d1640 100644 --- a/utils.h +++ b/utils.h @@ -26,4 +26,5 @@ int remove_socket(SocketManager *manager, int socket_fd); int close_all_sockets(SocketManager *manager); int parse_port(const char *port_str); +int close_with_retry(int fd); #endif From 9f70e15af670fa5df68788414e5677226c52a115 Mon Sep 17 00:00:00 2001 From: tomoya-honda Date: Fri, 17 Jan 2025 10:01:25 +0000 Subject: [PATCH 41/41] Monitor signal to shutdown after cleanup --- server.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/server.c b/server.c index b2b9727..df54b25 100644 --- a/server.c +++ b/server.c @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include #include @@ -9,7 +11,6 @@ #include #include #include -#include #include "utils.h" @@ -18,10 +19,14 @@ #define MAX_CLIENTS 30 #define MAX_EVENTS 10 +int pipe_fds[2]; + int create_listen_sockets(const char *port_str, SocketManager *listen_socket_manager); int add_listen_sockets_to_epoll(int epoll_fd, SocketManager *listen_socket_manager); +int add_pipe_to_epoll(int epoll_fd); int handle_new_connection(int listen_socket_fd, int epoll_fd, SocketManager *client_socket_manager); int handle_client(SocketData *client_socket_data, struct epoll_event event); +void handle_sigint(int sig); int main(int argc, char **argv) { @@ -74,6 +79,22 @@ int main(int argc, char **argv) goto cleanup; } + // Create a pipe for signal handling + if (pipe(pipe_fds) == -1) + { + perror("server: pipe()"); + exit_code = 1; + goto cleanup; + } + + // Add the pipe to the epoll instance + if (add_pipe_to_epoll(epoll_fd) == -1) + { + puts("Failed to add pipe to epoll\n"); + exit_code = 1; + goto cleanup; + } + puts("Monitoring for events..."); struct epoll_event events[MAX_EVENTS]; @@ -115,6 +136,27 @@ int main(int argc, char **argv) remove_socket(&client_socket_manager, socket_data->socket_fd); } } + // Check if the event is for the pipe + else if (events[i].data.fd == pipe_fds[0]) + { + int sig; + while (read(pipe_fds[0], &sig, sizeof(sig)) == -1) + { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + { + continue; + } + perror("server: read()"); + exit_code = 1; + goto cleanup; + } + + if (sig == SIGINT) + { + puts("Received SIGINT, exiting..."); + goto cleanup; + } + } } } @@ -125,6 +167,8 @@ int main(int argc, char **argv) } close_all_sockets(&client_socket_manager); close_all_sockets(&listen_socket_manager); + close_with_retry(pipe_fds[0]); + close_with_retry(pipe_fds[1]); if (exit_code != 0) { @@ -276,6 +320,65 @@ int add_listen_sockets_to_epoll(int epoll_fd, SocketManager *listen_socket_manag return 0; } +/** + * @brief Add the pipe to the epoll instance. + * + * This function sets the pipe read end to non-blocking mode and adds it to the epoll instance. + * + * @param[in] epoll_fd The file descriptor of the epoll instance. + * @return The status of the process. + * @retval 0 The pipe was successfully added to the epoll instance. + * @retval -1 An error occurred during the process. + */ +int add_pipe_to_epoll(int epoll_fd) +{ + // Get the current flags for + int flags; + while ((flags = fcntl(pipe_fds[0], F_GETFL, 0)) == -1) + { + if (errno == EINTR) + { + continue; + } + perror("server: fcntl()"); + return -1; + } + + // Set the pipe read end to non-blocking mode + while (fcntl(pipe_fds[0], F_SETFL, flags | O_NONBLOCK) == -1) + { + if (errno == EINTR) + { + continue; + } + perror("server: fcntl()"); + return -1; + } + + // Set up a signal handler for SIGINT + struct sigaction sa; + sa.sa_handler = handle_sigint; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGINT, &sa, NULL) == -1) + { + perror("server: sigaction()"); + return -1; + } + + // Add the pipe read end to the epoll instance + struct epoll_event event; + event.events = EPOLLIN; + event.data.fd = pipe_fds[0]; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipe_fds[0], &event) == -1) + { + perror("server: epoll_ctl()"); + return -1; + } + + return 0; +} + /** * @brief Handle a new connection on the listening socket. * @@ -440,3 +543,23 @@ int handle_client(SocketData *client_socket_data, struct epoll_event event) return 1; } + +/** + * @brief Handle the SIGINT signal + * + * This function writes the signal number to the pipe to notify the main loop to exit. + * + * @param[in] sig The signal number. + */ +void handle_sigint(int sig) +{ + while (write(pipe_fds[1], &sig, sizeof(sig)) == -1) + { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + { + continue; + } + perror("server: write()"); + exit(1); + } +}