字符串比较函数strncasecmp():比较字符串的前n个字符|字符串查找函数strstr和string.find() 查找字符串str1中是否存在与str2一样的子字符串

本文介绍了C语言的strncasecmp()函数,用于比较字符串的前n个字符,以及C++中string类的find()函数,详细解析了find()的各种用法,包括查找子字符串、查找特定字符及其变种函数的使用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘要:string类里,find函数的实现里用到字符串比较函数,substr函数截取子字符串时常与find函数相配合。

C语言strncasecmp()函数:比较字符串的前n个字符


头文件:#include <string.h>

定义函数:int strncasecmp(const char *s1, const char *s2, size_t n);

函数说明:strncasecmp()用来比较参数s1 和s2 字符串前n个字符,比较时会自动忽略大小写的差异。

返回值:若参数s1 和s2 字符串相同则返回0。s1 若大于s2 则返回大于0 的值,s1 若小于s2 则返回小于0 的值。

范例
    
    
    
  1. #include <string.h>
  2. main(){
  3. char *a = "aBc
我的实验作业是做一个web server,这是作业要求: 分别使用多线程,多进程,IO多路复用实现一个简单的web server(端口80)承载附件的web。对比这三种实现方式各自的优缺点。 Ps:Web server普通socket通信的区别主要在于:多了字段的解析处理。 多线程:优点:线程间数据共享方便、线程切换开销小、可以充分利用多核处理器优势;缺点:线程间同步互斥需要额外开销容易引起死锁、线程数量过多时系统资源开销大、可能存在线程间的资源竞争 多进程:优点:进程间相互独立不会竞争资源、可以更好利用多核处理器、稳定性高;缺点:切换开销大、系统资源消耗大 IO多路复用:优点:单线程可同时处理多个IO请求,提高了并发性、节省系统资源、实现高效的事件驱动编程;缺点,计算密集型任务中IO多路复用效率可能较低、编程复杂性高 课题目的: 1. 熟悉公司的开发设计流程 2. 熟悉HTTP协议 3. 熟悉并掌握Linux环境应用开发 4. 熟悉socket网络编程 5. 加强多线程/多进程编程能力 6. 熟悉经典IO复用设计模型 课题要求: 1、实现以多线程方式实现web server功能(必修) 2、实现以多进程方式实现web server功能(必修) 3、实现以IO多路复用方式实现web server功能(必修) 4、提供完善Debug机制(必修) 5、Web server需完成get post等基础请求处理(必修) 6、如何使其具备高并发性并实现(选修) 7、使用makefile构建(必修 以报告的形式提交。 下面是我的代码: "utils.c" #include "utils.h" #include "debug.h" #include <time.h> const char *get_content_type(const char *path) { LOG_DEBUG("Determining content type for: %s", path); const char *ext = strrchr(path, '.'); if (!ext) { LOG_DEBUG("No file extension, defaulting to text/plain"); return "text/plain"; } if (strcmp(ext, ".html") == 0) return "text/html"; if (strcmp(ext, ".css") == 0) return "text/css"; if (strcmp(ext, ".js") == 0) return "application/javascript"; if (strcmp(ext, ".json") == 0) return "application/json"; if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; if (strcmp(ext, ".png") == 0) return "image/png"; if (strcmp(ext, ".gif") == 0) return "image/gif"; if (strcmp(ext, ".ico") == 0) return "image/x-icon"; LOG_DEBUG("Unknown file extension '%s', defaulting to text/plain", ext); return "text/plain"; } int file_exists(const char *path) { LOG_DEBUG("Checking file existence: %s", path); int exists = access(path, F_OK) != -1; if (!exists) { LOG_WARN("File not found: %s", path); } return exists; } long get_file_size(FILE *file) { if (fseek(file, 0, SEEK_END) != 0) { LOG_ERROR("fseek(SEEK_END) failed: %s", strerror(errno)); return -1; } long size = ftell(file); if (size == -1) { LOG_ERROR("ftell failed: %s", strerror(errno)); } if (fseek(file, 0, SEEK_SET) != 0) { LOG_ERROR("fseek(SEEK_SET) failed: %s", strerror(errno)); } LOG_DEBUG("File size: %ld bytes", size); return size; } void log_request(const char *client_ip, const char *method, const char *path, int status) { time_t now = time(NULL); char time_buf[64]; strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", localtime(&now)); // 根据状态码确定日志级别 if (status >= 400) { LOG_ERROR("[%s] %s - %s %s - %d", time_buf, client_ip, method, path, status); } else if (status >= 300) { LOG_WARN("[%s] %s - %s %s - %d", time_buf, client_ip, method, path, status); } else { LOG_INFO("[%s] %s - %s %s - %d", time_buf, client_ip, method, path, status); } } "http_parser.c" #include "http_parser.h" #include "debug.h" #include <string.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> int parse_http_request(const char *request, http_request_t *req) { LOG_DEBUG("Parsing HTTP request"); char buffer[4096]; strncpy(buffer, request, sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = '\0'; // 解析请求行 char *line = strtok(buffer, "\r\n"); if (!line) { LOG_ERROR("Empty request line"); return -1; } // 解析方法、路径HTTP版本 if (sscanf(line, "%15s %1023s %15s", req->method, req->path, req->version) != 3) { LOG_ERROR("Invalid request line: %s", line); return -1; } LOG_DEBUG("Request line: %s %s %s", req->method, req->path, req->version); // 解析头部字段 req->content_length = 0; memset(req->host, 0, sizeof(req->host)); while ((line = strtok(NULL, "\r\n")) != NULL && line[0] != '\0') { if (strncasecmp(line, "Host:", 5) == 0) { sscanf(line + 5, " %255s", req->host); LOG_DEBUG("Host: %s", req->host); } else if (strncasecmp(line, "Content-Length:", 15) == 0) { sscanf(line + 15, " %d", &req->content_length); LOG_DEBUG("Content-Length: %d", req->content_length); } } return 0; } int parse_request_body(int client_fd, http_request_t *req, const char *buffer, ssize_t bytes_read) { if (strcmp(req->method, "POST") != 0 || req->content_length <= 0) { LOG_DEBUG("Skipping body parsing for non-POST request"); return 0; // 非POST请求或没有内容体 } LOG_DEBUG("Parsing POST request body (length: %d)", req->content_length); // 定位请求头结束位置 const char *body_start = strstr(buffer, "\r\n\r\n"); if (!body_start) { LOG_ERROR("Failed to find body separator in request"); return -1; } body_start += 4; // 跳过空行 size_t header_length = body_start - buffer; size_t body_received = (bytes_read > header_length) ? (bytes_read - header_length) : 0; LOG_DEBUG("Header length: %zu, Body received: %zu", header_length, body_received); // 分配内存存储请求体 req->body = malloc(req->content_length + 1); if (!req->body) { LOG_CRITICAL("Body allocation failed: %s", strerror(errno)); return -1; } // 复制已接收部分 size_t to_copy = (body_received < (size_t)req->content_length) ? body_received : (size_t)req->content_length; memcpy(req->body, body_start, to_copy); req->body[req->content_length] = '\0'; LOG_DEBUG("Body content (%d bytes):\n%.*s", req->content_length, (int)req->content_length, req->body); return 0; } void free_request_body(http_request_t *req) { if (req->body) { LOG_DEBUG("Freeing request body"); free(req->body); req->body = NULL; } } // URL解码函数 void urldecode(char *src) { char *p = src; char *q = src; while (*p) { if (*p == '+') { *q++ = ' '; p++; } else if (*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) { *q++ = (char)strtol((char[]){p[1], p[2], 0}, NULL, 16); p += 3; } else { *q++ = *p++; } } *q = '\0'; } void log_post_body(const http_request_t *req, const char *client_ip) { if (!req->body || req->content_length <= 0) { LOG_DEBUG("No POST body to log"); return; } // 安全截断长请求体 int log_length = (req->content_length > 1024) ? 1024 : req->content_length; char buf[log_length + 1]; memcpy(buf, req->body, log_length); buf[log_length] = '\0'; LOG_INFO("POST data from %s for %s (length: %d)", client_ip, req->path, req->content_length); // 解析并打印结构化参数 char *pair = strtok(buf, "&"); while (pair) { char *eq = strchr(pair, '='); if (eq) { *eq = '\0'; char *key = pair; char *value = eq + 1; // 对键值进行URL解码 urldecode(key); urldecode(value); LOG_INFO(" %-8s: %s", key, value); } pair = strtok(NULL, "&"); } } "http_response.c" #include "http_response.h" #include "utils.h" #include "debug.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/sendfile.h> #include <fcntl.h> #include <sys/stat.h> #include <arpa/inet.h> void send_http_response(int client_fd, int status, const char *message, const char *content_type, long content_length) { char header[1024]; snprintf(header, sizeof(header), "HTTP/1.1 %d %s\r\n" "Content-Type: %s\r\n" "Content-Length: %ld\r\n\r\n", // "Connection: close\r\n\r\n", status, message, content_type, content_length); LOG_DEBUG("Sending HTTP response header:\n%s", header); ssize_t sent = send(client_fd, header, strlen(header), 0); if (sent != (ssize_t)strlen(header)) { LOG_ERROR("Partial header sent (%zd/%zu bytes)", sent, strlen(header)); } } void send_error_response(int client_fd, int status, const char *message) { char body[512]; snprintf(body, sizeof(body), "<html><body><h1>%d %s</h1></body></html>", status, message); LOG_WARN("Sending error response: %d %s", status, message); send_http_response(client_fd, status, message, "text/html", strlen(body)); ssize_t sent = send(client_fd, body, strlen(body), 0); if (sent != (ssize_t)strlen(body)) { LOG_ERROR("Partial error body sent (%zd/%zu bytes)", sent, strlen(body)); } } void handle_http_request(int client_fd, http_request_t *req) { LOG_DEBUG("Handling HTTP request: %s %s", req->method, req->path); char file_path[2048]; // 默认首页 if (strcmp(req->path, "/") == 0) { snprintf(file_path, sizeof(file_path), "web/Index.html"); } else { snprintf(file_path, sizeof(file_path), "web%s", req->path); } LOG_DEBUG("Resolved file path: %s", file_path); // 防止路径遍历攻击 if (strstr(file_path, "..")) { LOG_WARN("Path traversal attempt detected: %s", file_path); send_error_response(client_fd, 403, "Forbidden"); return; } // 检查文件是否存在 if (!file_exists(file_path)) { LOG_WARN("File not found: %s", file_path); send_error_response(client_fd, 404, "Not Found"); return; } // 打开文件 FILE *file = fopen(file_path, "rb"); if (!file) { LOG_ERROR("File open failed: %s, error: %s", file_path, strerror(errno)); send_error_response(client_fd, 500, "Internal Server Error"); return; } LOG_DEBUG("File opened successfully: %s", file_path); // 获取文件大小 long file_size = get_file_size(file); // 发送HTTP头 const char *content_type = get_content_type(file_path); LOG_DEBUG("Sending file: %s, type: %s, size: %ld", file_path, content_type, file_size); send_http_response(client_fd, 200, "OK", content_type, file_size); // 使用sendfile高效发送文件 int fd = fileno(file); off_t offset = 0; long remaining = file_size; while (remaining > 0) { ssize_t sent = sendfile(client_fd, fd, &offset, remaining); if (sent <= 0) { LOG_ERROR("sendfile failed: %s, sent: %zd/%ld bytes", strerror(errno), file_size - remaining, file_size); break; } remaining -= sent; LOG_DEBUG("Sent %zd bytes, remaining: %ld bytes", sent, remaining); } if (remaining == 0) { LOG_DEBUG("File sent completely: %s", file_path); } else { LOG_WARN("File partially sent: %s (%ld/%ld bytes)", file_path, file_size - remaining, file_size); } fclose(file); } "thread_pool.c" #include "thread_pool.h" #include "debug.h" #include <stdlib.h> #include <stdio.h> void queue_init(task_queue_t *queue, int capacity) { LOG_DEBUG("Initializing task queue (capacity: %d)", capacity); queue->tasks = malloc(capacity * sizeof(task_t)); if (!queue->tasks) { LOG_CRITICAL("Queue malloc failed: %s", strerror(errno)); exit(EXIT_FAILURE); } queue->front = 0; queue->rear = -1; queue->size = 0; queue->capacity = capacity; } void queue_push(task_queue_t *queue, task_t task) { if (queue->size >= queue->capacity) { LOG_ERROR("Task queue overflow (capacity: %d)", queue->capacity); return; } queue->rear = (queue->rear + 1) % queue->capacity; queue->tasks[queue->rear] = task; queue->size++; LOG_DEBUG("Task added to queue (size: %d/%d)", queue->size, queue->capacity); } task_t queue_pop(task_queue_t *queue) { if (queue->size <= 0) { LOG_ERROR("Attempted to pop from empty task queue"); task_t empty_task = {NULL, NULL}; return empty_task; } task_t task = queue->tasks[queue->front]; queue->front = (queue->front + 1) % queue->capacity; queue->size--; LOG_DEBUG("Task popped from queue (size: %d/%d)", queue->size, queue->capacity); return task; } int queue_empty(task_queue_t *queue) { return queue->size == 0; } void queue_clean(task_queue_t *queue) { LOG_DEBUG("Cleaning task queue"); free(queue->tasks); queue->tasks = NULL; queue->front = 0; queue->rear = -1; queue->size = 0; queue->capacity = 0; } void *worker_thread(void *arg) { thread_pool_t *pool = (thread_pool_t *)arg; LOG_DEBUG("Worker thread started (TID: %lu)", (unsigned long)pthread_self()); while (1) { pthread_mutex_lock(&pool->lock); // 等待新任务或关闭信号 while (queue_empty(&pool->queue) && !pool->shutdown) { LOG_DEBUG("Worker thread waiting (TID: %lu)", (unsigned long)pthread_self()); pthread_cond_wait(&pool->cond, &pool->lock); } // 检查是否关闭 if (pool->shutdown) { pthread_mutex_unlock(&pool->lock); LOG_DEBUG("Worker thread exiting (TID: %lu)", (unsigned long)pthread_self()); pthread_exit(NULL); } // 获取任务 task_t task = queue_pop(&pool->queue); pthread_mutex_unlock(&pool->lock); // 执行任务 if (task.function) { LOG_DEBUG("Worker thread executing task (TID: %lu)", (unsigned long)pthread_self()); task.function(task.arg); } } return NULL; } thread_pool_t *thread_pool_create(int num_threads) { LOG_INFO("Creating thread pool (size: %d)", num_threads); thread_pool_t *pool = malloc(sizeof(thread_pool_t)); if (!pool) { LOG_CRITICAL("Thread pool malloc failed: %s", strerror(errno)); exit(EXIT_FAILURE); } pool->num_threads = num_threads; pool->shutdown = 0; // 初始化任务队列 queue_init(&pool->queue, 128); // 初始化互斥锁条件变量 if (pthread_mutex_init(&pool->lock, NULL) != 0) { LOG_CRITICAL("Mutex init failed: %s", strerror(errno)); free(pool); exit(EXIT_FAILURE); } if (pthread_cond_init(&pool->cond, NULL) != 0) { LOG_CRITICAL("Condition init failed: %s", strerror(errno)); pthread_mutex_destroy(&pool->lock); free(pool); exit(EXIT_FAILURE); } // 创建工作线程 pool->threads = malloc(num_threads * sizeof(pthread_t)); if (!pool->threads) { LOG_CRITICAL("Thread array malloc failed: %s", strerror(errno)); pthread_mutex_destroy(&pool->lock); pthread_cond_destroy(&pool->cond); free(pool); exit(EXIT_FAILURE); } for (int i = 0; i < num_threads; i++) { if (pthread_create(&pool->threads[i], NULL, worker_thread, pool) != 0) { LOG_CRITICAL("Thread creation failed: %s", strerror(errno)); // 清理已创建的线程 for (int j = 0; j < i; j++) { pthread_cancel(pool->threads[j]); } pthread_mutex_destroy(&pool->lock); pthread_cond_destroy(&pool->cond); free(pool->threads); free(pool); exit(EXIT_FAILURE); } LOG_DEBUG("Worker thread %d created (TID: %lu)", i, (unsigned long)pool->threads[i]); } LOG_INFO("Thread pool created successfully"); return pool; } void thread_pool_add_task(thread_pool_t *pool, void (*function)(void *), void *arg) { pthread_mutex_lock(&pool->lock); // 创建新任务 task_t task; task.function = function; task.arg = arg; // 添加到队列 queue_push(&pool->queue, task); // 通知工作线程 pthread_cond_signal(&pool->cond); pthread_mutex_unlock(&pool->lock); LOG_DEBUG("Task added to thread pool"); } void thread_pool_destroy(thread_pool_t *pool) { LOG_INFO("Destroying thread pool"); pthread_mutex_lock(&pool->lock); pool->shutdown = 1; pthread_cond_broadcast(&pool->cond); pthread_mutex_unlock(&pool->lock); // 等待所有线程退出 for (int i = 0; i < pool->num_threads; i++) { LOG_DEBUG("Joining worker thread %d (TID: %lu)", i, (unsigned long)pool->threads[i]); pthread_join(pool->threads[i], NULL); } // 清理资源 LOG_DEBUG("Freeing thread array"); free(pool->threads); LOG_DEBUG("Cleaning task queue"); queue_clean(&pool->queue); LOG_DEBUG("Destroying mutex and condition"); pthread_mutex_destroy(&pool->lock); pthread_cond_destroy(&pool->cond); LOG_DEBUG("Freeing thread pool structure"); free(pool); LOG_INFO("Thread pool destroyed successfully"); } 多进程: /* !Copyright(c) 2022-2025 TP-LINK Technologies CO.LTD. * All rights reserved. * * \file server_process.c * \brief This source file is for testing code. * * \author zhaoyunlong <zhaoyunlong1@tp-link.com.hk> * \version 1.0.0 * \date 07/8/2025 * * \history \arg 1.0.0, 07/8/2025, zhaoyunlong, Create file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/wait.h> #include <signal.h> #include "http_parser.h" #include "http_response.h" #include "utils.h" #include "debug.h" #define PORT 80 /* 服务器监听端口 */ #define BACKLOG 128 /* 等待连接队伍的最大长度 */ #define BUFFER_SIZE 8192 /* 接收http请求的缓冲区大小(8KB) */ // 客户端处理函数 void handle_client(void *arg) { LOG_DEBUG("Child process started (PID: %d)", getpid()); int client_fd = *(int *)arg; char buffer[BUFFER_SIZE]; char client_ip[INET_ADDRSTRLEN]; // 获取客户端IP struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); /* 获取套接字关联的远程协议地址 */ if (getpeername(client_fd, (struct sockaddr *)&client_addr, &addr_len) < 0) { LOG_ERROR("getpeername failed: %s", strerror(errno)); } inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN); /* 将数值格式的IP地址转换为点分十进制格式 */ LOG_DEBUG("Client IP: %s", client_ip); // 接收HTTP请求 ssize_t bytes_read = recv(client_fd, buffer, BUFFER_SIZE - 1, 0); /* 等待接收数据 */ if (bytes_read <= 0) { if (bytes_read == 0) { LOG_DEBUG("Connection closed by client"); } else { LOG_ERROR("recv failed: %s", strerror(errno)); } close(client_fd); free(arg); return; } buffer[bytes_read] = '\0'; LOG_DEBUG("Received %zd bytes from %s", bytes_read, client_ip); // 解析HTTP请求 http_request_t req; if (parse_http_request(buffer, &req) != 0) { LOG_ERROR("Failed to parse HTTP request"); log_request(client_ip, "UNKNOWN", "INVALID", 400); send_error_response(client_fd, 400, "Bad Request"); close(client_fd); free(arg); return; } // 记录请求日志 LOG_INFO("Request: %s %s from %s", req.method, req.path, client_ip); // 解析并记录POST请求体 if (strcmp(req.method, "POST") == 0) { LOG_DEBUG("Processing POST request"); if (parse_request_body(client_fd, &req, buffer, bytes_read) == 0) { log_post_body(&req, client_ip); } else { LOG_WARN("Failed to parse POST body"); } } // 处理请求 if ((strcmp(req.method, "GET") == 0) || (strcmp(req.method, "POST") == 0)) { handle_http_request(client_fd, &req); } else { LOG_WARN("Unsupported method: %s", req.method); send_error_response(client_fd, 501, "Not Implemented"); } close(client_fd); free(arg); LOG_DEBUG("Child process completed (PID: %d)", getpid()); } // SIGCHLD信号处理函数(回收僵尸进程) void sigchld_handler(int sig) { int status; pid_t pid; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { LOG_DEBUG("Child process %d exited with status %d", pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { LOG_WARN("Child process %d killed by signal %d", pid, WTERMSIG(status)); } } } int main() { // 初始化调试系统 log_init("./log/"); debug_init(DEBUG_LEVEL_INFO); int server_fd; int client_fd; struct sockaddr_in server_addr; LOG_INFO("Starting multi-process server on port %d", PORT); // 创建套接字 CHECK((server_fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0, exit(EXIT_FAILURE), "socket failed: %s", strerror(errno)); LOG_DEBUG("Socket created: fd=%d", server_fd); // 设置端口复用 int opt = 1; CHECK(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "setsockopt failed: %s", strerror(errno)); // 绑定地址 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); CHECK(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "bind failed: %s", strerror(errno)); // 开始监听 CHECK(listen(server_fd, BACKLOG) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "listen failed: %s", strerror(errno)); // 设置SIGCHLD信号处理僵尸进程 signal(SIGCHLD, sigchld_handler); // 安装信号处理器 LOG_INFO("Web server running on port %d (PID: %d)", PORT, getpid()); while (1) { LOG_DEBUG("Waiting for new connection..."); // 接受新连接 struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { LOG_ERROR("accept failed: %s", strerror(errno)); continue; } LOG_DEBUG("New connection accepted: fd=%d", client_fd); // 分配客户端套接字内存 int *client_ptr = malloc(sizeof(int)); if (!client_ptr) { LOG_CRITICAL("malloc failed: %s", strerror(errno)); close(client_fd); continue; } *client_ptr = client_fd; LOG_DEBUG("Allocated client context: %p", (void *)client_ptr); /* 多进程处理 */ // 创建子进程处理连接 pid_t pid = fork(); if (pid == 0) { // 子进程 close(server_fd); // 关闭监听套接字 handle_client(client_ptr); exit(EXIT_SUCCESS); // 处理完成后退出 } else if (pid > 0) { // 父进程 close(client_fd); // 关闭客户端套接字(父进程不需要) LOG_DEBUG("Forked child process PID: %d", pid); } else { LOG_ERROR("fork failed: %s", strerror(errno)); perror("fork failed"); close(client_fd); } } // 清理资源 LOG_INFO("Shutting down server..."); close(server_fd); LOG_INFO("Server shutdown complete"); return 0; } 多线程: /* !Copyright(c) 2022-2025 TP-LINK Technologies CO.LTD. * All rights reserved. * * \file server_pthread.c * \brief This source file is for testing code. * * \author zhaoyunlong <zhaoyunlong1@tp-link.com.hk> * \version 1.0.0 * \date 07/8/2025 * * \history \arg 1.0.0, 07/8/2025, zhaoyunlong, Create file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> #include "thread_pool.h" #include "http_parser.h" #include "http_response.h" #include "utils.h" #include "debug.h" #define PORT 80 /* 服务器监听端口 */ #define BACKLOG 128 /* 等待连接队伍的最大长度 */ #define THREAD_POOL_SIZE 16 /* 线程池的工作线程数量 */ #define BUFFER_SIZE 8192 /* 接收http请求的缓冲区大小(8KB) */ // 客户端处理函数 void handle_client(void *arg) { LOG_DEBUG("Client handler started"); int client_fd = *(int *)arg; char buffer[BUFFER_SIZE]; char client_ip[INET_ADDRSTRLEN]; // 获取客户端IP struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); /* 获取套接字关联的远程协议地址 */ if (getpeername(client_fd, (struct sockaddr *)&client_addr, &addr_len) < 0) { LOG_ERROR("getpeername failed: %s", strerror(errno)); } inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN); /* 将数值格式的IP地址转换为点分十进制格式 */ LOG_DEBUG("Client IP: %s", client_ip); // 接收HTTP请求 ssize_t bytes_read = recv(client_fd, buffer, BUFFER_SIZE - 1, 0); /* 等待接收数据 */ if (bytes_read <= 0) { if (bytes_read == 0) { LOG_DEBUG("Connection closed by client"); } else { LOG_ERROR("recv failed: %s", strerror(errno)); } close(client_fd); free(arg); return; } buffer[bytes_read] = '\0'; LOG_DEBUG("Received %zd bytes from %s", bytes_read, client_ip); // 解析HTTP请求 http_request_t req; if (parse_http_request(buffer, &req) != 0) { LOG_ERROR("Failed to parse HTTP request"); log_request(client_ip, "UNKNOWN", "INVALID", 400); send_error_response(client_fd, 400, "Bad Request"); close(client_fd); free(arg); return; } // 记录请求日志 LOG_INFO("Request: %s %s from %s", req.method, req.path, client_ip); // 解析并记录POST请求体 if (strcmp(req.method, "POST") == 0) { LOG_DEBUG("Processing POST request"); if (parse_request_body(client_fd, &req, buffer, bytes_read) == 0) { log_post_body(&req, client_ip); } else { LOG_WARN("Failed to parse POST body"); } } // 处理请求 if ((strcmp(req.method, "GET") == 0) || (strcmp(req.method, "POST") == 0)) { handle_http_request(client_fd, &req); } else { LOG_WARN("Unsupported method: %s", req.method); send_error_response(client_fd, 501, "Not Implemented"); } // 释放资源 free_request_body(&req); close(client_fd); free(arg); LOG_DEBUG("Client handler finished"); } int main() { // 初始化调试系统 log_init("./log/"); debug_init(DEBUG_LEVEL_INFO); int server_fd; int client_fd; struct sockaddr_in server_addr; LOG_INFO("Starting web server on port %d", PORT); // 创建套接字 CHECK((server_fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0, exit(EXIT_FAILURE), "socket failed: %s", strerror(errno)); LOG_DEBUG("Socket created: fd=%d", server_fd); // 设置端口复用 int opt = 1; CHECK(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "setsockopt failed: %s", strerror(errno)); // 绑定地址 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); CHECK(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "bind failed: %s", strerror(errno)); // 开始监听 CHECK(listen(server_fd, BACKLOG) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "listen failed: %s", strerror(errno)); // 全局线程池 thread_pool_t *pool = NULL; // 初始化线程池 pool = thread_pool_create(THREAD_POOL_SIZE); CHECK(pool != NULL, { close(server_fd); exit(EXIT_FAILURE); }, "Thread pool creation failed"); LOG_INFO("Web server running on port %d", PORT); while (1) { LOG_DEBUG("Waiting for new connection..."); // 接受新连接 struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { LOG_ERROR("accept failed: %s", strerror(errno)); continue; } LOG_DEBUG("New connection accepted: fd=%d", client_fd); // 分配客户端套接字内存 int *client_ptr = malloc(sizeof(int)); if (!client_ptr) { LOG_CRITICAL("malloc failed: %s", strerror(errno)); close(client_fd); continue; } *client_ptr = client_fd; LOG_DEBUG("Allocated client context: %p", (void *)client_ptr); /* 多线程处理--线程池 */ // 将任务加入线程池 thread_pool_add_task(pool, handle_client, client_ptr); } // 清理资源 LOG_INFO("Shutting down server..."); thread_pool_destroy(pool); close(server_fd); LOG_INFO("Server shutdown complete"); return 0; } 多路IO复用: /* !Copyright(c) 2022-2025 TP-LINK Technologies CO.LTD. * All rights reserved. * * \file server_epoll.c * \brief This source file is for testing code. * * \author zhaoyunlong <zhaoyunlong1@tp-link.com.hk> * \version 1.0.0 * \date 07/8/2025 * * \history \arg 1.0.0, 07/8/2025, zhaoyunlong, Create file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/wait.h> #include <sys/epoll.h> #include <fcntl.h> #include <errno.h> #include "http_parser.h" #include "http_response.h" #include "utils.h" #include "debug.h" #define PORT 80 /* 服务器监听端口 */ #define BACKLOG 128 /* 等待连接队伍的最大长度 */ #define MAX_EVENTS 1024 /* epoll最大事件数 */ #define BUFFER_SIZE 8192 /* 接收http请求的缓冲区大小(8KB) */ // 客户端连接信息结构体 typedef struct { int fd; // 客户端文件描述符 char buffer[BUFFER_SIZE]; // 接收缓冲区 ssize_t bytes_received; // 已接收字节数 char client_ip[INET_ADDRSTRLEN]; // 客户端IP地址 } client_info_t; // 处理客户端请求 void process_client(int client_fd, client_info_t *client) { LOG_DEBUG("Processing client request (fd: %d)", client_fd); http_request_t req; // 解析HTTP请求 if (parse_http_request(client->buffer, &req) != 0) { // 解析失败记录错误日志 LOG_ERROR("Failed to parse HTTP request"); log_request(client->client_ip, "UNKNOWN", "INVALID", 400); send_error_response(client_fd, 400, "Bad Request"); return; } // 记录成功请求日志 LOG_INFO("Request: %s %s from %s", req.method, req.path, client->client_ip); // 解析并记录POST请求体 if (strcmp(req.method, "POST") == 0) { LOG_DEBUG("Processing POST request"); if (parse_request_body(client_fd, &req, client->buffer, client->bytes_received) == 0) { log_post_body(&req, client->client_ip); } else { LOG_WARN("Failed to parse POST body"); } } // 处理GET/POST请求 if (strcmp(req.method, "GET") == 0 || strcmp(req.method, "POST") == 0) { handle_http_request(client_fd, &req); } else { // 不支持的HTTP方法 LOG_WARN("Unsupported method: %s", req.method); send_error_response(client_fd, 501, "Not Implemented"); } } int main() { // 初始化调试系统 log_init("./log/"); debug_init(DEBUG_LEVEL_INFO); int server_fd; int epoll_fd; struct sockaddr_in server_addr; struct epoll_event ev, events[MAX_EVENTS]; // epoll事件结构 LOG_INFO("Starting epoll server on port %d", PORT); // 创建套接字 CHECK((server_fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0, exit(EXIT_FAILURE), "socket failed: %s", strerror(errno)); LOG_DEBUG("Socket created: fd=%d", server_fd); // 设置端口复用 int opt = 1; CHECK(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "setsockopt failed: %s", strerror(errno)); // 绑定地址 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); CHECK(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "bind failed: %s", strerror(errno)); // 开始监听 CHECK(listen(server_fd, BACKLOG) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "listen failed: %s", strerror(errno)); // 创建epoll实例 CHECK((epoll_fd = epoll_create1(0)) >= 0, { close(server_fd); exit(EXIT_FAILURE); }, "epoll_create1 failed: %s", strerror(errno)); LOG_DEBUG("Epoll instance created: fd=%d", epoll_fd); // 添加服务器套接字到epoll监听 ev.events = EPOLLIN; // 监听可读事件 ev.data.fd = server_fd; // 关联服务器文件描述符 CHECK(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) == 0, { close(server_fd); close(epoll_fd); exit(EXIT_FAILURE); }, "epoll_ctl server_fd failed: %s", strerror(errno)); LOG_INFO("Web server running on port %d", PORT); while (1) { // 等待事件发生,-1表示无限等待 int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds < 0) { if (errno == EINTR) continue; // 被信号中断 LOG_ERROR("epoll_wait failed: %s", strerror(errno)); continue; } LOG_DEBUG("Epoll events triggered: %d", nfds); // 处理所有就绪事件 for (int i = 0; i < nfds; i++) { // 处理新客户端连接 if (events[i].data.fd == server_fd) { struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); // 接受新连接 int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { LOG_ERROR("accept failed: %s", strerror(errno)); continue; } LOG_DEBUG("New connection accepted: fd=%d", client_fd); // 设置非阻塞模式(边缘触发必须) int flags = fcntl(client_fd, F_GETFL, 0); if (fcntl(client_fd, F_SETFL, flags | O_NONBLOCK) < 0) { LOG_ERROR("fcntl nonblock failed: %s", strerror(errno)); close(client_fd); continue; } // 创建并初始化客户端信息结构 client_info_t *client = malloc(sizeof(client_info_t)); if (!client) { LOG_CRITICAL("malloc client_info failed: %s", strerror(errno)); close(client_fd); continue; } memset(client, 0, sizeof(client_info_t)); client->fd = client_fd; // 获取客户端IP inet_ntop(AF_INET, &client_addr.sin_addr, client->client_ip, INET_ADDRSTRLEN); LOG_DEBUG("Client IP: %s", client->client_ip); // 添加客户端到epoll监听(边缘触发模式) ev.events = EPOLLIN | EPOLLET; // 边缘触发模式 ev.data.ptr = client; // 关联客户端数据结构 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) < 0) { LOG_ERROR("epoll_ctl client_fd failed: %s", strerror(errno)); close(client_fd); free(client); } else { LOG_DEBUG("Added client to epoll: fd=%d", client_fd); } } // 处理客户端数据可读事件 else if (events[i].events & EPOLLIN) { client_info_t *client = (client_info_t *)events[i].data.ptr; LOG_DEBUG("Data available on fd=%d", client->fd); // 非阻塞读取数据 ssize_t count = recv(client->fd, client->buffer + client->bytes_received, BUFFER_SIZE - client->bytes_received - 1, 0); if (count > 0) { // 更新已接收字节数 client->bytes_received += count; client->buffer[client->bytes_received] = '\0'; // 确保字符串结束 LOG_DEBUG("Received %zd bytes (total %zd) from %s", count, client->bytes_received, client->client_ip); // 检查HTTP请求是否结束(通过空行判断) if (strstr(client->buffer, "\r\n\r\n") != NULL) { LOG_DEBUG("Full request received from %s", client->client_ip); // 移除epoll监听 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client->fd, NULL); // 处理完整请求 process_client(client->fd, client); // 关闭连接并释放资源 close(client->fd); free(client); } } // 处理连接关闭或错误 else if (count == 0 || (count < 0 && errno != EAGAIN)) { LOG_DEBUG("Connection closed by client: %s", client->client_ip); // 移除epoll监听 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client->fd, NULL); close(client->fd); free(client); } // EAGAIN错误在边缘触发模式下是正常的,表示数据已读完 } } } // 清理资源 LOG_INFO("Shutting down epoll server..."); close(epoll_fd); close(server_fd); LOG_INFO("Server shutdown complete"); return 0; } makefile: SRC_DIR = ./src INCLUDE_DIR = ./include BUILD_DIR = ./build OBJ_DIR := $(BUILD_DIR)/obj # 编译器设置 CC = gcc CFLAGS = -Wall -I $(INCLUDE_DIR) -pthread -Iinclude # 公共模块(所有服务器共享) COMMON_SRCS := \ $(SRC_DIR)/http_parser.c \ $(SRC_DIR)/http_response.c \ $(SRC_DIR)/thread_pool.c \ $(SRC_DIR)/utils.c \ $(SRC_DIR)/debug.c \ COMMON_OBJS := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(COMMON_SRCS)) # 服务器主程序 SERVER_PROCESS_SRC := $(SRC_DIR)/server_process.c SERVER_PTHREAD_SRC := $(SRC_DIR)/server_pthread.c SERVER_EPOLL_SRC := $(SRC_DIR)/server_epoll.c SERVER_PROCESS_OBJ := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SERVER_PROCESS_SRC)) SERVER_PTHREAD_OBJ := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SERVER_PTHREAD_SRC)) SERVER_EPOLL_OBJ := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SERVER_EPOLL_SRC)) # 最终可执行文件 TARGET_PROCESS = $(BUILD_DIR)/server_process TARGET_PTHREAD = $(BUILD_DIR)/server_pthread TARGET_EPOLL = $(BUILD_DIR)/server_epoll all: $(TARGET_PROCESS) $(TARGET_PTHREAD) $(TARGET_EPOLL) # 创建build目录 $(BUILD_DIR) $(OBJ_DIR): @mkdir -p $@ # 编译公共模块 $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) $(CC) $(CFLAGS) -c $< -o $@ # 编译服务器主程序 $(OBJ_DIR)/server_%.o: $(SRC_DIR)/server_%.c | $(OBJ_DIR) $(CC) $(CFLAGS) -c $< -o $@ # 链接生成可执行文件 $(TARGET_PROCESS): $(SERVER_PROCESS_OBJ) $(COMMON_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) @echo "$@ building success!......" $(TARGET_PTHREAD): $(SERVER_PTHREAD_OBJ) $(COMMON_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) @echo "$@ building success!......" $(TARGET_EPOLL): $(SERVER_EPOLL_OBJ) $(COMMON_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) @echo "$@ building success!......" # 清理编译文件 clean: @echo "clean..." rm -rf $(BUILD_DIR) 你学习一下我的代码,帮我写一下报告,注意报告中不要贴代码,你主要是设计一下报告的框架写文字图片。我写了一部分了,你可以接着完善并继续写: 1. 需求介绍 1.1. 需求概述背景说明 本课题实验旨在通过使用多线程,多进程,IO多路复用三种不同的处理方式,实现一个web server以承载附件的web,并对比三种方式各自的优缺点。通过深入理解服务器架构设计的原理,掌握Linux环境下高性能网络编程的核心技术。 1.2. 课题目的 1. 熟悉公司的开发设计流程 2. 熟悉HTTP协议 3. 熟悉并掌握Linux环境应用开发 4. 熟悉socket网络编程 5. 加强多线程/多进程编程能力 6. 熟悉经典IO复用设计模型 1.3. 课题要求 1. 实现以多线程方式实现Web server功能(必修) 2. 实现以多进程方式实现Web server功能(必修) 3. 实现以IO多路复用方式实现Web server功能(必修) 4. 提供完善Debug机制(必修) 5. Web server需完成GETPOST等基础请求处理(必修) 6. 如何使其具备高并发性并实现(选修) 7. 使用Makefile构建(必修) 2. 系统架构描述 2.1. 系统框架图 通过http协议构建一个Web服务器,该服务器能够处理浏览器发过来的http请求,并根据http请求返回http响应给浏览器。该服务器的功能上可以存放各种各样的资源,用户可以利用浏览器来访问我的服务器资源,也可以提交数据给服务器,让服务器去处理结果,并返回给浏览器。 2.2. 模块划分 2.3. 模块描述 3. 4.
08-16
你写的报告有问题,没有结合我的程序本身,你再学习一下我的程序,帮我完善一下报告,注意多写点文字,我自己把代码截图贴进去,我的代码这样: 下面是我的代码: “utils.c” #include “utils.h” #include “debug.h” #include <time.h> const char *get_content_type(const char *path) { LOG_DEBUG(“Determining content type for: %s”, path); const char *ext = strrchr(path, '.'); if (!ext) { LOG_DEBUG("No file extension, defaulting to text/plain"); return "text/plain"; } if (strcmp(ext, ".html") == 0) return "text/html"; if (strcmp(ext, ".css") == 0) return "text/css"; if (strcmp(ext, ".js") == 0) return "application/javascript"; if (strcmp(ext, ".json") == 0) return "application/json"; if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; if (strcmp(ext, ".png") == 0) return "image/png"; if (strcmp(ext, ".gif") == 0) return "image/gif"; if (strcmp(ext, ".ico") == 0) return "image/x-icon"; LOG_DEBUG("Unknown file extension '%s', defaulting to text/plain", ext); return "text/plain"; } int file_exists(const char *path) { LOG_DEBUG(“Checking file existence: %s”, path); int exists = access(path, F_OK) != -1; if (!exists) { LOG_WARN(“File not found: %s”, path); } return exists; } long get_file_size(FILE *file) { if (fseek(file, 0, SEEK_END) != 0) { LOG_ERROR(“fseek(SEEK_END) failed: %s”, strerror(errno)); return -1; } long size = ftell(file); if (size == -1) { LOG_ERROR("ftell failed: %s", strerror(errno)); } if (fseek(file, 0, SEEK_SET) != 0) { LOG_ERROR("fseek(SEEK_SET) failed: %s", strerror(errno)); } LOG_DEBUG("File size: %ld bytes", size); return size; } void log_request(const char *client_ip, const char *method, const char *path, int status) { time_t now = time(NULL); char time_buf[64]; strftime(time_buf, sizeof(time_buf), “%Y-%m-%d %H:%M:%S”, localtime(&now)); // 根据状态码确定日志级别 if (status >= 400) { LOG_ERROR("[%s] %s - %s %s - %d", time_buf, client_ip, method, path, status); } else if (status >= 300) { LOG_WARN("[%s] %s - %s %s - %d", time_buf, client_ip, method, path, status); } else { LOG_INFO("[%s] %s - %s %s - %d", time_buf, client_ip, method, path, status); } } “http_parser.c” #include “http_parser.h” #include “debug.h” #include <string.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> int parse_http_request(const char *request, http_request_t *req) { LOG_DEBUG(“Parsing HTTP request”); char buffer[4096]; strncpy(buffer, request, sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = ‘\0’; // 解析请求行 char *line = strtok(buffer, "\r\n"); if (!line) { LOG_ERROR("Empty request line"); return -1; } // 解析方法、路径HTTP版本 if (sscanf(line, "%15s %1023s %15s", req->method, req->path, req->version) != 3) { LOG_ERROR("Invalid request line: %s", line); return -1; } LOG_DEBUG("Request line: %s %s %s", req->method, req->path, req->version); // 解析头部字段 req->content_length = 0; memset(req->host, 0, sizeof(req->host)); while ((line = strtok(NULL, "\r\n")) != NULL && line[0] != '\0') { if (strncasecmp(line, "Host:", 5) == 0) { sscanf(line + 5, " %255s", req->host); LOG_DEBUG("Host: %s", req->host); } else if (strncasecmp(line, "Content-Length:", 15) == 0) { sscanf(line + 15, " %d", &req->content_length); LOG_DEBUG("Content-Length: %d", req->content_length); } } return 0; } int parse_request_body(int client_fd, http_request_t *req, const char *buffer, ssize_t bytes_read) { if (strcmp(req->method, “POST”) != 0 || req->content_length <= 0) { LOG_DEBUG(“Skipping body parsing for non-POST request”); return 0; // 非POST请求或没有内容体 } LOG_DEBUG("Parsing POST request body (length: %d)", req->content_length); // 定位请求头结束位置 const char *body_start = strstr(buffer, "\r\n\r\n"); if (!body_start) { LOG_ERROR("Failed to find body separator in request"); return -1; } body_start += 4; // 跳过空行 size_t header_length = body_start - buffer; size_t body_received = (bytes_read > header_length) ? (bytes_read - header_length) : 0; LOG_DEBUG("Header length: %zu, Body received: %zu", header_length, body_received); // 分配内存存储请求体 req->body = malloc(req->content_length + 1); if (!req->body) { LOG_CRITICAL("Body allocation failed: %s", strerror(errno)); return -1; } // 复制已接收部分 size_t to_copy = (body_received < (size_t)req->content_length) ? body_received : (size_t)req->content_length; memcpy(req->body, body_start, to_copy); req->body[req->content_length] = '\0'; LOG_DEBUG("Body content (%d bytes):\n%.*s", req->content_length, (int)req->content_length, req->body); return 0; } void free_request_body(http_request_t *req) { if (req->body) { LOG_DEBUG(“Freeing request body”); free(req->body); req->body = NULL; } } // URL解码函数 void urldecode(char *src) { char *p = src; char *q = src; while (*p) { if (*p == ‘+’) { *q++ = ’ '; p++; } else if (*p == ‘%’ && isxdigit(p[1]) && isxdigit(p[2])) { *q++ = (char)strtol((char[]){p[1], p[2], 0}, NULL, 16); p += 3; } else { *q++ = *p++; } } *q = ‘\0’; } void log_post_body(const http_request_t *req, const char *client_ip) { if (!req->body || req->content_length <= 0) { LOG_DEBUG(“No POST body to log”); return; } // 安全截断长请求体 int log_length = (req->content_length > 1024) ? 1024 : req->content_length; char buf[log_length + 1]; memcpy(buf, req->body, log_length); buf[log_length] = '\0'; LOG_INFO("POST data from %s for %s (length: %d)", client_ip, req->path, req->content_length); // 解析并打印结构化参数 char *pair = strtok(buf, "&"); while (pair) { char *eq = strchr(pair, '='); if (eq) { *eq = '\0'; char *key = pair; char *value = eq + 1; // 对键值进行URL解码 urldecode(key); urldecode(value); LOG_INFO(" %-8s: %s", key, value); } pair = strtok(NULL, "&"); } } “http_response.c” #include “http_response.h” #include “utils.h” #include “debug.h” #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/sendfile.h> #include <fcntl.h> #include <sys/stat.h> #include <arpa/inet.h> void send_http_response(int client_fd, int status, const char *message, const char *content_type, long content_length) { char header[1024]; snprintf(header, sizeof(header), “HTTP/1.1 %d %s\r\n” “Content-Type: %s\r\n” “Content-Length: %ld\r\n\r\n”, // “Connection: close\r\n\r\n”, status, message, content_type, content_length); LOG_DEBUG("Sending HTTP response header:\n%s", header); ssize_t sent = send(client_fd, header, strlen(header), 0); if (sent != (ssize_t)strlen(header)) { LOG_ERROR("Partial header sent (%zd/%zu bytes)", sent, strlen(header)); } } void send_error_response(int client_fd, int status, const char *message) { char body[512]; snprintf(body, sizeof(body), “ %d %s”, status, message); LOG_WARN("Sending error response: %d %s", status, message); send_http_response(client_fd, status, message, "text/html", strlen(body)); ssize_t sent = send(client_fd, body, strlen(body), 0); if (sent != (ssize_t)strlen(body)) { LOG_ERROR("Partial error body sent (%zd/%zu bytes)", sent, strlen(body)); } } void handle_http_request(int client_fd, http_request_t *req) { LOG_DEBUG(“Handling HTTP request: %s %s”, req->method, req->path); char file_path[2048]; // 默认首页 if (strcmp(req->path, "/") == 0) { snprintf(file_path, sizeof(file_path), "web/Index.html"); } else { snprintf(file_path, sizeof(file_path), "web%s", req->path); } LOG_DEBUG("Resolved file path: %s", file_path); // 防止路径遍历攻击 if (strstr(file_path, "..")) { LOG_WARN("Path traversal attempt detected: %s", file_path); send_error_response(client_fd, 403, "Forbidden"); return; } // 检查文件是否存在 if (!file_exists(file_path)) { LOG_WARN("File not found: %s", file_path); send_error_response(client_fd, 404, "Not Found"); return; } // 打开文件 FILE *file = fopen(file_path, "rb"); if (!file) { LOG_ERROR("File open failed: %s, error: %s", file_path, strerror(errno)); send_error_response(client_fd, 500, "Internal Server Error"); return; } LOG_DEBUG("File opened successfully: %s", file_path); // 获取文件大小 long file_size = get_file_size(file); // 发送HTTP头 const char *content_type = get_content_type(file_path); LOG_DEBUG("Sending file: %s, type: %s, size: %ld", file_path, content_type, file_size); send_http_response(client_fd, 200, "OK", content_type, file_size); // 使用sendfile高效发送文件 int fd = fileno(file); off_t offset = 0; long remaining = file_size; while (remaining > 0) { ssize_t sent = sendfile(client_fd, fd, &offset, remaining); if (sent <= 0) { LOG_ERROR("sendfile failed: %s, sent: %zd/%ld bytes", strerror(errno), file_size - remaining, file_size); break; } remaining -= sent; LOG_DEBUG("Sent %zd bytes, remaining: %ld bytes", sent, remaining); } if (remaining == 0) { LOG_DEBUG("File sent completely: %s", file_path); } else { LOG_WARN("File partially sent: %s (%ld/%ld bytes)", file_path, file_size - remaining, file_size); } fclose(file); } “thread_pool.c” #include “thread_pool.h” #include “debug.h” #include <stdlib.h> #include <stdio.h> void queue_init(task_queue_t *queue, int capacity) { LOG_DEBUG(“Initializing task queue (capacity: %d)”, capacity); queue->tasks = malloc(capacity * sizeof(task_t)); if (!queue->tasks) { LOG_CRITICAL(“Queue malloc failed: %s”, strerror(errno)); exit(EXIT_FAILURE); } queue->front = 0; queue->rear = -1; queue->size = 0; queue->capacity = capacity; } void queue_push(task_queue_t *queue, task_t task) { if (queue->size >= queue->capacity) { LOG_ERROR(“Task queue overflow (capacity: %d)”, queue->capacity); return; } queue->rear = (queue->rear + 1) % queue->capacity; queue->tasks[queue->rear] = task; queue->size++; LOG_DEBUG("Task added to queue (size: %d/%d)", queue->size, queue->capacity); } task_t queue_pop(task_queue_t *queue) { if (queue->size <= 0) { LOG_ERROR(“Attempted to pop from empty task queue”); task_t empty_task = {NULL, NULL}; return empty_task; } task_t task = queue->tasks[queue->front]; queue->front = (queue->front + 1) % queue->capacity; queue->size--; LOG_DEBUG("Task popped from queue (size: %d/%d)", queue->size, queue->capacity); return task; } int queue_empty(task_queue_t *queue) { return queue->size == 0; } void queue_clean(task_queue_t *queue) { LOG_DEBUG(“Cleaning task queue”); free(queue->tasks); queue->tasks = NULL; queue->front = 0; queue->rear = -1; queue->size = 0; queue->capacity = 0; } void *worker_thread(void *arg) { thread_pool_t *pool = (thread_pool_t *)arg; LOG_DEBUG(“Worker thread started (TID: %lu)”, (unsigned long)pthread_self()); while (1) { pthread_mutex_lock(&pool->lock); // 等待新任务或关闭信号 while (queue_empty(&pool->queue) && !pool->shutdown) { LOG_DEBUG("Worker thread waiting (TID: %lu)", (unsigned long)pthread_self()); pthread_cond_wait(&pool->cond, &pool->lock); } // 检查是否关闭 if (pool->shutdown) { pthread_mutex_unlock(&pool->lock); LOG_DEBUG("Worker thread exiting (TID: %lu)", (unsigned long)pthread_self()); pthread_exit(NULL); } // 获取任务 task_t task = queue_pop(&pool->queue); pthread_mutex_unlock(&pool->lock); // 执行任务 if (task.function) { LOG_DEBUG("Worker thread executing task (TID: %lu)", (unsigned long)pthread_self()); task.function(task.arg); } } return NULL; } thread_pool_t *thread_pool_create(int num_threads) { LOG_INFO(“Creating thread pool (size: %d)”, num_threads); thread_pool_t *pool = malloc(sizeof(thread_pool_t)); if (!pool) { LOG_CRITICAL(“Thread pool malloc failed: %s”, strerror(errno)); exit(EXIT_FAILURE); } pool->num_threads = num_threads; pool->shutdown = 0; // 初始化任务队列 queue_init(&pool->queue, 128); // 初始化互斥锁条件变量 if (pthread_mutex_init(&pool->lock, NULL) != 0) { LOG_CRITICAL("Mutex init failed: %s", strerror(errno)); free(pool); exit(EXIT_FAILURE); } if (pthread_cond_init(&pool->cond, NULL) != 0) { LOG_CRITICAL("Condition init failed: %s", strerror(errno)); pthread_mutex_destroy(&pool->lock); free(pool); exit(EXIT_FAILURE); } // 创建工作线程 pool->threads = malloc(num_threads * sizeof(pthread_t)); if (!pool->threads) { LOG_CRITICAL("Thread array malloc failed: %s", strerror(errno)); pthread_mutex_destroy(&pool->lock); pthread_cond_destroy(&pool->cond); free(pool); exit(EXIT_FAILURE); } for (int i = 0; i < num_threads; i++) { if (pthread_create(&pool->threads[i], NULL, worker_thread, pool) != 0) { LOG_CRITICAL("Thread creation failed: %s", strerror(errno)); // 清理已创建的线程 for (int j = 0; j < i; j++) { pthread_cancel(pool->threads[j]); } pthread_mutex_destroy(&pool->lock); pthread_cond_destroy(&pool->cond); free(pool->threads); free(pool); exit(EXIT_FAILURE); } LOG_DEBUG("Worker thread %d created (TID: %lu)", i, (unsigned long)pool->threads[i]); } LOG_INFO("Thread pool created successfully"); return pool; } void thread_pool_add_task(thread_pool_t *pool, void (*function)(void *), void *arg) { pthread_mutex_lock(&pool->lock); // 创建新任务 task_t task; task.function = function; task.arg = arg; // 添加到队列 queue_push(&pool->queue, task); // 通知工作线程 pthread_cond_signal(&pool->cond); pthread_mutex_unlock(&pool->lock); LOG_DEBUG("Task added to thread pool"); } void thread_pool_destroy(thread_pool_t *pool) { LOG_INFO(“Destroying thread pool”); pthread_mutex_lock(&pool->lock); pool->shutdown = 1; pthread_cond_broadcast(&pool->cond); pthread_mutex_unlock(&pool->lock); // 等待所有线程退出 for (int i = 0; i < pool->num_threads; i++) { LOG_DEBUG("Joining worker thread %d (TID: %lu)", i, (unsigned long)pool->threads[i]); pthread_join(pool->threads[i], NULL); } // 清理资源 LOG_DEBUG("Freeing thread array"); free(pool->threads); LOG_DEBUG("Cleaning task queue"); queue_clean(&pool->queue); LOG_DEBUG("Destroying mutex and condition"); pthread_mutex_destroy(&pool->lock); pthread_cond_destroy(&pool->cond); LOG_DEBUG("Freeing thread pool structure"); free(pool); LOG_INFO("Thread pool destroyed successfully"); } 多进程: /* !Copyright© 2022-2025 TP-LINK Technologies CO.LTD. All rights reserved. \file server_process.c \brief This source file is for testing code. \author zhaoyunlong zhaoyunlong1@tp-link.com.hk \version 1.0.0 \date 07/8/2025 \history \arg 1.0.0, 07/8/2025, zhaoyunlong, Create file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/wait.h> #include <signal.h> #include “http_parser.h” #include “http_response.h” #include “utils.h” #include “debug.h” #define PORT 80 /* 服务器监听端口 / #define BACKLOG 128 / 等待连接队伍的最大长度 / #define BUFFER_SIZE 8192 / 接收http请求的缓冲区大小(8KB) */ // 客户端处理函数 void handle_client(void *arg) { LOG_DEBUG(“Child process started (PID: %d)”, getpid()); int client_fd = *(int *)arg; char buffer[BUFFER_SIZE]; char client_ip[INET_ADDRSTRLEN]; // 获取客户端IP struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); /* 获取套接字关联的远程协议地址 */ if (getpeername(client_fd, (struct sockaddr *)&client_addr, &addr_len) < 0) { LOG_ERROR("getpeername failed: %s", strerror(errno)); } inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN); /* 将数值格式的IP地址转换为点分十进制格式 */ LOG_DEBUG("Client IP: %s", client_ip); // 接收HTTP请求 ssize_t bytes_read = recv(client_fd, buffer, BUFFER_SIZE - 1, 0); /* 等待接收数据 */ if (bytes_read <= 0) { if (bytes_read == 0) { LOG_DEBUG("Connection closed by client"); } else { LOG_ERROR("recv failed: %s", strerror(errno)); } close(client_fd); free(arg); return; } buffer[bytes_read] = '\0'; LOG_DEBUG("Received %zd bytes from %s", bytes_read, client_ip); // 解析HTTP请求 http_request_t req; if (parse_http_request(buffer, &req) != 0) { LOG_ERROR("Failed to parse HTTP request"); log_request(client_ip, "UNKNOWN", "INVALID", 400); send_error_response(client_fd, 400, "Bad Request"); close(client_fd); free(arg); return; } // 记录请求日志 LOG_INFO("Request: %s %s from %s", req.method, req.path, client_ip); // 解析并记录POST请求体 if (strcmp(req.method, "POST") == 0) { LOG_DEBUG("Processing POST request"); if (parse_request_body(client_fd, &req, buffer, bytes_read) == 0) { log_post_body(&req, client_ip); } else { LOG_WARN("Failed to parse POST body"); } } // 处理请求 if ((strcmp(req.method, "GET") == 0) || (strcmp(req.method, "POST") == 0)) { handle_http_request(client_fd, &req); } else { LOG_WARN("Unsupported method: %s", req.method); send_error_response(client_fd, 501, "Not Implemented"); } close(client_fd); free(arg); LOG_DEBUG("Child process completed (PID: %d)", getpid()); } // SIGCHLD信号处理函数(回收僵尸进程) void sigchld_handler(int sig) { int status; pid_t pid; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { LOG_DEBUG(“Child process %d exited with status %d”, pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { LOG_WARN(“Child process %d killed by signal %d”, pid, WTERMSIG(status)); } } } int main() { // 初始化调试系统 log_init(“./log/”); debug_init(DEBUG_LEVEL_INFO); int server_fd; int client_fd; struct sockaddr_in server_addr; LOG_INFO("Starting multi-process server on port %d", PORT); // 创建套接字 CHECK((server_fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0, exit(EXIT_FAILURE), "socket failed: %s", strerror(errno)); LOG_DEBUG("Socket created: fd=%d", server_fd); // 设置端口复用 int opt = 1; CHECK(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "setsockopt failed: %s", strerror(errno)); // 绑定地址 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); CHECK(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "bind failed: %s", strerror(errno)); // 开始监听 CHECK(listen(server_fd, BACKLOG) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "listen failed: %s", strerror(errno)); // 设置SIGCHLD信号处理僵尸进程 signal(SIGCHLD, sigchld_handler); // 安装信号处理器 LOG_INFO("Web server running on port %d (PID: %d)", PORT, getpid()); while (1) { LOG_DEBUG("Waiting for new connection..."); // 接受新连接 struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { LOG_ERROR("accept failed: %s", strerror(errno)); continue; } LOG_DEBUG("New connection accepted: fd=%d", client_fd); // 分配客户端套接字内存 int *client_ptr = malloc(sizeof(int)); if (!client_ptr) { LOG_CRITICAL("malloc failed: %s", strerror(errno)); close(client_fd); continue; } *client_ptr = client_fd; LOG_DEBUG("Allocated client context: %p", (void *)client_ptr); /* 多进程处理 */ // 创建子进程处理连接 pid_t pid = fork(); if (pid == 0) { // 子进程 close(server_fd); // 关闭监听套接字 handle_client(client_ptr); exit(EXIT_SUCCESS); // 处理完成后退出 } else if (pid > 0) { // 父进程 close(client_fd); // 关闭客户端套接字(父进程不需要) LOG_DEBUG("Forked child process PID: %d", pid); } else { LOG_ERROR("fork failed: %s", strerror(errno)); perror("fork failed"); close(client_fd); } } // 清理资源 LOG_INFO("Shutting down server..."); close(server_fd); LOG_INFO("Server shutdown complete"); return 0; } 多线程: /* !Copyright© 2022-2025 TP-LINK Technologies CO.LTD. All rights reserved. \file server_pthread.c \brief This source file is for testing code. \author zhaoyunlong zhaoyunlong1@tp-link.com.hk \version 1.0.0 \date 07/8/2025 \history \arg 1.0.0, 07/8/2025, zhaoyunlong, Create file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> #include “thread_pool.h” #include “http_parser.h” #include “http_response.h” #include “utils.h” #include “debug.h” #define PORT 80 /* 服务器监听端口 / #define BACKLOG 128 / 等待连接队伍的最大长度 / #define THREAD_POOL_SIZE 16 / 线程池的工作线程数量 / #define BUFFER_SIZE 8192 / 接收http请求的缓冲区大小(8KB) */ // 客户端处理函数 void handle_client(void *arg) { LOG_DEBUG(“Client handler started”); int client_fd = *(int *)arg; char buffer[BUFFER_SIZE]; char client_ip[INET_ADDRSTRLEN]; // 获取客户端IP struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); /* 获取套接字关联的远程协议地址 */ if (getpeername(client_fd, (struct sockaddr *)&client_addr, &addr_len) < 0) { LOG_ERROR("getpeername failed: %s", strerror(errno)); } inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN); /* 将数值格式的IP地址转换为点分十进制格式 */ LOG_DEBUG("Client IP: %s", client_ip); // 接收HTTP请求 ssize_t bytes_read = recv(client_fd, buffer, BUFFER_SIZE - 1, 0); /* 等待接收数据 */ if (bytes_read <= 0) { if (bytes_read == 0) { LOG_DEBUG("Connection closed by client"); } else { LOG_ERROR("recv failed: %s", strerror(errno)); } close(client_fd); free(arg); return; } buffer[bytes_read] = '\0'; LOG_DEBUG("Received %zd bytes from %s", bytes_read, client_ip); // 解析HTTP请求 http_request_t req; if (parse_http_request(buffer, &req) != 0) { LOG_ERROR("Failed to parse HTTP request"); log_request(client_ip, "UNKNOWN", "INVALID", 400); send_error_response(client_fd, 400, "Bad Request"); close(client_fd); free(arg); return; } // 记录请求日志 LOG_INFO("Request: %s %s from %s", req.method, req.path, client_ip); // 解析并记录POST请求体 if (strcmp(req.method, "POST") == 0) { LOG_DEBUG("Processing POST request"); if (parse_request_body(client_fd, &req, buffer, bytes_read) == 0) { log_post_body(&req, client_ip); } else { LOG_WARN("Failed to parse POST body"); } } // 处理请求 if ((strcmp(req.method, "GET") == 0) || (strcmp(req.method, "POST") == 0)) { handle_http_request(client_fd, &req); } else { LOG_WARN("Unsupported method: %s", req.method); send_error_response(client_fd, 501, "Not Implemented"); } // 释放资源 free_request_body(&req); close(client_fd); free(arg); LOG_DEBUG("Client handler finished"); } int main() { // 初始化调试系统 log_init(“./log/”); debug_init(DEBUG_LEVEL_INFO); int server_fd; int client_fd; struct sockaddr_in server_addr; LOG_INFO("Starting web server on port %d", PORT); // 创建套接字 CHECK((server_fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0, exit(EXIT_FAILURE), "socket failed: %s", strerror(errno)); LOG_DEBUG("Socket created: fd=%d", server_fd); // 设置端口复用 int opt = 1; CHECK(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "setsockopt failed: %s", strerror(errno)); // 绑定地址 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); CHECK(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "bind failed: %s", strerror(errno)); // 开始监听 CHECK(listen(server_fd, BACKLOG) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "listen failed: %s", strerror(errno)); // 全局线程池 thread_pool_t *pool = NULL; // 初始化线程池 pool = thread_pool_create(THREAD_POOL_SIZE); CHECK(pool != NULL, { close(server_fd); exit(EXIT_FAILURE); }, "Thread pool creation failed"); LOG_INFO("Web server running on port %d", PORT); while (1) { LOG_DEBUG("Waiting for new connection..."); // 接受新连接 struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { LOG_ERROR("accept failed: %s", strerror(errno)); continue; } LOG_DEBUG("New connection accepted: fd=%d", client_fd); // 分配客户端套接字内存 int *client_ptr = malloc(sizeof(int)); if (!client_ptr) { LOG_CRITICAL("malloc failed: %s", strerror(errno)); close(client_fd); continue; } *client_ptr = client_fd; LOG_DEBUG("Allocated client context: %p", (void *)client_ptr); /* 多线程处理--线程池 */ // 将任务加入线程池 thread_pool_add_task(pool, handle_client, client_ptr); } // 清理资源 LOG_INFO("Shutting down server..."); thread_pool_destroy(pool); close(server_fd); LOG_INFO("Server shutdown complete"); return 0; } 多路IO复用: /* !Copyright© 2022-2025 TP-LINK Technologies CO.LTD. All rights reserved. \file server_epoll.c \brief This source file is for testing code. \author zhaoyunlong zhaoyunlong1@tp-link.com.hk \version 1.0.0 \date 07/8/2025 \history \arg 1.0.0, 07/8/2025, zhaoyunlong, Create file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/wait.h> #include <sys/epoll.h> #include <fcntl.h> #include <errno.h> #include “http_parser.h” #include “http_response.h” #include “utils.h” #include “debug.h” #define PORT 80 /* 服务器监听端口 / #define BACKLOG 128 / 等待连接队伍的最大长度 / #define MAX_EVENTS 1024 / epoll最大事件数 / #define BUFFER_SIZE 8192 / 接收http请求的缓冲区大小(8KB) */ // 客户端连接信息结构体 typedef struct { int fd; // 客户端文件描述符 char buffer[BUFFER_SIZE]; // 接收缓冲区 ssize_t bytes_received; // 已接收字节数 char client_ip[INET_ADDRSTRLEN]; // 客户端IP地址 } client_info_t; // 处理客户端请求 void process_client(int client_fd, client_info_t *client) { LOG_DEBUG(“Processing client request (fd: %d)”, client_fd); http_request_t req; // 解析HTTP请求 if (parse_http_request(client->buffer, &req) != 0) { // 解析失败记录错误日志 LOG_ERROR("Failed to parse HTTP request"); log_request(client->client_ip, "UNKNOWN", "INVALID", 400); send_error_response(client_fd, 400, "Bad Request"); return; } // 记录成功请求日志 LOG_INFO("Request: %s %s from %s", req.method, req.path, client->client_ip); // 解析并记录POST请求体 if (strcmp(req.method, "POST") == 0) { LOG_DEBUG("Processing POST request"); if (parse_request_body(client_fd, &req, client->buffer, client->bytes_received) == 0) { log_post_body(&req, client->client_ip); } else { LOG_WARN("Failed to parse POST body"); } } // 处理GET/POST请求 if (strcmp(req.method, "GET") == 0 || strcmp(req.method, "POST") == 0) { handle_http_request(client_fd, &req); } else { // 不支持的HTTP方法 LOG_WARN("Unsupported method: %s", req.method); send_error_response(client_fd, 501, "Not Implemented"); } } int main() { // 初始化调试系统 log_init(“./log/”); debug_init(DEBUG_LEVEL_INFO); int server_fd; int epoll_fd; struct sockaddr_in server_addr; struct epoll_event ev, events[MAX_EVENTS]; // epoll事件结构 LOG_INFO("Starting epoll server on port %d", PORT); // 创建套接字 CHECK((server_fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0, exit(EXIT_FAILURE), "socket failed: %s", strerror(errno)); LOG_DEBUG("Socket created: fd=%d", server_fd); // 设置端口复用 int opt = 1; CHECK(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "setsockopt failed: %s", strerror(errno)); // 绑定地址 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); CHECK(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "bind failed: %s", strerror(errno)); // 开始监听 CHECK(listen(server_fd, BACKLOG) == 0, { close(server_fd); exit(EXIT_FAILURE); }, "listen failed: %s", strerror(errno)); // 创建epoll实例 CHECK((epoll_fd = epoll_create1(0)) >= 0, { close(server_fd); exit(EXIT_FAILURE); }, "epoll_create1 failed: %s", strerror(errno)); LOG_DEBUG("Epoll instance created: fd=%d", epoll_fd); // 添加服务器套接字到epoll监听 ev.events = EPOLLIN; // 监听可读事件 ev.data.fd = server_fd; // 关联服务器文件描述符 CHECK(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) == 0, { close(server_fd); close(epoll_fd); exit(EXIT_FAILURE); }, "epoll_ctl server_fd failed: %s", strerror(errno)); LOG_INFO("Web server running on port %d", PORT); while (1) { // 等待事件发生,-1表示无限等待 int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds < 0) { if (errno == EINTR) continue; // 被信号中断 LOG_ERROR("epoll_wait failed: %s", strerror(errno)); continue; } LOG_DEBUG("Epoll events triggered: %d", nfds); // 处理所有就绪事件 for (int i = 0; i < nfds; i++) { // 处理新客户端连接 if (events[i].data.fd == server_fd) { struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); // 接受新连接 int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { LOG_ERROR("accept failed: %s", strerror(errno)); continue; } LOG_DEBUG("New connection accepted: fd=%d", client_fd); // 设置非阻塞模式(边缘触发必须) int flags = fcntl(client_fd, F_GETFL, 0); if (fcntl(client_fd, F_SETFL, flags | O_NONBLOCK) < 0) { LOG_ERROR("fcntl nonblock failed: %s", strerror(errno)); close(client_fd); continue; } // 创建并初始化客户端信息结构 client_info_t *client = malloc(sizeof(client_info_t)); if (!client) { LOG_CRITICAL("malloc client_info failed: %s", strerror(errno)); close(client_fd); continue; } memset(client, 0, sizeof(client_info_t)); client->fd = client_fd; // 获取客户端IP inet_ntop(AF_INET, &client_addr.sin_addr, client->client_ip, INET_ADDRSTRLEN); LOG_DEBUG("Client IP: %s", client->client_ip); // 添加客户端到epoll监听(边缘触发模式) ev.events = EPOLLIN | EPOLLET; // 边缘触发模式 ev.data.ptr = client; // 关联客户端数据结构 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) < 0) { LOG_ERROR("epoll_ctl client_fd failed: %s", strerror(errno)); close(client_fd); free(client); } else { LOG_DEBUG("Added client to epoll: fd=%d", client_fd); } } // 处理客户端数据可读事件 else if (events[i].events & EPOLLIN) { client_info_t *client = (client_info_t *)events[i].data.ptr; LOG_DEBUG("Data available on fd=%d", client->fd); // 非阻塞读取数据 ssize_t count = recv(client->fd, client->buffer + client->bytes_received, BUFFER_SIZE - client->bytes_received - 1, 0); if (count > 0) { // 更新已接收字节数 client->bytes_received += count; client->buffer[client->bytes_received] = '\0'; // 确保字符串结束 LOG_DEBUG("Received %zd bytes (total %zd) from %s", count, client->bytes_received, client->client_ip); // 检查HTTP请求是否结束(通过空行判断) if (strstr(client->buffer, "\r\n\r\n") != NULL) { LOG_DEBUG("Full request received from %s", client->client_ip); // 移除epoll监听 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client->fd, NULL); // 处理完整请求 process_client(client->fd, client); // 关闭连接并释放资源 close(client->fd); free(client); } } // 处理连接关闭或错误 else if (count == 0 || (count < 0 && errno != EAGAIN)) { LOG_DEBUG("Connection closed by client: %s", client->client_ip); // 移除epoll监听 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client->fd, NULL); close(client->fd); free(client); } // EAGAIN错误在边缘触发模式下是正常的,表示数据已读完 } } } // 清理资源 LOG_INFO("Shutting down epoll server..."); close(epoll_fd); close(server_fd); LOG_INFO("Server shutdown complete"); return 0; }
最新发布
08-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值