Skip to content

Instantly share code, notes, and snippets.

@masakielastic
Created June 21, 2025 19:01
Show Gist options
  • Select an option

  • Save masakielastic/72915525527c88fa78313e7a8421f904 to your computer and use it in GitHub Desktop.

Select an option

Save masakielastic/72915525527c88fa78313e7a8421f904 to your computer and use it in GitHub Desktop.
poll で HTTP/1 サーバー
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#include <fcntl.h>
#define PORT 8080
#define MAX_CLIENTS 100
#define BUFFER_SIZE 4096
// HTTPレスポンス用のテンプレート
const char *http_response_template =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: %d\r\n"
"Connection: close\r\n"
"\r\n"
"%s";
const char *html_content =
"<html><head><title>HTTP Server</title></head>"
"<body><h1>Hello from C HTTP Server!</h1>"
"<p>This server uses poll() for I/O multiplexing.</p></body></html>";
// ソケットを非ブロッキングモードに設定
int set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
perror("fcntl F_GETFL");
return -1;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl F_SETFL");
return -1;
}
return 0;
}
// HTTPリクエストを処理してレスポンスを送信
void handle_http_request(int client_fd, char *request) {
char response[BUFFER_SIZE];
int content_length = strlen(html_content);
// HTTPレスポンスを作成
int response_length = snprintf(response, sizeof(response),
http_response_template,
content_length, html_content);
// レスポンスを送信
send(client_fd, response, response_length, 0);
printf("Sent response to client (fd: %d)\n", client_fd);
}
// リスニングソケットを作成・設定
int create_server_socket(int port) {
int server_fd;
struct sockaddr_in address;
int opt = 1;
// ソケット作成
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
return -1;
}
// SO_REUSEADDRオプションを設定
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR,
&opt, sizeof(opt)) < 0) {
perror("setsockopt failed");
close(server_fd);
return -1;
}
// アドレス設定
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
// バインド
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
close(server_fd);
return -1;
}
// リッスン
if (listen(server_fd, 10) < 0) {
perror("listen failed");
close(server_fd);
return -1;
}
// 非ブロッキングモードに設定
if (set_nonblocking(server_fd) < 0) {
close(server_fd);
return -1;
}
return server_fd;
}
// pollfd配列から指定されたfdを削除
void remove_fd_from_poll(struct pollfd *fds, int *nfds, int fd) {
for (int i = 0; i < *nfds; i++) {
if (fds[i].fd == fd) {
// 最後の要素を削除する位置に移動
fds[i] = fds[*nfds - 1];
(*nfds)--;
break;
}
}
}
int main() {
int server_fd;
struct pollfd fds[MAX_CLIENTS + 1]; // +1 for server socket
int nfds = 0;
char buffer[BUFFER_SIZE];
printf("Starting HTTP server on port %d...\n", PORT);
// サーバーソケット作成
server_fd = create_server_socket(PORT);
if (server_fd < 0) {
exit(EXIT_FAILURE);
}
// サーバーソケットをpollfd配列に追加
fds[0].fd = server_fd;
fds[0].events = POLLIN;
nfds = 1;
printf("Server listening on port %d\n", PORT);
while (1) {
// poll()でI/Oイベントを待機
int ret = poll(fds, nfds, -1);
if (ret < 0) {
perror("poll failed");
break;
}
// 各ファイルディスクリプタをチェック
for (int i = 0; i < nfds; i++) {
if (fds[i].revents & POLLIN) {
if (fds[i].fd == server_fd) {
// 新しい接続を受け入れ
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(server_fd,
(struct sockaddr *)&client_addr,
&client_len);
if (client_fd < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("accept failed");
}
continue;
}
printf("New client connected (fd: %d, IP: %s)\n",
client_fd, inet_ntoa(client_addr.sin_addr));
// クライアントソケットを非ブロッキングモードに設定
if (set_nonblocking(client_fd) < 0) {
close(client_fd);
continue;
}
// 新しいクライアントをpollfd配列に追加
if (nfds < MAX_CLIENTS + 1) {
fds[nfds].fd = client_fd;
fds[nfds].events = POLLIN;
nfds++;
} else {
printf("Too many clients, rejecting connection\n");
close(client_fd);
}
} else {
// クライアントからのデータを受信
int client_fd = fds[i].fd;
ssize_t bytes_read = recv(client_fd, buffer,
sizeof(buffer) - 1, 0);
if (bytes_read <= 0) {
// 接続が閉じられたかエラー
if (bytes_read == 0) {
printf("Client disconnected (fd: %d)\n", client_fd);
} else {
perror("recv failed");
}
close(client_fd);
remove_fd_from_poll(fds, &nfds, client_fd);
} else {
// HTTPリクエストを処理
buffer[bytes_read] = '\0';
printf("Received request from client (fd: %d):\n%s\n",
client_fd, buffer);
handle_http_request(client_fd, buffer);
// HTTP/1.0のためConnection: closeなので接続を閉じる
close(client_fd);
remove_fd_from_poll(fds, &nfds, client_fd);
}
}
}
// エラーイベントをチェック
if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
printf("Error on fd %d, closing connection\n", fds[i].fd);
close(fds[i].fd);
remove_fd_from_poll(fds, &nfds, fds[i].fd);
}
}
}
// クリーンアップ
close(server_fd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment