Webサーバーの実装
Webサーバーの基本的な実装方法を説明します。
- HTTPリクエストの解析
- ファイル拡張子に基づくMIMEタイプの決定
- sscanfの使用方法
- ディレクトリのスキャン
- HTTPリダイレクト
- URLエンコードとデコード
HTTPリクエストの解析
HTTPリクエストを解析する関数の例です。
void http_response_header(int client_socket, const char* content_type) {
char buffer[1024];
// ステータス行
snprintf(buffer, sizeof(buffer), "HTTP/1.1 200 OK\r\n");
send(client_socket, buffer, strlen(buffer), 0);
// ヘッダ行
snprintf(buffer, sizeof(buffer), "Content-Type: %s\r\n", content_type);
send(client_socket, buffer, strlen(buffer), 0);
// 空行
send(client_socket, "\r\n", 2, 0);
}
ディレクトリのスキャン
ディレクトリの内容をスキャンする例です。
#include <dirent.h>
void scan_directory(const char* dir_path) {
struct dirent** entries;
int num = scandir(dir_path, &entries, nullptr, alphasort);
if (num == -1) {
perror("ディレクトリのスキャンに失敗");
return;
}
for (int i = 0; i < num; ++i) {
struct dirent* entry = entries[i];
printf("ファイル名: %s\n", entry->d_name);
free(entry);
}
free(entries);
}
URLエンコード
URLエンコードの概要です。
- 日本語などの特殊文字をエンコードする必要があります。
- エンコード不要な文字: ., _, *, /, ~, 数字, 英字
- エンコードする文字は%XX形式にします。
Unicodeツールを使用して確認できます。
sudo apt-get install unicode
epollモデルサーバー
epollを使用した非ブロック型サーバーの例です。
// epoll_server.c
#include <sys/epoll.h>
#include <arpa/inet.h>
struct epoll_data {
int socket;
char buffer[4096];
};
void create_epoll_server(int port, const char* dir_path) {
int epoll_fd = epoll_create(1);
if (epoll_fd == -1) {
perror(" epollの作成に失敗");
return;
}
// リスンソケットの作成
int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(listen_socket, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(listen_socket, 64);
// epollにリスンソケットを登録
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = listen_socket;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_socket, &event);
while (true) {
struct epoll_event events[1];
int event_count = epoll_wait(epoll_fd, events, 1, -1);
if (event_count == -1) {
perror(" epoll_waitに失敗");
break;
}
for (int i = 0; i < event_count; ++i) {
int socketFd = events[i].data.fd;
if (events[i].events & EPOLLIN) {
if (socketFd == listen_socket) {
// 新規接続の受付
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
int client_socket = accept(socketFd, (struct sockaddr*)&client_addr, &addr_len);
if (client_socket != -1) {
// 接続を非ブロックに設定
int flags = fcntl(client_socket, F_GETFL);
fcntl(client_socket, F_SETFL, flags | O_NONBLOCK);
// epollに接続を登録
struct epoll_event new_event;
new_event.events = EPOLLIN;
new_event.data.fd = client_socket;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_socket, &new_event);
}
} else {
// データの受信
char buffer[1024];
int read_size = read(socketFd, buffer, sizeof(buffer));
if (read_size > 0) {
// リクエストを処理
process_request(buffer, socketFd, dir_path);
}
}
}
}
}
}
sscanfの使用方法
sscanf関数の例です。
// 空白まで読み込む
char result[100];
sscanf("123456 abcdedf", "%[^ ]", result);
printf("%s\n", result); // "123456"
// 指定された文字集合のみを読み込む
sscanf("123456abcdedfBCDEF", "%[1-9a-z]", result);
printf("%s\n", result); // "123456abcdedf"
// 指定された文字集合を避ける
sscanf("123456abcdedfBCDEF", "%[^A-Z]", result);
printf("%s\n", result); // "123456abcdedf"
scandirの使用方法
scandir関数の例です。
int scandir(const char* dir, struct dirent*** list,
int (*filter)(const struct dirent*),
int (*sort_func)(const struct dirent**, const struct dirent**));
strftimeの使用方法
strftime関数の例です。
#include <time.h>
char* format_time(const struct tm* time_struct, char* output, size_t max_size) {
strftime(output, max_size, "%Y-%m-%d %H:%M:%S", time_struct);
return output;
}