From ffd84005fea81a4c48e05678f564c6be198b4699 Mon Sep 17 00:00:00 2001 From: Satorien Date: Thu, 1 Jan 2026 19:25:50 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E3=82=B5=E3=83=BC=E3=83=90?= =?UTF-8?q?=E3=83=BB=E3=82=AF=E3=83=A9=E3=82=A4=E3=82=A2=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=81=AE=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Makefile | 10 ++++ client.c | 75 +++++++++++++++++++++++++++++ server.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.c | 34 ++++++++++++++ utils.h | 9 ++++ 6 files changed, 265 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 client.c create mode 100644 server.c create mode 100644 utils.c create mode 100644 utils.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d009987 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +run_* \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7d99d02 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +all: server client + +server: server.c utils.c + gcc server.c utils.c -o run_server -Wall -Wextra -pedantic -std=c11 + +client: client.c utils.c + gcc client.c utils.c -o run_client -Wall -Wextra -pedantic -std=c11 + +clean: + rm -f run_server run_client \ No newline at end of file diff --git a/client.c b/client.c new file mode 100644 index 0000000..8894273 --- /dev/null +++ b/client.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include "utils.h" + +#define PORT_NUMBER 8080 +#define SERVER_IP "127.0.0.1" +#define BUFFER_SIZE 64 + +void connect_client_to_server(int client_socket_fd) { + // サーバアドレス構造体 + struct sockaddr_in server_address; + server_address.sin_family = AF_INET; + // ホストバイトオーダーからネットワークバイトオーダーへ変換 + server_address.sin_port = htons(PORT_NUMBER); + inet_pton(AF_INET, SERVER_IP, &server_address.sin_addr); + + // サーバへの接続 + if (connect(client_socket_fd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) { + handle_error("Connection to server failed"); + } +} + +char* generate_request(const char *function, const char *query) { + // リクエストの生成 + char *request = calloc(BUFFER_SIZE, 1); + + if (strcmp(function, "calc") == 0) { + snprintf(request, BUFFER_SIZE, "GET /calc?query=%s HTTP/1.1\r\n", query); + } else { + handle_error("Unsupported function"); + } + return request; +} + +void send_request_and_get_response(int client_socket_fd, char *request) { + write(client_socket_fd, request, strlen(request)); + log_msg("Request sent:\n%s\n", request); + char *buffer = calloc(BUFFER_SIZE, 1); + read(client_socket_fd, buffer, BUFFER_SIZE - 1); + log_msg("Response from server:\n%s\n\n", buffer); + free(buffer); +} + +int main(int argc, char **argv){ + // 引数の数をチェック + if (argc < 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + fprintf(stderr, "Example: %s calc 18+20\n", argv[0]); + exit(EXIT_FAILURE); + } + + // ソケットの初期化 + int client_socket = initialize_socket(); + log_msg("Client socket initialized.\n"); + + // サーバへの接続 + connect_client_to_server(client_socket); + log_msg("Connected to server %s:%d.\n", SERVER_IP, PORT_NUMBER); + + // リクエストの送信とレスポンスの受信 + char *function = argv[1]; + char *query = argv[2]; + char *request = generate_request(function, query); + send_request_and_get_response(client_socket, request); + log_msg("Request sent and response received.\n"); + + // ソケットのクローズ + close_socket(client_socket); + log_msg("Client socket closed.\n"); + free(request); + return 0; +} \ No newline at end of file diff --git a/server.c b/server.c new file mode 100644 index 0000000..41fc893 --- /dev/null +++ b/server.c @@ -0,0 +1,136 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include "utils.h" + +#define PORT_NUMBER 8080 +#define LISTEN_BACKLOG 5 +#define BUFFER_SIZE 64 + +volatile sig_atomic_t is_server_running = 1; + +void signal_handler() { + is_server_running = 0; +} + +void bind_socket(int socket_fd) { + // サーバアドレス構造体: sin_family, sin_addr, sin_port + struct sockaddr_in address_info; + address_info.sin_family = AF_INET; + address_info.sin_addr.s_addr = INADDR_ANY; + address_info.sin_port = htons(PORT_NUMBER); + + // ソケットのバインド + if (bind(socket_fd, (struct sockaddr *) &address_info, sizeof(address_info)) < 0) { + handle_error("Bind failed"); + } +} + +void listen_for_connections(int socket_fd) { + // 接続待機 + if (listen(socket_fd, LISTEN_BACKLOG) < 0) { + handle_error("Listen failed"); + } +} + +int accept_connection(int server_socket_fd) { + // クライアントからの接続受付 + struct sockaddr_in client_address; + socklen_t client_address_len = sizeof(client_address); + + int client_socket_fd = accept(server_socket_fd, (struct sockaddr *) &client_address, &client_address_len); + if (client_socket_fd < 0) { + // シグナルによる中断の場合は-1を返す + if (errno == EINTR) { + return -1; + } + handle_error("Accept failed"); + } + + return client_socket_fd; +} + +char* calculate_query(const char *query) { + // クエリの計算処理 + // 例: "query=2+10" -> 12 + int operand1, operand2; + char operator; + char *buffer = calloc(BUFFER_SIZE, 1); + sscanf(query, "query=%d%c%d", &operand1, &operator, &operand2); + switch (operator) { + case '+': snprintf(buffer, BUFFER_SIZE, "%d", operand1 + operand2); return buffer; + default: handle_error("Unsupported operator"); return NULL; + } +} + +void handle_client_request(int client_socket_fd) { + // リクエストの処理 + char *buffer = calloc(BUFFER_SIZE, 1); + read(client_socket_fd, buffer, BUFFER_SIZE - 1); + log_msg("Request received:\n%s\n", buffer); + + // /calc エンドポイントの処理 + if (strncmp(buffer, "GET /calc?", 10) == 0) { + char *calculation = calculate_query(buffer + 10); + char response[512]; + snprintf(response, sizeof(response), "HTTP/1.1 200 OK\r\nContent-Length:2\r\n\r\n%s", calculation); + write(client_socket_fd, response, strlen(response)); + log_msg("Response sent:\n%s\n", response); + } else { + handle_error("Unsupported request method"); + } +} + +int main(){ + // ソケットの初期化 + int server_socket = initialize_socket(); + log_msg("Server socket initialized.\n"); + + // アドレスの再利用を有効化 + int optval = 1; + if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) { + handle_error("setsockopt SO_REUSEADDR failed"); + } + + // ソケットのバインド + bind_socket(server_socket); + log_msg("Server socket bound to port %d.\n", PORT_NUMBER); + + // クライアントからの接続を待機 + listen_for_connections(server_socket); + log_msg("Server is listening on port %d...\n----------\n", PORT_NUMBER); + + // シグナルハンドラの設定 + struct sigaction sa; + sa.sa_handler = signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; // SA_RESTARTを設定しない(システムコールを自動再開しない) + if (sigaction(SIGINT, &sa, NULL) == -1) { + handle_error("sigaction failed"); + } + + // リクエストの受信と処理 + while (is_server_running) { + int client_socket = accept_connection(server_socket); + + if (client_socket < 0) continue; + + log_msg("Client connected.\n"); + + handle_client_request(client_socket); + log_msg("Client request handled.\n"); + + close_socket(client_socket); + log_msg("Client socket closed.\n----------\n"); + } + + // サーバソケットのクローズ + close_socket(server_socket); + log_msg("\r\nServer shutdown gracefully.\n"); + return 0; +} \ No newline at end of file diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..de5adda --- /dev/null +++ b/utils.c @@ -0,0 +1,34 @@ +#include "utils.h" +#include +#include +#include +#include +#include + +void handle_error(const char *msg) { + do { + perror(msg); exit(EXIT_FAILURE); + } while (0); +} + +void log_msg(const char *format, ...) { + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); +} + +int initialize_socket() { + // ソケットの初期化 IPv6の通信はAF_INET6を使用 + int tcp_socket = socket(AF_INET, SOCK_STREAM, 0); + if (tcp_socket < 0) handle_error("Socket creation failed"); + + return tcp_socket; +} + +void close_socket(int socket_fd) { + // ソケットのクローズ + if (close(socket_fd) < 0) { + handle_error("Close failed"); + } +} \ No newline at end of file diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..64d1744 --- /dev/null +++ b/utils.h @@ -0,0 +1,9 @@ +#ifndef UTILS_H +#define UTILS_H + +void handle_error(const char *msg); +void log_msg(const char *format, ...); +int initialize_socket(); +void close_socket(int socket_fd); + +#endif // UTILS_H \ No newline at end of file From f1889912af782b35f0abc68546d625d9a3fa6da0 Mon Sep 17 00:00:00 2001 From: Satorien Date: Fri, 2 Jan 2026 15:51:26 +0900 Subject: [PATCH 2/4] fix: error handling --- client.c | 44 ++++++++++++++++++++++++++++++++---- server.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++-------- utils.c | 21 +++++++++-------- 3 files changed, 109 insertions(+), 25 deletions(-) diff --git a/client.c b/client.c index 8894273..c1db9b3 100644 --- a/client.c +++ b/client.c @@ -2,20 +2,23 @@ #include #include #include +#include #include #include "utils.h" #define PORT_NUMBER 8080 #define SERVER_IP "127.0.0.1" +#define IP_VERSION "IPv4" #define BUFFER_SIZE 64 void connect_client_to_server(int client_socket_fd) { // サーバアドレス構造体 struct sockaddr_in server_address; - server_address.sin_family = AF_INET; + int domain = (strcmp(IP_VERSION, "IPv6") == 0) ? AF_INET6 : AF_INET; + server_address.sin_family = domain; // ホストバイトオーダーからネットワークバイトオーダーへ変換 server_address.sin_port = htons(PORT_NUMBER); - inet_pton(AF_INET, SERVER_IP, &server_address.sin_addr); + inet_pton(domain, SERVER_IP, &server_address.sin_addr); // サーバへの接続 if (connect(client_socket_fd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) { @@ -36,10 +39,41 @@ char* generate_request(const char *function, const char *query) { } void send_request_and_get_response(int client_socket_fd, char *request) { - write(client_socket_fd, request, strlen(request)); + // タイムアウトの設定(10秒) + struct timeval timeout; + timeout.tv_sec = 10; + timeout.tv_usec = 0; + + // 送信タイムアウトの設定 + if (setsockopt(client_socket_fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) { + log_msg("Warning: Failed to set send timeout\n"); + } + + // 受信タイムアウトの設定 + if (setsockopt(client_socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) { + log_msg("Warning: Failed to set receive timeout\n"); + } + + size_t request_len = strlen(request); + size_t sent_len = 0; + while (sent_len < request_len) { + ssize_t n = write(client_socket_fd, request + sent_len, request_len - sent_len); + if (n < 0) { + handle_error("Write to server failed"); + } + sent_len += n; + } log_msg("Request sent:\n%s\n", request); char *buffer = calloc(BUFFER_SIZE, 1); - read(client_socket_fd, buffer, BUFFER_SIZE - 1); + ssize_t bytes_read = read(client_socket_fd, buffer, BUFFER_SIZE - 1); + if (bytes_read < 0) { + free(buffer); + handle_error("Read from server failed"); + } else if (bytes_read == 0) { + free(buffer); + handle_error("Server closed the connection"); + } + buffer[bytes_read] = '\0'; log_msg("Response from server:\n%s\n\n", buffer); free(buffer); } @@ -53,7 +87,7 @@ int main(int argc, char **argv){ } // ソケットの初期化 - int client_socket = initialize_socket(); + int client_socket = initialize_socket(IP_VERSION); log_msg("Client socket initialized.\n"); // サーバへの接続 diff --git a/server.c b/server.c index 41fc893..8d97986 100644 --- a/server.c +++ b/server.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -9,12 +10,14 @@ #include "utils.h" #define PORT_NUMBER 8080 +#define IP_VERSION "IPv4" #define LISTEN_BACKLOG 5 #define BUFFER_SIZE 64 volatile sig_atomic_t is_server_running = 1; -void signal_handler() { +void signal_handler(int signum) { + (void)signum; is_server_running = 0; } @@ -62,39 +65,85 @@ char* calculate_query(const char *query) { char operator; char *buffer = calloc(BUFFER_SIZE, 1); sscanf(query, "query=%d%c%d", &operand1, &operator, &operand2); + + char *extracted_query = calloc(BUFFER_SIZE, 1); + snprintf(extracted_query, BUFFER_SIZE, "query=%d%c%d", operand1, operator, operand2); + size_t extracted_len = strlen(extracted_query); + if (extracted_len > 0 && (strncmp(query, extracted_query, extracted_len) != 0)) { + log_msg("Malformed query: %s\n", query); + snprintf(buffer, BUFFER_SIZE, "Invalid query format"); + free(extracted_query); + return buffer; + } + free(extracted_query); + switch (operator) { - case '+': snprintf(buffer, BUFFER_SIZE, "%d", operand1 + operand2); return buffer; - default: handle_error("Unsupported operator"); return NULL; + case '+': + if ((operand2 > 0 && operand1 > INT_MAX - operand2) || + (operand2 < 0 && operand1 < INT_MIN - operand2)) { + snprintf(buffer, BUFFER_SIZE, "Overflow error"); + log_msg("Overflow detected: %d + %d\n", operand1, operand2); + } else { + snprintf(buffer, BUFFER_SIZE, "%d", operand1 + operand2); + } + return buffer; + default: + log_msg("Unsupported operator: %c\n", operator); + snprintf(buffer, BUFFER_SIZE, "Unsupported operator"); + return buffer; } } void handle_client_request(int client_socket_fd) { // リクエストの処理 char *buffer = calloc(BUFFER_SIZE, 1); - read(client_socket_fd, buffer, BUFFER_SIZE - 1); + ssize_t bytes_read = read(client_socket_fd, buffer, BUFFER_SIZE - 1); + if (bytes_read < 0) { + log_msg("Read from client failed"); + } else if (bytes_read == 0) { + free(buffer); + log_msg("Client disconnected.\n"); + return; + } + buffer[bytes_read] = '\0'; log_msg("Request received:\n%s\n", buffer); // /calc エンドポイントの処理 if (strncmp(buffer, "GET /calc?", 10) == 0) { char *calculation = calculate_query(buffer + 10); - char response[512]; - snprintf(response, sizeof(response), "HTTP/1.1 200 OK\r\nContent-Length:2\r\n\r\n%s", calculation); - write(client_socket_fd, response, strlen(response)); + char *response = calloc(BUFFER_SIZE, 1); + size_t response_body_length = strlen(calculation); + snprintf(response, BUFFER_SIZE, "HTTP/1.1 200 OK\r\nContent-Length:%zu\r\n\r\n%s", + response_body_length, calculation); + + size_t response_len = strlen(response); + size_t sent_len = 0; + while (sent_len < response_len) { + ssize_t n = write(client_socket_fd, response + sent_len, response_len - sent_len); + if (n < 0) { + log_msg("Write to client failed"); + break; + } + sent_len += n; + } log_msg("Response sent:\n%s\n", response); + free(response); + free(calculation); } else { - handle_error("Unsupported request method"); + log_msg("Unsupported request method"); } + free(buffer); } int main(){ // ソケットの初期化 - int server_socket = initialize_socket(); + int server_socket = initialize_socket(IP_VERSION); log_msg("Server socket initialized.\n"); // アドレスの再利用を有効化 int optval = 1; if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) { - handle_error("setsockopt SO_REUSEADDR failed"); + log_msg("setsockopt SO_REUSEADDR failed"); } // ソケットのバインド diff --git a/utils.c b/utils.c index de5adda..da0e821 100644 --- a/utils.c +++ b/utils.c @@ -1,14 +1,14 @@ -#include "utils.h" -#include -#include #include -#include +#include +#include +#include #include +#include +#include "utils.h" void handle_error(const char *msg) { - do { - perror(msg); exit(EXIT_FAILURE); - } while (0); + perror(msg); + exit(EXIT_FAILURE); } void log_msg(const char *format, ...) { @@ -18,9 +18,10 @@ void log_msg(const char *format, ...) { va_end(args); } -int initialize_socket() { - // ソケットの初期化 IPv6の通信はAF_INET6を使用 - int tcp_socket = socket(AF_INET, SOCK_STREAM, 0); +int initialize_socket(const char* IPversion) { + // ソケットの初期化 (IPv4: AF_INET, IPv6: AF_INET6) + int domain = (strcmp(IPversion, "IPv6") == 0) ? AF_INET6 : AF_INET; + int tcp_socket = socket(domain, SOCK_STREAM, 0); if (tcp_socket < 0) handle_error("Socket creation failed"); return tcp_socket; From e7a22dd67614f6f3261b5dd501c298feaf528800 Mon Sep 17 00:00:00 2001 From: Satorien Date: Fri, 2 Jan 2026 23:58:42 +0900 Subject: [PATCH 3/4] feat: bad request response --- client.c | 2 +- server.c | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/client.c b/client.c index c1db9b3..29a20cf 100644 --- a/client.c +++ b/client.c @@ -9,7 +9,7 @@ #define PORT_NUMBER 8080 #define SERVER_IP "127.0.0.1" #define IP_VERSION "IPv4" -#define BUFFER_SIZE 64 +#define BUFFER_SIZE 128 void connect_client_to_server(int client_socket_fd) { // サーバアドレス構造体 diff --git a/server.c b/server.c index 8d97986..340a41a 100644 --- a/server.c +++ b/server.c @@ -12,7 +12,7 @@ #define PORT_NUMBER 8080 #define IP_VERSION "IPv4" #define LISTEN_BACKLOG 5 -#define BUFFER_SIZE 64 +#define BUFFER_SIZE 128 volatile sig_atomic_t is_server_running = 1; @@ -113,9 +113,17 @@ void handle_client_request(int client_socket_fd) { char *calculation = calculate_query(buffer + 10); char *response = calloc(BUFFER_SIZE, 1); size_t response_body_length = strlen(calculation); - snprintf(response, BUFFER_SIZE, "HTTP/1.1 200 OK\r\nContent-Length:%zu\r\n\r\n%s", + + if (strcmp(calculation, "Invalid query format") == 0 || + strcmp(calculation, "Unsupported operator") == 0 || + strcmp(calculation, "Overflow error") == 0) { + snprintf(response, BUFFER_SIZE, "HTTP/1.1 400 Bad Request\r\nContent-Length:%zu\r\n\r\n%s", + response_body_length, calculation); + } else { + snprintf(response, BUFFER_SIZE, "HTTP/1.1 200 OK\r\nContent-Length:%zu\r\n\r\n%s", response_body_length, calculation); - + } + size_t response_len = strlen(response); size_t sent_len = 0; while (sent_len < response_len) { From 174b38b774857d081c81da04592f69110a745d79 Mon Sep 17 00:00:00 2001 From: Satorien Date: Sat, 3 Jan 2026 00:26:09 +0900 Subject: [PATCH 4/4] fix: minor format --- server.c | 12 ++++++------ utils.c | 4 ++-- utils.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/server.c b/server.c index 340a41a..de51a15 100644 --- a/server.c +++ b/server.c @@ -99,10 +99,10 @@ void handle_client_request(int client_socket_fd) { char *buffer = calloc(BUFFER_SIZE, 1); ssize_t bytes_read = read(client_socket_fd, buffer, BUFFER_SIZE - 1); if (bytes_read < 0) { - log_msg("Read from client failed"); + log_msg("Read from client failed\n"); } else if (bytes_read == 0) { free(buffer); - log_msg("Client disconnected.\n"); + log_msg("Client disconnected\n"); return; } buffer[bytes_read] = '\0'; @@ -117,10 +117,10 @@ void handle_client_request(int client_socket_fd) { if (strcmp(calculation, "Invalid query format") == 0 || strcmp(calculation, "Unsupported operator") == 0 || strcmp(calculation, "Overflow error") == 0) { - snprintf(response, BUFFER_SIZE, "HTTP/1.1 400 Bad Request\r\nContent-Length:%zu\r\n\r\n%s", + snprintf(response, BUFFER_SIZE, "HTTP/1.1 400 Bad Request\r\nContent-Length: %zu\r\n\r\n%s", response_body_length, calculation); } else { - snprintf(response, BUFFER_SIZE, "HTTP/1.1 200 OK\r\nContent-Length:%zu\r\n\r\n%s", + snprintf(response, BUFFER_SIZE, "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s", response_body_length, calculation); } @@ -129,7 +129,7 @@ void handle_client_request(int client_socket_fd) { while (sent_len < response_len) { ssize_t n = write(client_socket_fd, response + sent_len, response_len - sent_len); if (n < 0) { - log_msg("Write to client failed"); + log_msg("Write to client failed\n"); break; } sent_len += n; @@ -138,7 +138,7 @@ void handle_client_request(int client_socket_fd) { free(response); free(calculation); } else { - log_msg("Unsupported request method"); + log_msg("Unsupported request method\n"); } free(buffer); } diff --git a/utils.c b/utils.c index da0e821..6889bd5 100644 --- a/utils.c +++ b/utils.c @@ -18,9 +18,9 @@ void log_msg(const char *format, ...) { va_end(args); } -int initialize_socket(const char* IPversion) { +int initialize_socket(const char* ip_version) { // ソケットの初期化 (IPv4: AF_INET, IPv6: AF_INET6) - int domain = (strcmp(IPversion, "IPv6") == 0) ? AF_INET6 : AF_INET; + int domain = (strcmp(ip_version, "IPv6") == 0) ? AF_INET6 : AF_INET; int tcp_socket = socket(domain, SOCK_STREAM, 0); if (tcp_socket < 0) handle_error("Socket creation failed"); diff --git a/utils.h b/utils.h index 64d1744..cffb1ab 100644 --- a/utils.h +++ b/utils.h @@ -3,7 +3,7 @@ void handle_error(const char *msg); void log_msg(const char *format, ...); -int initialize_socket(); +int initialize_socket(const char *ip_version); void close_socket(int socket_fd); #endif // UTILS_H \ No newline at end of file