#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#define PORT 80
#define MAX_CLIENTS 100
#define BUFFER_SIZE 8192
#define ROOT_DIR “”
int debug_mode = 1; // 调试模式开关
pid_t main_pid;
// 日志函数
void log_message(const char *type, const char *message, int level)
{
if (level > 0 && !debug_mode)
return;
time_t now = time(NULL); char *time_str = ctime(&now); time_str[strlen(time_str) - 1] = '\0'; char log_buffer[1024]; snprintf(log_buffer, sizeof(log_buffer), "[%s] %s: %s", time_str, type, message); printf("%s\n", log_buffer); // 写入日志文件(使用O_APPEND保证多进程写入安全) int log_fd = open("webserver.log", O_WRONLY | O_CREAT | O_APPEND, 0644); if (log_fd >= 0) { dprintf(log_fd, "%s\n", log_buffer); close(log_fd); }
}
// 获取绝对路径
void get_absolute_path(char *abs_path, const char *rel_path)
{
char cwd[1024];
if (getcwd(cwd, sizeof(cwd)))
{
snprintf(abs_path, 1024, “%s/%s/%s”, cwd, ROOT_DIR, rel_path);
}
else
{
snprintf(abs_path, 1024, “%s/%s”, ROOT_DIR, rel_path);
}
}
// 路径安全检查和规范化
int normalize_path(char *path)
{
// 移除多余的斜杠
char *src = path;
char *dst = path;
while (*src) { if (*src == '/' && *(src + 1) == '/') { src++; continue; } *dst++ = *src++; } *dst = '\0'; // 检查路径遍历攻击 if (strstr(path, "..") != NULL) { return 0; } return 1;
}
// 获取文件类型
const char *get_content_type(const char *path)
{
const char *ext = strrchr(path, ‘.’);
if (ext)
{
if (strcmp(ext, “.html”) == 0)
return “text/html”;
if (strcmp(ext, “.htm”) == 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”;
if (strcmp(ext, “.svg”) == 0)
return “image/svg+xml”;
if (strcmp(ext, “.txt”) == 0)
return “text/plain”;
if (strcmp(ext, “.pdf”) == 0)
return “application/pdf”;
}
return “application/octet-stream”;
}
// 发送HTTP响应
void send_response(int client_fd, int status, const char *status_text,
const char *content_type, const char *body, size_t body_len)
{
char header[512];
int header_len = snprintf(header, sizeof(header),
“HTTP/1.1 %d %s\r\n”
“Content-Type: %s\r\n”
“Content-Length: %zu\r\n”
“Connection: close\r\n\r\n”,
status, status_text, content_type, body_len);
write(client_fd, header, header_len); if (body && body_len > 0) { write(client_fd, body, body_len); }
}
// 发送文件
void send_file(int client_fd, const char *path)
{
char abs_path[1024];
get_absolute_path(abs_path, path);
if (!normalize_path(abs_path)) { const char *forbidden = "403 Forbidden"; send_response(client_fd, 403, "Forbidden", "text/plain", forbidden, strlen(forbidden)); log_message("SECURITY", "Path traversal attempt detected", 0); return; } char debug_msg[1024]; snprintf(debug_msg, sizeof(debug_msg), "Accessing file: %s", abs_path); log_message("DEBUG", debug_msg, 1); FILE *file = fopen(abs_path, "rb"); if (!file) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to open %s: %s", abs_path, strerror(errno)); log_message("ERROR", error_msg, 0); const char *not_found = "404 Not Found"; send_response(client_fd, 404, "Not Found", "text/plain", not_found, strlen(not_found)); return; } fseek(file, 0, SEEK_END); long file_size = ftell(file); fseek(file, 0, SEEK_SET); if (file_size <= 0) { fclose(file); const char *server_error = "500 Internal Server Error (Empty file)"; send_response(client_fd, 500, "Internal Server Error", "text/plain", server_error, strlen(server_error)); log_message("ERROR", "File is empty", 0); return; } char *buffer = malloc(file_size); if (!buffer) { fclose(file); const char *server_error = "500 Internal Server Error"; send_response(client_fd, 500, "Internal Server Error", "text/plain", server_error, strlen(server_error)); log_message("ERROR", "Memory allocation failed", 0); return; } size_t read_size = fread(buffer, 1, file_size, file); fclose(file); if (read_size != (size_t) file_size) { free(buffer); const char *server_error = "500 Internal Server Error"; send_response(client_fd, 500, "Internal Server Error", "text/plain", server_error, strlen(server_error)); log_message("ERROR", "File read error", 0); return; } const char *content_type = get_content_type(path); send_response(client_fd, 200, "OK", content_type, buffer, file_size); free(buffer);
}
// 处理目录请求
void handle_directory(int client_fd, const char *path)
{
char index_path[1024];
snprintf(index_path, sizeof(index_path), “%s/Contact.html”, path);
struct stat st; if (stat(index_path, &st) == 0 && S_ISREG(st.st_mode)) { send_file(client_fd, index_path); } else { const char *not_found = "404 Directory index not found"; send_response(client_fd, 404, "Not Found", "text/plain", not_found, strlen(not_found)); log_message("ERROR", "Directory index not found", 0); }
}
// 处理客户端请求(现在在子进程中执行)
void handle_client(int client_fd)
{
char buffer[BUFFER_SIZE];
ssize_t bytes_read = read(client_fd, buffer, sizeof(buffer) - 1); if (bytes_read <= 0) { close(client_fd); return; } buffer[bytes_read] = '\0'; if (debug_mode) { char req_msg[BUFFER_SIZE + 50]; snprintf(req_msg, sizeof(req_msg), "Request:\n%.*s", (int) bytes_read, buffer); log_message("REQUEST", req_msg, 1); } char method[16], path[256], protocol[16]; if (sscanf(buffer, "%15s %255s %15s", method, path, protocol) != 3) { const char *bad_request = "400 Bad Request"; send_response(client_fd, 400, "Bad Request", "text/plain", bad_request, strlen(bad_request)); close(client_fd); return; } // 完全忽略 favicon.ico 请求 if (strcmp(path, "/favicon.ico") == 0) { close(client_fd); return; // 直接返回,不发送任何响应 } // 处理路径 char full_path[1024]; if (strcmp(path, "/") == 0) { // 默认显示 Contact.html strcpy(full_path, "Contact.html"); } else { // 跳过开头的斜杠 strcpy(full_path, path + 1); } // 获取绝对路径用于文件检查 char abs_path[1024]; get_absolute_path(abs_path, full_path); // 调试信息 char debug_msg[1024]; snprintf(debug_msg, sizeof(debug_msg), "Resolved path: %s", abs_path); log_message("DEBUG", debug_msg, 1); // 处理GET请求 if (strcmp(method, "GET") == 0) { struct stat st; if (stat(abs_path, &st) == 0) { if (S_ISDIR(st.st_mode)) { // 如果是目录,尝试查找目录下的 Contact.html handle_directory(client_fd, abs_path); } else { // 发送文件 send_file(client_fd, full_path); } } else { // 文件不存在 char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "File not found: %s", abs_path); log_message("ERROR", error_msg, 0); const char *not_found = "404 Not Found"; send_response(client_fd, 404, "Not Found", "text/plain", not_found, strlen(not_found)); } } // 处理POST请求 else if (strcmp(method, "POST") == 0) { // 查找请求体 char *body = strstr(buffer, "\r\n\r\n"); if (body) { body += 4; // 跳过空行 // 记录POST数据 char log_msg[1024]; snprintf(log_msg, sizeof(log_msg), "POST data: %.*s", (int) (bytes_read - (body - buffer)), body); log_message("INFO", log_msg, 1); // 简单的POST处理示例 const char *response = "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n" "Content-Length: 12\r\n" "Connection: close\r\n\r\n" "POST Received"; write(client_fd, response, strlen(response)); } else { const char *bad_request = "400 Bad Request (Missing body)"; send_response(client_fd, 400, "Bad Request", "text/plain", bad_request, strlen(bad_request)); } } // 处理其他HTTP方法 else { const char *not_allowed = "405 Method Not Allowed"; send_response(client_fd, 405, "Method Not Allowed", "text/plain", not_allowed, strlen(not_allowed)); } close(client_fd);
}
// 僵尸进程回收函数
void reap_zombies(int sig)
{
while (waitpid(-1, NULL, WNOHANG) > 0);
}
// 清理函数
void cleanup()
{
log_message(“INFO”, “Server shutting down”, 0);
}
void handle_signal(int sig)
{
if (getpid() != main_pid)
{
_exit(0); // 子进程直接退出
}
else if (sig == SIGINT || sig == SIGTERM)
{
log_message(“INFO”, “Received termination signal, shutting down”, 0);
exit(0); // 主进程正常退出
}
}
int main()
{
main_pid = getpid(); // 第一行获取主进程PID
// 仅主进程注册清理函数 if (getpid() == main_pid) { atexit(cleanup); } // 设置信号处理 signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal); signal(SIGCHLD, reap_zombies); // 添加子进程回收信号 // 创建套接字 int server_fd; if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置套接字选项 int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { perror("setsockopt failed"); exit(EXIT_FAILURE); } // 绑定地址 struct sockaddr_in address; 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("bind failed"); exit(EXIT_FAILURE); } // 监听端口 if (listen(server_fd, MAX_CLIENTS) < 0) { perror("listen failed"); exit(EXIT_FAILURE); } // 检查根目录是否存在 char root_path[1024]; get_absolute_path(root_path, ""); struct stat st; if (stat(root_path, &st) != 0 || !S_ISDIR(st.st_mode)) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "Root directory '%s' not found or inaccessible", root_path); log_message("FATAL", error_msg, 0); exit(EXIT_FAILURE); } else { char info_msg[256]; snprintf(info_msg, sizeof(info_msg), "Serving files from: %s", root_path); log_message("INFO", info_msg, 0); } log_message("INFO", "Server started successfully (Multi-process)", 0); printf("Multi-process server running on port %d\n", PORT); // 主循环 while (1) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &addr_len); if (client_fd < 0) { if (errno == EINTR) continue; // 信号中断处理 perror("accept failed"); continue; } // 记录客户端信息 char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &client_addr . sin_addr, client_ip, INET_ADDRSTRLEN); char conn_msg[256]; snprintf(conn_msg, sizeof(conn_msg), "Client connected: %s:%d", client_ip, ntohs(client_addr . sin_port)); log_message("INFO", conn_msg, 1); // 创建子进程处理请求 pid_t pid = fork(); if (pid == 0) { // 子进程 close(server_fd); // 关闭监听套接字 handle_client(client_fd); _exit(EXIT_SUCCESS); // 处理完成后退出 } else if (pid > 0) { // 父进程 close(client_fd); // 关闭父进程中不需要的客户端套接字 } else { perror("fork failed"); close(client_fd); } } close(server_fd); return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#include <arpa/inet.h>
#define PORT 80
#define MAX_EVENTS 1024
#define BUFFER_SIZE 8192
#define ROOT_DIR “”
int debug_mode = 1;
// 连接状态结构体
typedef struct
{
int fd;
char read_buf[BUFFER_SIZE];
size_t read_len;
char write_buf[BUFFER_SIZE];
size_t write_len;
size_t write_sent;
int keep_alive;
struct sockaddr_in client_addr;
} connection_t;
// 日志函数
void log_message(const char *type, const char *message, int level)
{
if (level > 0 && !debug_mode)
return;
time_t now = time(NULL); char *time_str = ctime(&now); time_str[strlen(time_str) - 1] = '\0'; char log_buffer[1024]; snprintf(log_buffer, sizeof(log_buffer), "[%s] %s: %s", time_str, type, message); printf("%s\n", log_buffer); // 写入日志文件(使用O_APPEND保证多进程写入安全) int log_fd = open("webserver.log", O_WRONLY | O_CREAT | O_APPEND, 0644); if (log_fd >= 0) { dprintf(log_fd, "%s\n", log_buffer); close(log_fd); }
}
// 获取绝对路径
void get_absolute_path(char *abs_path, const char *rel_path)
{
char cwd[1024];
if (getcwd(cwd, sizeof(cwd)))
{
snprintf(abs_path, 1024, “%s/%s/%s”, cwd, ROOT_DIR, rel_path);
}
else
{
snprintf(abs_path, 1024, “%s/%s”, ROOT_DIR, rel_path);
}
}
// 路径安全检查和规范化
int normalize_path(char *path)
{
// 移除多余的斜杠
char *src = path;
char *dst = path;
while (*src) { if (*src == '/' && *(src + 1) == '/') { src++; continue; } *dst++ = *src++; } *dst = '\0'; // 检查路径遍历攻击 if (strstr(path, "..") != NULL) { return 0; } return 1;
}
// 获取文件类型
const char *get_content_type(const char *path)
{
const char *ext = strrchr(path, ‘.’);
if (ext)
{
if (strcmp(ext, “.html”) == 0)
return “text/html”;
if (strcmp(ext, “.htm”) == 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”;
if (strcmp(ext, “.svg”) == 0)
return “image/svg+xml”;
if (strcmp(ext, “.txt”) == 0)
return “text/plain”;
if (strcmp(ext, “.pdf”) == 0)
return “application/pdf”;
}
return “application/octet-stream”;
}
// 设置非阻塞IO
void set_nonblocking(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1)
{
perror(“fcntl F_GETFL”);
exit(EXIT_FAILURE);
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
{
perror(“fcntl F_SETFL”);
exit(EXIT_FAILURE);
}
}
// 创建HTTP响应头
void build_http_header(char *header, int status, const char *status_text,
const char *content_type, size_t content_len, int keep_alive)
{
snprintf(header, BUFFER_SIZE,
“HTTP/1.1 %d %s\r\n”
“Content-Type: %s\r\n”
“Content-Length: %zu\r\n”
“Connection: %s\r\n”
“Server: epoll-server\r\n\r\n”,
status, status_text, content_type, content_len,
keep_alive ? “keep-alive” : “close”);
}
// 发送文件响应
void send_file_response(connection_t *conn, const char *path)
{
char abs_path[1024];
get_absolute_path(abs_path, path);
if (!normalize_path(abs_path)) { const char *forbidden = "403 Forbidden"; build_http_header(conn -> write_buf, 403, "Forbidden", "text/plain", strlen(forbidden), conn -> keep_alive); memcpy(conn -> write_buf + strlen(conn -> write_buf), forbidden, strlen(forbidden)); conn -> write_len = strlen(conn -> write_buf); return; } int fd = open(abs_path, O_RDONLY); if (fd == -1) { const char *not_found = "404 Not Found"; build_http_header(conn -> write_buf, 404, "Not Found", "text/plain", strlen(not_found), conn -> keep_alive); memcpy(conn -> write_buf + strlen(conn -> write_buf), not_found, strlen(not_found)); conn -> write_len = strlen(conn -> write_buf); return; } struct stat st; fstat(fd, &st); off_t file_size = st . st_size; const char *content_type = get_content_type(path); build_http_header(conn -> write_buf, 200, "OK", content_type, file_size, conn -> keep_alive); size_t header_len = strlen(conn -> write_buf); // 使用sendfile零拷贝发送文件 conn -> write_len = header_len; conn -> write_sent = 0; // 如果文件很小,直接读取到内存 if (file_size < BUFFER_SIZE - header_len) { ssize_t n = read(fd, conn -> write_buf + header_len, file_size); if (n > 0) { conn -> write_len += n; } close(fd); } else { // 大文件使用sendfile在发送时处理 close(fd); // 实际发送在事件循环中处理 }
}
// 处理HTTP请求
void handle_request(connection_t *conn)
{
// 解析请求行
char method[16], path[256], protocol[16];
if (sscanf(conn -> read_buf, “%15s %255s %15s”, method, path, protocol) != 3)
{
const char *bad_request = “400 Bad Request”;
build_http_header(conn -> write_buf, 400, “Bad Request”, “text/plain”, strlen(bad_request), 0);
memcpy(conn -> write_buf + strlen(conn -> write_buf), bad_request, strlen(bad_request));
conn -> write_len = strlen(conn -> write_buf);
return;
}
// 检查Keep-Alive conn -> keep_alive = (strstr(conn -> read_buf, "Connection: keep-alive") != NULL); // 忽略favicon if (strcmp(path, "/favicon.ico") == 0) { conn -> write_len = 0; return; } // 处理路径 char full_path[1024]; if (strcmp(path, "/") == 0) { strcpy(full_path, "Contact.html"); } else { strcpy(full_path, path + 1); } // 处理GET请求 if (strcmp(method, "GET") == 0) { send_file_response(conn, full_path); } // 处理POST请求 else if (strcmp(method, "POST") == 0) { const char *response = "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n" "Content-Length: 12\r\n" "Connection: close\r\n\r\n" "POST Received"; strncpy(conn -> write_buf, response, strlen(response)); conn -> write_len = strlen(response); conn -> keep_alive = 0; // POST后关闭连接 } // 其他方法 else { const char *not_allowed = "405 Method Not Allowed"; build_http_header(conn -> write_buf, 405, "Method Not Allowed", "text/plain", strlen(not_allowed), 0); memcpy(conn -> write_buf + strlen(conn -> write_buf), not_allowed, strlen(not_allowed)); conn -> write_len = strlen(conn -> write_buf); }
}
// 初始化服务器套接字
int init_server_socket()
{
int server_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (server_fd == -1)
{
perror(“socket”);
exit(EXIT_FAILURE);
}
int opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in address = { .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = htons(PORT) }; if (bind(server_fd, (struct sockaddr *) &address, sizeof(address)) < 0) { perror("bind"); close(server_fd); exit(EXIT_FAILURE); } if (listen(server_fd, SOMAXCONN) < 0) { perror("listen"); close(server_fd); exit(EXIT_FAILURE); } return server_fd;
}
int main()
{
int server_fd = init_server_socket();
log_message(“INFO”, “IO Multiplexing server started (epoll)”, 0);
// 创建epoll实例 int epoll_fd = epoll_create1(0); if (epoll_fd == -1) { perror("epoll_create1"); close(server_fd); exit(EXIT_FAILURE); } // 添加服务器套接字到epoll struct epoll_event ev; ev . events = EPOLLIN; ev . data . fd = server_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) == -1) { perror("epoll_ctl: server_fd"); close(server_fd); close(epoll_fd); exit(EXIT_FAILURE); } // 事件循环 struct epoll_event events[MAX_EVENTS]; connection_t *connections[MAX_EVENTS] = {0}; while (1) { int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_wait"); break; } for (int i = 0; i < nfds; i++) { // 新连接 if (events[i] . data . fd == server_fd) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &addr_len); if (client_fd == -1) { perror("accept"); continue; } set_nonblocking(client_fd); // 创建连接对象 connection_t *conn = malloc(sizeof(connection_t)); if (!conn) { close(client_fd); continue; } *conn = (connection_t){ .fd = client_fd, .read_len = 0, .write_len = 0, .write_sent = 0, .keep_alive = 0, .client_addr = client_addr }; // 添加到epoll监控 ev . events = EPOLLIN | EPOLLET; // 边缘触发模式 ev . data . ptr = conn; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) == -1) { perror("epoll_ctl: client_fd"); free(conn); close(client_fd); continue; } connections[client_fd] = conn; char ip_str[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &client_addr . sin_addr, ip_str, INET_ADDRSTRLEN); char log_msg[128]; snprintf(log_msg, sizeof(log_msg), "Client connected: %s:%d", ip_str, ntohs(client_addr . sin_port)); log_message("INFO", log_msg, 1); } // 客户端事件 else { connection_t *conn = events[i] . data . ptr; // 可读事件 if (events[i] . events & EPOLLIN) { ssize_t count = read(conn -> fd, conn -> read_buf + conn -> read_len, BUFFER_SIZE - conn -> read_len - 1); if (count == -1) { // 错误或EAGAIN if (errno != EAGAIN && errno != EWOULDBLOCK) { goto close_conn; } } else if (count == 0) { // 客户端关闭连接 goto close_conn; } else { conn -> read_len += count; conn -> read_buf[conn -> read_len] = '\0'; // 检查是否收到完整请求 if (strstr(conn -> read_buf, "\r\n\r\n")) { if (debug_mode) { log_message("REQUEST", conn -> read_buf, 1); } handle_request(conn); // 修改为监听写事件 ev . events = EPOLLOUT | EPOLLET; ev . data . ptr = conn; epoll_ctl(epoll_fd, EPOLL_CTL_MOD, conn -> fd, &ev); } } } // 可写事件 if (events[i] . events & EPOLLOUT) { if (conn -> write_len > 0) { ssize_t count = write(conn -> fd, conn -> write_buf + conn -> write_sent, conn -> write_len - conn -> write_sent); if (count == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { goto close_conn; } } else { conn -> write_sent += count; // 全部发送完成 if (conn -> write_sent >= conn -> write_len) { if (conn -> keep_alive) { // 保持连接,重置状态 conn -> read_len = 0; conn -> write_len = 0; conn -> write_sent = 0; // 修改为监听读事件 ev . events = EPOLLIN | EPOLLET; ev . data . ptr = conn; epoll_ctl(epoll_fd, EPOLL_CTL_MOD, conn -> fd, &ev); } else { goto close_conn; } } } } } // 错误事件 if (events[i] . events & (EPOLLERR | EPOLLHUP)) { goto close_conn; } continue; close_conn: // 关闭连接并清理 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, conn -> fd, NULL); close(conn -> fd); free(conn); connections[conn -> fd] = NULL; } } } close(server_fd); close(epoll_fd); return 0;
}
前一个代码是可正常运行的,后一个不行,请你基于此信息再分析原因
最新发布