生データの中身を表示
携帯で接続。
404が出ているが、ヘッダの中身が見れたので気にしない。
ubuntu@ubuntu:~/kaihatsu/net$ gcc server.c -o server
ubuntu@ubuntu:~/kaihatsu/net$ ./server
ソケット作成成功
ソケットオプション設定
ポート 8080 にバインド成功
接続待機開始...
新しい接続 [192.168.68.163:60116]
▼▼▼ 生リクエストデータ ▼▼▼
GET / HTTP/1.1
Host: 192.168.68.96:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ja-CN;q=0.8,ja;q=0.7
▲▲▲ 生リクエストデータ ▲▲▲
解析結果:
メソッド: GET
リクエストパス: /
初期パスに変換: /index.html
アクセスファイル: public/index.html
ファイル情報:
サイズ: 207 bytes
タイプ: text/html
▼▼▼ レスポンスヘッダ ▼▼▼
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 207
▲▲▲ レスポンスヘッダ ▲▲▲
接続を閉じました
新しい接続 [192.168.68.163:60114]
▼▼▼ 生リクエストデータ ▼▼▼
GET /style.css HTTP/1.1
Host: 192.168.68.96:8080
Connection: keep-alive
User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36
Accept: text/css,*/*;q=0.1
Referer: http://192.168.68.96:8080/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ja-CN;q=0.8,ja;q=0.7
▲▲▲ 生リクエストデータ ▲▲▲
解析結果:
メソッド: GET
リクエストパス: /style.css
アクセスファイル: public/style.css
ファイル開くのに失敗: No such file or directory
▼▼▼ レスポンスヘッダ ▼▼▼
HTTP/1.1 404 OK
Content-Type: text/plain
Content-Length: 9
▲▲▲ レスポンスヘッダ ▲▲▲
接続を閉じました
新しい接続 [192.168.68.163:60118]
▼▼▼ 生リクエストデータ ▼▼▼
GET /app.js HTTP/1.1
Host: 192.168.68.96:8080
Connection: keep-alive
User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36
Accept: */*
Referer: http://192.168.68.96:8080/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ja-CN;q=0.8,ja;q=0.7
▲▲▲ 生リクエストデータ ▲▲▲
解析結果:
メソッド: GET
リクエストパス: /app.js
アクセスファイル: public/app.js
ファイル開くのに失敗: No such file or directory
▼▼▼ レスポンスヘッダ ▼▼▼
HTTP/1.1 404 OK
Content-Type: text/plain
Content-Length: 9
▲▲▲ レスポンスヘッダ ▲▲▲
接続を閉じました
Cコード
server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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/"
// 内容を判定する関数
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";
}
// レスポンスを送信する関数
void send_response(int client_fd, int status, const char *content_type, const char *body, int content_length) {
char header[BUFFER_SIZE];
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);
// レスポンスヘッダを表示
printf("▼▼▼ レスポンスヘッダ ▼▼▼\n");
printf("%s", header);
printf("▲▲▲ レスポンスヘッダ ▲▲▲\n\n");
write(client_fd, header, strlen(header));
write(client_fd, body, content_length);
}
// リクエストを処理する関数
void handle_request(int client_fd) {
char buffer[BUFFER_SIZE] = {0};
ssize_t bytes_read = read(client_fd, buffer, sizeof(buffer));
if (bytes_read < 0) {
perror("リクエストの読み込み失敗");
return;
}
// 生のリクエストデータを表示
printf("▼▼▼ 生リクエストデータ ▼▼▼\n%.*s\n", (int)bytes_read, buffer);
printf("▲▲▲ 生リクエストデータ ▲▲▲\n\n");
// リクエストヘッダを解析
char method[16], path[256];
sscanf(buffer, "%s %s", method, path);
printf("解析結果:\n");
printf("メソッド: %s\nリクエストパス: %s\n\n", method, path);
// デフォルトパスの処理
if (strcmp(path, "/") == 0) {
strcpy(path, "/index.html");
printf("初期パスに変換: %s\n", path);
}
// ローカルファイルパスを構築
char full_path[512];
snprintf(full_path, sizeof(full_path), "%s%s", ROOT_DIR, path + 1);
printf("アクセスファイル: %s\n", full_path);
// ファイルを開く
int file_fd = open(full_path, O_RDONLY);
if (file_fd == -1) {
perror("ファイル開くのに失敗");
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);
printf("ファイル情報:\nサイズ: %ld bytes\nタイプ: %s\n\n", st.st_size, get_content_type(path));
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);
}
printf("ソケット作成成功\n");
// ソケットオプション設定
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
printf("ソケットオプション設定\n");
// アドレスバインド
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("バインド失敗");
exit(EXIT_FAILURE);
}
printf("ポート %d にバインド成功\n", PORT);
// リスニング開始
if (listen(server_fd, 3) < 0) {
perror("リスニング失敗");
exit(EXIT_FAILURE);
}
printf("接続待機開始...\n\n");
// メインループ
while (1) {
socklen_t addrlen = sizeof(address);
client_fd = accept(server_fd, (struct sockaddr *)&address, &addrlen);
if (client_fd < 0) {
perror("接続受付失敗");
continue;
}
// クライアント情報を表示
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &address.sin_addr, client_ip, INET_ADDRSTRLEN);
printf("新しい接続 [%s:%d]\n", client_ip, ntohs(address.sin_port));
handle_request(client_fd);
close(client_fd);
printf("接続を閉じました\n\n");
}
return 0;
}