Linuxネットワークプログラミング - Webサーバーの実装とURLエンコード

Webサーバーの実装

Webサーバーの基本的な実装方法を説明します。

  1. HTTPリクエストの解析
  2. ファイル拡張子に基づくMIMEタイプの決定
  3. sscanfの使用方法
  4. ディレクトリのスキャン
  5. HTTPリダイレクト
  6. 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;
}
        

タグ: Linuxネットワークプログラミング HTTPサーバー URLエンコード epoll strftime

6月10日 17:42 投稿