#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;
}
前一个代码是可正常运行的,后一个不行,请你基于此信息再分析原因