From 603789006c56f8ff67519a9c5280a9b5a7a63690 Mon Sep 17 00:00:00 2001
From: CGH0S7 <776459475@qq.com>
Date: Sun, 8 Jun 2025 21:34:30 +0800
Subject: [PATCH] weblab finished
---
weblab/Makefile | 43 +++++
weblab/webroot/index.html | 99 ++++++++++
weblab/webserver | Bin 0 -> 21792 bytes
weblab/webserver.c | 374 ++++++++++++++++++++++++++++++++++++++
weblab/webserver.ini | 8 +
weblab/webserver.log | 3 +
weblab/www.ini | 2 -
7 files changed, 527 insertions(+), 2 deletions(-)
create mode 100644 weblab/Makefile
create mode 100644 weblab/webroot/index.html
create mode 100644 weblab/webserver
create mode 100644 weblab/webserver.c
create mode 100644 weblab/webserver.ini
create mode 100644 weblab/webserver.log
delete mode 100644 weblab/www.ini
diff --git a/weblab/Makefile b/weblab/Makefile
new file mode 100644
index 0000000..50195d0
--- /dev/null
+++ b/weblab/Makefile
@@ -0,0 +1,43 @@
+# Makefile for Web Server
+
+CC = gcc
+CFLAGS = -Wall -Wextra -std=c99
+TARGET = webserver
+SOURCE = webserver.c
+
+# 默认目标
+all: $(TARGET)
+
+# 编译web服务器
+$(TARGET): $(SOURCE)
+ $(CC) $(CFLAGS) -o $(TARGET) $(SOURCE)
+
+# 创建webroot目录和示例文件
+setup:
+ mkdir -p webroot
+ @echo "Creating webroot directory and copying index.html..."
+
+# 运行服务器
+run: $(TARGET)
+ ./$(TARGET)
+
+# 清理编译文件
+clean:
+ rm -f $(TARGET)
+ rm -f webserver.log
+
+# 完全清理(包括webroot目录)
+distclean: clean
+ rm -rf webroot
+
+# 帮助信息
+help:
+ @echo "Available targets:"
+ @echo " all - Compile the web server"
+ @echo " setup - Create webroot directory"
+ @echo " run - Compile and run the server"
+ @echo " clean - Remove compiled files and logs"
+ @echo " distclean- Remove everything including webroot"
+ @echo " help - Show this help message"
+
+.PHONY: all setup run clean distclean help
diff --git a/weblab/webroot/index.html b/weblab/webroot/index.html
new file mode 100644
index 0000000..d2a804a
--- /dev/null
+++ b/weblab/webroot/index.html
@@ -0,0 +1,99 @@
+
+
+
+
+
+ 简单Web服务器测试页面
+
+
+
+
+
🎉 Web服务器运行成功!
+
+
+
恭喜!你的C语言Web服务器已经成功运行!
+
这个页面证明了服务器能够正确处理HTTP请求并返回静态网页内容。
+
+
+
服务器功能特性:
+
+
+
✅ HTTP协议支持
+
服务器实现了基本的HTTP/1.1协议,能够解析GET请求并返回适当的响应。
+
+
+
+
✅ 配置文件支持
+
通过webserver.ini文件可以配置服务器端口和网站根目录。
+
+
+
+
✅ 访问日志记录
+
服务器会记录所有访问请求的IP地址、时间和请求路径到webserver.log文件。
+
+
+
+
✅ 多种文件类型支持
+
支持HTML、CSS、JavaScript、图片等多种MIME类型的文件。
+
+
+
+
✅ 并发访问支持
+
服务器能够在不重启的情况下处理多次访问请求。
+
+
+
测试建议:
+
+ - 尝试访问不存在的页面,查看404错误处理
+ - 检查webserver.log文件中的访问记录
+ - 修改webserver.ini配置文件并重启服务器
+ - 在webroot目录中添加更多HTML、CSS、图片文件进行测试
+
+
+
+
+
+
+
+
diff --git a/weblab/webserver b/weblab/webserver
new file mode 100644
index 0000000000000000000000000000000000000000..f3197edb2730989d262972a60fe9258f1df2f15e
GIT binary patch
literal 21792
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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define BUFFER_SIZE 4096
+#define CONFIG_LINE_MAX 256
+#define LOG_LINE_MAX 512
+#define DEFAULT_PORT 8080
+#define DEFAULT_ROOT "./webroot"
+
+// 服务器配置结构
+typedef struct {
+ char root[256];
+ int port;
+} ServerConfig;
+
+// 全局变量
+ServerConfig config;
+FILE *log_file = NULL;
+
+// 函数声明
+void load_config(const char *config_file);
+void init_log();
+void log_request(const char *client_ip, const char *method, const char *path);
+void handle_client(int client_socket, struct sockaddr_in client_addr);
+void send_response(int client_socket, const char *status,
+ const char *content_type, const char *body, int body_length);
+void send_file(int client_socket, const char *file_path);
+void send_404(int client_socket);
+char *get_mime_type(const char *file_path);
+void signal_handler(int sig);
+
+int main() {
+ int server_socket, client_socket;
+ struct sockaddr_in server_addr, client_addr;
+ socklen_t client_len = sizeof(client_addr);
+
+ // 信号处理
+ signal(SIGINT, signal_handler);
+ signal(SIGTERM, signal_handler);
+
+ // 加载配置文件
+ load_config("webserver.ini");
+
+ // 初始化日志
+ init_log();
+ printf("Starting Web Server...\n");
+ printf("Root directory: %s\n", config.root);
+ printf("Port: %d\n", config.port);
+
+ // 创建socket
+ server_socket = socket(AF_INET, SOCK_STREAM, 0);
+ if (server_socket < 0) {
+ perror("Socket creation failed");
+ exit(1);
+ }
+
+ // 设置socket选项以重用地址
+ int opt = 1;
+ if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) <
+ 0) {
+ perror("setsockopt failed");
+ exit(1);
+ }
+
+ // 配置服务器地址
+ memset(&server_addr, 0, sizeof(server_addr));
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_addr.s_addr = INADDR_ANY;
+ server_addr.sin_port = htons(config.port);
+
+ // 绑定socket
+ if (bind(server_socket, (struct sockaddr *)&server_addr,
+ sizeof(server_addr)) < 0) {
+ perror("Bind failed");
+ close(server_socket);
+ exit(1);
+ }
+
+ // 监听连接
+ if (listen(server_socket, 10) < 0) {
+ perror("Listen failed");
+ close(server_socket);
+ exit(1);
+ }
+
+ printf("Server is listening on port %d...\n", config.port);
+ printf("Press Ctrl+C to stop the server.\n\n");
+
+ // 主服务循环
+ while (1) {
+ client_socket =
+ accept(server_socket, (struct sockaddr *)&client_addr, &client_len);
+ if (client_socket < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ perror("Accept failed");
+ continue;
+ }
+
+ // 处理客户端请求
+ handle_client(client_socket, client_addr);
+ close(client_socket);
+ }
+
+ close(server_socket);
+ if (log_file) {
+ fclose(log_file);
+ }
+ return EXIT_SUCCESS;
+}
+
+// 加载配置文件
+void load_config(const char *config_file) {
+ FILE *file;
+ char line[CONFIG_LINE_MAX];
+ char key[128], value[128];
+
+ // 设置默认值
+ strcpy(config.root, DEFAULT_ROOT);
+ config.port = DEFAULT_PORT;
+
+ file = fopen(config_file, "r");
+ if (!file) {
+ printf("Warning: Cannot open config file '%s', using defaults.\n",
+ config_file);
+ return;
+ }
+
+ while (fgets(line, sizeof(line), file)) {
+ // 跳过注释和空行
+ if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') {
+ continue;
+ }
+
+ // 解析键值对
+ if (sscanf(line, "%127[^=]=%127s", key, value) == 2) {
+ // 去除键的空格
+ char *k = key;
+ while (*k == ' ' || *k == '\t')
+ k++;
+ char *k_end = k + strlen(k) - 1;
+ while (k_end > k && (*k_end == ' ' || *k_end == '\t' || *k_end == '\n' ||
+ *k_end == '\r')) {
+ *k_end = '\0';
+ k_end--;
+ }
+
+ if (strcmp(k, "root") == 0) {
+ strcpy(config.root, value);
+ } else if (strcmp(k, "port") == 0) {
+ config.port = atoi(value);
+ if (config.port <= 0 || config.port > 65535) {
+ config.port = DEFAULT_PORT;
+ }
+ }
+ }
+ }
+
+ fclose(file);
+}
+
+// 初始化日志
+void init_log() {
+ log_file = fopen("webserver.log", "a");
+ if (!log_file) {
+ printf("Warning: Cannot open log file, logging to stdout only.\n");
+ }
+}
+
+// 记录访问日志
+void log_request(const char *client_ip, const char *method, const char *path) {
+ time_t now;
+ struct tm *tm_info;
+ char time_str[64];
+ char log_line[LOG_LINE_MAX];
+
+ time(&now);
+ tm_info = localtime(&now);
+ strftime(time_str, sizeof(time_str), "%Y/%m/%d %H:%M:%S", tm_info);
+
+ snprintf(log_line, sizeof(log_line), "%s IP:%s %s %s\n", time_str, client_ip,
+ method, path);
+
+ // 输出到控制台
+ printf("%s", log_line);
+
+ // 输出到日志文件
+ if (log_file) {
+ fprintf(log_file, "%s", log_line);
+ fflush(log_file);
+ }
+}
+
+// 处理客户端请求
+void handle_client(int client_socket, struct sockaddr_in client_addr) {
+ char buffer[BUFFER_SIZE];
+ char method[16], path[256], version[16];
+ char *client_ip = inet_ntoa(client_addr.sin_addr);
+
+ // 接收HTTP请求
+ int bytes_received = recv(client_socket, buffer, sizeof(buffer) - 1, 0);
+ if (bytes_received <= 0) {
+ return;
+ }
+
+ buffer[bytes_received] = '\0';
+
+ // 解析HTTP请求行
+ if (sscanf(buffer, "%15s %255s %15s", method, path, version) != 3) {
+ send_404(client_socket);
+ return;
+ }
+
+ // 记录访问日志
+ log_request(client_ip, method, path);
+
+ // 只处理GET请求
+ if (strcmp(method, "GET") != 0) {
+ send_response(client_socket, "405 Method Not Allowed", "text/html",
+ "405 Method Not Allowed
", 30);
+ return;
+ }
+
+ // 构建完整文件路径
+ char file_path[512];
+ if (strcmp(path, "/") == 0) {
+ snprintf(file_path, sizeof(file_path), "%s/index.html", config.root);
+ } else {
+ snprintf(file_path, sizeof(file_path), "%s%s", config.root, path);
+ }
+
+ // 检查文件是否存在
+ struct stat file_stat;
+ if (stat(file_path, &file_stat) == 0 && S_ISREG(file_stat.st_mode)) {
+ send_file(client_socket, file_path);
+ } else {
+ send_404(client_socket);
+ }
+}
+
+// 发送HTTP响应
+void send_response(int client_socket, const char *status,
+ const char *content_type, const char *body,
+ int body_length) {
+ char header[1024];
+
+ snprintf(header, sizeof(header),
+ "HTTP/1.1 %s\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: %d\r\n"
+ "Connection: close\r\n"
+ "\r\n",
+ status, content_type, body_length);
+
+ send(client_socket, header, strlen(header), 0);
+ if (body && body_length > 0) {
+ send(client_socket, body, body_length, 0);
+ }
+}
+
+// 发送文件
+void send_file(int client_socket, const char *file_path) {
+ FILE *file = fopen(file_path, "rb");
+ if (!file) {
+ send_404(client_socket);
+ return;
+ }
+
+ // 获取文件大小
+ fseek(file, 0, SEEK_END);
+ long file_size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ // 获取MIME类型
+ char *mime_type = get_mime_type(file_path);
+
+ // 发送HTTP头
+ char header[1024];
+ snprintf(header, sizeof(header),
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: %ld\r\n"
+ "Connection: close\r\n"
+ "\r\n",
+ mime_type, file_size);
+
+ send(client_socket, header, strlen(header), 0);
+
+ // 发送文件内容
+ char file_buffer[4096];
+ size_t bytes_read;
+ while ((bytes_read = fread(file_buffer, 1, sizeof(file_buffer), file)) > 0) {
+ send(client_socket, file_buffer, bytes_read, 0);
+ }
+
+ fclose(file);
+}
+
+// 发送404错误
+void send_404(int client_socket) {
+ const char *body = "404 Not Found
The requested page "
+ "was not found.
";
+ send_response(client_socket, "404 Not Found", "text/html", body,
+ strlen(body));
+}
+
+// 获取MIME类型
+char *get_mime_type(const char *file_path) {
+ const char *ext = strrchr(file_path, '.');
+ if (!ext)
+ return "application/octet-stream";
+
+ if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) {
+ return "text/html";
+ } else if (strcmp(ext, ".css") == 0) {
+ return "text/css";
+ } else if (strcmp(ext, ".js") == 0) {
+ return "application/javascript";
+ } else if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) {
+ return "image/jpeg";
+ } else if (strcmp(ext, ".png") == 0) {
+ return "image/png";
+ } else if (strcmp(ext, ".gif") == 0) {
+ return "image/gif";
+ } else if (strcmp(ext, ".txt") == 0) {
+ return "text/plain";
+ }
+
+ return "application/octet-stream";
+}
+
+// 信号处理函数
+void signal_handler(int sig) {
+ char *signal_name = (sig == SIGINT) ? "SIGINT" : "SIGTERM";
+ time_t now;
+ struct tm *tm_info;
+ char time_str[64];
+
+ // 获取当前时间
+ time(&now);
+ tm_info = localtime(&now);
+ strftime(time_str, sizeof(time_str), "%Y/%m/%d %H:%M:%S", tm_info);
+
+ // 记录关闭信息
+ printf("\n[%s] Received %s signal. Shutting down server...\n", time_str,
+ signal_name);
+
+ // 如果日志文件打开,记录关闭信息并关闭
+ if (log_file) {
+ fprintf(log_file, "[%s] Server shutdown initiated by %s\n", time_str,
+ signal_name);
+ fflush(log_file);
+ fclose(log_file);
+ log_file = NULL;
+ }
+
+ // 等待1秒以确保所有pending的写操作完成
+ sleep(1);
+
+ printf("Server shutdown complete.\n");
+ exit(0);
+}
diff --git a/weblab/webserver.ini b/weblab/webserver.ini
new file mode 100644
index 0000000..b235639
--- /dev/null
+++ b/weblab/webserver.ini
@@ -0,0 +1,8 @@
+# Web服务器配置文件
+# 网站根目录
+root=./webroot
+
+# 服务器监听端口
+port=80
+
+# 注释:可以修改以上配置来自定义服务器行为
diff --git a/weblab/webserver.log b/weblab/webserver.log
new file mode 100644
index 0000000..fce0fa1
--- /dev/null
+++ b/weblab/webserver.log
@@ -0,0 +1,3 @@
+2025/06/08 21:25:57 IP:127.0.0.1 GET /
+2025/06/08 21:25:57 IP:127.0.0.1 GET /favicon.ico
+[2025/06/08 21:26:15] Server shutdown initiated by SIGINT
diff --git a/weblab/www.ini b/weblab/www.ini
deleted file mode 100644
index 812b2fc..0000000
--- a/weblab/www.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-root=c:\adb
-port=80