Created
June 21, 2025 18:51
-
-
Save masakielastic/831e34cb363604a0ffb6629d4429ec38 to your computer and use it in GitHub Desktop.
select システムコールで HTTP/1 サーバー
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #include <sys/socket.h> | |
| #include <sys/select.h> | |
| #include <netinet/in.h> | |
| #include <arpa/inet.h> | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #define PORT 8080 | |
| #define MAX_CLIENTS 10 | |
| #define BUFFER_SIZE 1024 | |
| // HTTPレスポンスのテンプレート | |
| const char *http_response = | |
| "HTTP/1.0 200 OK\r\n" | |
| "Content-Type: text/html\r\n" | |
| "Connection: close\r\n" | |
| "\r\n" | |
| "<html><body>" | |
| "<h1>Hello from C HTTP Server!</h1>" | |
| "<p>This server uses select() for I/O multiplexing.</p>" | |
| "</body></html>"; | |
| // ソケットを非ブロッキングモードに設定 | |
| int set_nonblocking(int fd) { | |
| int flags = fcntl(fd, F_GETFL, 0); | |
| if (flags == -1) return -1; | |
| return fcntl(fd, F_SETFL, flags | O_NONBLOCK); | |
| } | |
| // HTTPリクエストを処理 | |
| void handle_request(int client_fd) { | |
| char buffer[BUFFER_SIZE]; | |
| ssize_t bytes_read; | |
| // リクエストを読み込み | |
| bytes_read = recv(client_fd, buffer, sizeof(buffer) - 1, 0); | |
| if (bytes_read > 0) { | |
| buffer[bytes_read] = '\0'; | |
| printf("Request received from client %d:\n%s\n", client_fd, buffer); | |
| // HTTPレスポンスを送信 | |
| send(client_fd, http_response, strlen(http_response), 0); | |
| } | |
| // 接続を閉じる | |
| close(client_fd); | |
| printf("Client %d disconnected\n", client_fd); | |
| } | |
| int main() { | |
| int server_fd, client_fd; | |
| struct sockaddr_in server_addr, client_addr; | |
| socklen_t client_len = sizeof(client_addr); | |
| fd_set read_fds, master_fds; | |
| int max_fd; | |
| int opt = 1; | |
| // サーバーソケットを作成 | |
| server_fd = socket(AF_INET, SOCK_STREAM, 0); | |
| if (server_fd == -1) { | |
| perror("socket"); | |
| exit(EXIT_FAILURE); | |
| } | |
| // SO_REUSEADDRオプションを設定 | |
| if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { | |
| perror("setsockopt"); | |
| exit(EXIT_FAILURE); | |
| } | |
| // サーバーアドレスを設定 | |
| memset(&server_addr, 0, sizeof(server_addr)); | |
| server_addr.sin_family = AF_INET; | |
| server_addr.sin_addr.s_addr = INADDR_ANY; | |
| server_addr.sin_port = htons(PORT); | |
| // ソケットをバインド | |
| if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { | |
| perror("bind"); | |
| exit(EXIT_FAILURE); | |
| } | |
| // リスニング開始 | |
| if (listen(server_fd, MAX_CLIENTS) < 0) { | |
| perror("listen"); | |
| exit(EXIT_FAILURE); | |
| } | |
| // サーバーソケットを非ブロッキングモードに設定 | |
| if (set_nonblocking(server_fd) < 0) { | |
| perror("set_nonblocking"); | |
| exit(EXIT_FAILURE); | |
| } | |
| printf("HTTP Server started on port %d\n", PORT); | |
| printf("Access: http://localhost:%d\n", PORT); | |
| // fd_setを初期化 | |
| FD_ZERO(&master_fds); | |
| FD_SET(server_fd, &master_fds); | |
| max_fd = server_fd; | |
| while (1) { | |
| // master_fdsをread_fdsにコピー | |
| read_fds = master_fds; | |
| // selectでI/O多重化 | |
| int activity = select(max_fd + 1, &read_fds, NULL, NULL, NULL); | |
| if (activity < 0) { | |
| perror("select"); | |
| break; | |
| } | |
| // 各ファイルディスクリプタをチェック | |
| for (int fd = 0; fd <= max_fd; fd++) { | |
| if (FD_ISSET(fd, &read_fds)) { | |
| if (fd == server_fd) { | |
| // 新しい接続を受け入れ | |
| client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len); | |
| if (client_fd >= 0) { | |
| printf("New connection from %s:%d (fd: %d)\n", | |
| inet_ntoa(client_addr.sin_addr), | |
| ntohs(client_addr.sin_port), | |
| client_fd); | |
| // クライアントソケットを非ブロッキングモードに設定 | |
| if (set_nonblocking(client_fd) < 0) { | |
| perror("set_nonblocking client"); | |
| close(client_fd); | |
| continue; | |
| } | |
| // クライアントソケットをfd_setに追加 | |
| FD_SET(client_fd, &master_fds); | |
| if (client_fd > max_fd) { | |
| max_fd = client_fd; | |
| } | |
| } | |
| } else { | |
| // 既存のクライアントからのデータを処理 | |
| handle_request(fd); | |
| // クライアントソケットをfd_setから削除 | |
| FD_CLR(fd, &master_fds); | |
| // max_fdを更新 | |
| if (fd == max_fd) { | |
| while (max_fd > server_fd && !FD_ISSET(max_fd, &master_fds)) { | |
| max_fd--; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| close(server_fd); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment