実証
このCプログラムは、TCPソケットを利用してポート8080でHTTPリクエストを受け取り、public/ ディレクトリ内の静的ファイルをHTTPレスポンスとして返す簡易Webサーバーです。
socket → bind → listen → accept → read(リクエスト)→ open + read(ファイル)→ write(レスポンス)という一連の流れに従って、Webアクセスを処理しています。
ファイル構造
c_http_server/
├── server.c # サーバー実装
├── server
└── public/
└── index.html # 公開するHTML
起動
ubuntu@ubuntu:~/kaihatsu/server$ gcc server.c -o server
ubuntu@ubuntu:~/kaihatsu/server$ ./server
サーバーが起動しました: http://localhost:8080
LAN内の他の端末からはIPアドレスを指定してアクセスしてください
サーバー側のIPアドレスを調べる
ubuntu@ubuntu:~$ hostname -I
192.168.68.96
ubuntu@ubuntu:~$
端末(ターミナル)から接続
ubuntu@ubuntu:~$ curl http://192.168.68.96:8080
<!DOCTYPE html>
<html>
<head>
<title>C HTTP Server</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>C Language Server</h1>
<script src="/app.js"></script>
</body>
</html>
ubuntu@ubuntu:~$
携帯から接続
ブラウザを開き
http://192.168.68.96:8080
と入力する。
Cコード
server.c
/*----------------------------------------------------------
HTTPサーバーの実装
C言語で記述された単一スレッドのWebサーバー
8080ポートで待機し、publicディレクトリ内の静的ファイルを配信
------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // 文字列操作
#include <unistd.h> // システムコール(read/writeなど)
#include <arpa/inet.h> // ネットワーク関連操作
#include <sys/socket.h> // ソケット
#include <sys/stat.h> // ファイル状態情報
#include <fcntl.h> // ファイル制御
// 設定定数
#define PORT 8080 // 待機ポート番号
#define BUFFER_SIZE 2048 // バッファサイズ
#define ROOT_DIR "public/" // 静的ファイルのルートディレクトリ
/*----------------------------------------------------------
コンテンツタイプを取得する関数
引数 path: リクエストされたファイルパス
戻り値: MIMEタイプ文字列
------------------------------------------------------------*/
const char* get_content_type(const char *path) {
const char *dot = strrchr(path, '.'); // 拡張子の検索
if (dot) {
if (strcmp(dot, ".css") == 0) return "text/css";
if (strcmp(dot, ".js") == 0) return "application/javascript";
}
return "text/html"; // デフォルトはHTML
}
/*----------------------------------------------------------
HTTPレスポンスを送信する関数
引数 client_fd: クライアントのソケットディスクリプタ
status: HTTPステータスコード
content_type: Content-Typeヘッダの値
body: レスポンスボディ
content_length: コンテンツの長さ
------------------------------------------------------------*/
void send_response(int client_fd, int status, const char *content_type,
const char *body, int content_length) {
char header[BUFFER_SIZE];
// HTTPヘッダの生成
snprintf(header, sizeof(header),
"HTTP/1.1 %d OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n\r\n",
status, content_type, content_length);
// ヘッダとボディの送信
write(client_fd, header, strlen(header));
write(client_fd, body, content_length);
}
/*----------------------------------------------------------
クエストを処理する関数
引数 client_fd: クライアントのソケットディスクリプタ
------------------------------------------------------------*/
void handle_request(int client_fd) {
char buffer[BUFFER_SIZE] = {0};
read(client_fd, buffer, sizeof(buffer)); // リクエストの読み取り
// リクエストラインの解析(例: GET /index.html HTTP/1.1)
char method[16], path[256];
sscanf(buffer, "%s %s", method, path);
// ルートパスの場合はindex.htmlを表示
if (strcmp(path, "/") == 0) strcpy(path, "/index.html");
// ローカルファイルパスの生成
char full_path[512];
snprintf(full_path, sizeof(full_path), "%s%s", ROOT_DIR, path + 1);
// ファイルオープン
int file_fd = open(full_path, O_RDONLY);
if (file_fd == -1) { // ファイルが存在しない場合
send_response(client_fd, 404, "text/plain", "Not Found", 9);
return;
}
// ファイル内容の読み込み
struct stat st;
fstat(file_fd, &st); // ファイル情報の取得
char *content = malloc(st.st_size); // メモリ確保
read(file_fd, content, st.st_size);
// レスポンス送信
send_response(client_fd, 200, get_content_type(path), content, st.st_size);
// リソースの解放
free(content);
close(file_fd);
}
/*----------------------------------------------------------
メイン関数
------------------------------------------------------------*/
int main() {
int server_fd, client_fd;
struct sockaddr_in address;
// ソケットの作成
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("ソケットの作成に失敗しました");
exit(EXIT_FAILURE);
}
// ソケットオプションの設定(アドレスの再利用を許可)
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// アドレス構造体の設定
address.sin_family = AF_INET; // IPv4
address.sin_addr.s_addr = INADDR_ANY; // すべてのインターフェース
address.sin_port = htons(PORT); // ポート番号
// ソケットのバインド
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("バインドに失敗しました");
exit(EXIT_FAILURE);
}
// 接続待機開始 待機接続数を3に設定
if (listen(server_fd, 3) < 0) {
perror("リッスンに失敗しました");
exit(EXIT_FAILURE);
}
printf("サーバーが起動しました: http://localhost:%d\n", PORT);
printf("LAN内の他の端末からはIPアドレスを指定してアクセスしてください\n");
// メインループ(接続を受け付けて処理)
while (1) {
socklen_t addrlen = sizeof(address);
// 接続を受け付ける
if ((client_fd = accept(server_fd, (struct sockaddr *)&address, &addrlen)) < 0) {
perror("接続受け付けに失敗しました");
continue;
}
handle_request(client_fd); // リクエスト処理
close(client_fd); // クライアント接続を閉じる
}
return 0;
}