epoll+消息队列-通过使用pthread_cond_signal的一个简单实现

epoll与消息队列
本文介绍了一种使用epoll实现服务器的方式,并结合消息队列处理业务逻辑。epoll负责接收IO请求,将请求放入队列后立即返回,由其他线程处理队列中的请求。文章提供了具体的代码实现及解释。
第一次用epoll去实现一个服务器,
之前并不清楚epoll的用法,
了解之后才发现epoll服务器的主线程其实最好和处理业务的代码分开,
也就是说:
epoll响应外界的io请求,当epoll得到一个请求的时候,扔到一个消息队列中,然后epoll直接返回,再去等待io请求.而消息队列会通知多个线程去处理这些业务逻辑.

epoll第一次用,消息队列更是第一次用,开始一直在想,怎么写个阻塞的队列,而且要有主动通知的功能,想了一会儿发现pthread_cond_wait和pthread_cond_signal可以实现,于是就简单的试试,下面的代码已经可以实现我刚才想要得到的那个模型,细节就不管了.

对消息队列熟悉的同学请帮忙提点意见,并告诉我下还有哪些方法可以实现阻塞的消息队列.

[color=orange]对代码的解释和描述都写到注释中了.[/color]

/*
几个用到的类型定义以及全局变量bq
*/
char smtp_cmd_format;
struct epoll_event ev, events[MAX_EPOLL_SIZE];
int kdpfd,nfds;
struct block_queue
{
int queue[THREADS_COUNT];
long size;
pthread_cond_t cond;
pthread_mutex_t mutex;
}block_queue_t;
block_queue_t bq;
struct block_queue_param
{
void* func;
void* queue;
}block_queue_param_t;



void *block_queue(void * param)
{
void(* func)(void* );
int fd,i;
/*
由于block_queue是pthread_create的回调方法,
所以block_queue的参数必须是void*类型
*/
block_queue_t* bque = (block_queue_param_t*)param->queue;
/*
param->func是block_queue解锁时需要调用的函数,
而这个函数的参数是一个int fd,
该fd是消息队列中刚刚插入的一个元素.
*/
func = (block_queue_param_t*)param->func;

for(;;)
{
/*
lock->wait->unlock
这是经典的模式,
切记:
pthread_cond_wait的方法自带了先解锁,再等待,最后再加锁的代码
*/
pthread_mutex_lock(bque->mutex);
/*
线程在pthread_cond_wait这里被block住了,
当听到pthread_cond_signal通知的时候,
内核会从阻塞队列里面通过先进先出的原则唤醒一个线程,
这个线程会执行pthread_cond_wait之后的代码.
*/
pthread_cond_wait(bque->cond,bque->mutex);

if(bque->size==0)
{
/*
啥也不做
*/
}else
{
fd = bque->queue[0];
/*
移动队列,
由于该队列是简单的用数组保存fd,
所以移动这个操作必不可少,但肯定性能比链表差很多,
这是懒惰的代价
*/
for(i = 0; i < bque->size; ++i)
bque->queue[i] = bque->queue[i+1];
bque->queue[bque->size-1] = 0;
bque->size--;
/*
执行被唤醒后的方法,参数是刚刚插入到队列中的一个fd
*/
func(fd);
}

pthread_mutex_unlock(bque->mutex);
}
}

void insert_queue(struct block_queue *bque,int fd)
{
/*
加锁->通知->解锁

将元素插入队列之前需要先加锁
*/
pthread_mutex_lock(bque->mutex);
/*
检查队列目前的大小,
检查1:
当大小已经达到定义的数组大小THREADS_COUNT时,
抛弃该fd,说明服务器忙不过来了,消息队列已经满了

检查2:
当大小超过数组定义的大小THREADS_COUNT时,
肯定发生了异常,那就直接退出服务吧.
*/
if(bque->size == THREADS_COUNT)
return;
/*
bque->size其实也是队列末尾位置指针,
当插入一个元素后,这个指针自然也要向后移动一位.
*/
bque->queue[bque->size+1] = fd;
if(++bque->size > THREADS_COUNT)
{
fprintf(stderr,"Queue size over folow.%d",bque->size);
exit 1;
}
/*
当元素插入bque队列时,
该通过pthread_cond_signal通知内核去调度wait的线程了
*/
pthread_cond_signal(bque->cond);
pthread_mutex_unlock(bque->mutex);

}

/*
init_threads代码是初始化线程组的,
随便写写的,大家知道怎么实现就行
*/
int init_threads()
{
size_t i=0;
block_queue_param_t bqp;
/*
smtp_echo是处理epoll扔进队列中的fd的函数,
该方法实现了整个模型的业务逻辑,
整体代码的IO处理+消息队列以及业务处理分的很清晰,
三个模块每个只有一处代码和其它模块通讯,没有多少耦合.
*/
bqp.func = (void*)smtp_echo;
bqp.queue = (void*)bq;
pthread_cond_init(bqp.cond,NULL);
pthread_mutex_init(bqp.mutex,NULL);
for( i = 0; i < THREADS_COUNT; ++i)
{
pthread_t child_thread;
pthread_attr_t child_thread_attr;
pthread_attr_init(&child_thread_attr);
pthread_attr_setdetachstate(&child_thread_attr,PTHREAD_CREATE_DETACHED);
if( pthread_create(&child_thread,&child_thread_attr,block_queue, (void *)bqp) < 0 )
{
printf("pthread_create Failed : %s\n",strerror(errno));
return 1;
}
else
{
printf("pthread_create Success : %d\n",(int)child_thread);
return 0;
}
}

}

/*
handler是主线程访问的方法,
主线程通过handler把一个fd扔到消息队列之后,
不再做任何事情就直接返回了.

在我的应用中,主线程是个epoll实现的服务器,
由于epoll被响应的时候会知道哪些fd已经就位,
于是直接把就位的fd扔到消息队列中就好了,
主线程在继续等待其它fd的响应,而不需要去关心fd如何被处理.
*/

int handler(void* fd)
{
printf("handler:fd => %d\n",*(int *)(fd));
insert_queue(&bq,fd);
return 0;
}


/*
main函数是整个程序的入口点,
也是epoll服务器的实现,
epoll的思想很精髓,用法很简单,
只要把man 4 epoll_ctl的例子copy出来,就可用了,
不过那个例子语法有点问题,
而且events数组是用指针,应该用[]实现,因为指针没有分配空间.
*/
int main(int argc, char **argv)
{
int server_socket = init_smtp();
int n;

if(init_threads() == 0)
printf("Success full init_threads.");

smtp_cmd_format = "^([a-zA-Z0-9]) (.*)$";
kdpfd = epoll_create(MAX_EPOLL_SIZE);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = server_socket;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, server_socket, &ev) < 0) {
fprintf(stderr, "epoll set insertion error: fd=%d < 0",
server_socket);
return -1;
}

/*
epoll的使用看这里
*/
for(;;) {
struct sockaddr_in local;
socklen_t length = sizeof(local);
int client;

nfds = epoll_wait(kdpfd, events, MAX_EPOLL_SIZE, -1);

/*
当没有事件要处理时,epoll会阻塞住,
否则,内核会填充events数组,里面的每一个events[n].data.fd就是发生io时间的文件句柄
*/

for(n = 0; n < nfds; ++n) {
/*
这里要判断一下请求的来源,
if(events[n].data.fd == server_socket) {
这里是新建的连接,
因为io发生在server_socket上
}
else{
这里是已有的连接,
因为fd!= server_socket
那fd肯定是之前从server_socket接收到,
并且通过epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev)
加入到kdpfd所指向的内存空间中.
kdpfd其实是个文件句柄,在epoll_create(MAX_EPOLL_SIZE)时得到
}
*/
if(events[n].data.fd == server_socket) {
client = accept(server_socket, (struct sockaddr *) &local,&length);
if(client < 0){
perror("accept");
continue;
}
setnonblocking(client);
smtp_cmd("220",client);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
fprintf(stderr, "epoll set insertion error: fd=%d < 0",
client);
return -1;
}
}
else
/*
当已有的fd发生io操作时,
执行如下代码.也就是把fd扔到消息队列中.
*/
if(handler((void *)&events[n].data.fd) != 0)
perror("handler ret != 0");
}
}

close(server_socket);
return 0;
}
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/epoll.h> #include <pthread.h> #include <fcntl.h> #define MAX_EVENTS 1024 #define THREAD_POOL_SIZE 4 #define BUFFER_SIZE 4096 #define PORT 8080 // 任务队列结构 typedef struct { void (*task_function)(int); int client_fd; } Task; // 线程池结构 typedef struct { pthread_t *threads; Task *task_queue; int queue_size; int queue_capacity; int head; int tail; int count; pthread_mutex_t lock; pthread_cond_t task_cond; pthread_cond_t space_cond; } ThreadPool; // 全局变量 ThreadPool thread_pool; int epoll_fd; // 设置非阻塞套接字 void set_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); } // 初始化线程池 void thread_pool_init(ThreadPool *pool, int size, int capacity) { pool->threads = malloc(size * sizeof(pthread_t)); pool->task_queue = malloc(capacity * sizeof(Task)); pool->queue_size = 0; pool->queue_capacity = capacity; pool->head = 0; pool->tail = 0; pool->count = 0; pthread_mutex_init(&pool->lock, NULL); pthread_cond_init(&pool->task_cond, NULL); pthread_cond_init(&pool->space_cond, NULL); for (int i = 0; i < size; i++) { pthread_create(&pool->threads[i], NULL, (void *)worker_thread, pool); } } // 工作线程函数 void *worker_thread(void *arg) { ThreadPool *pool = (ThreadPool *)arg; while (1) { pthread_mutex_lock(&pool->lock); // 等待任务 while (pool->queue_size == 0) { pthread_cond_wait(&pool->task_cond, &pool->lock); } // 获取任务 Task task = pool->task_queue[pool->head]; pool->head = (pool->head + 1) % pool->queue_capacity; pool->queue_size--; pthread_cond_signal(&pool->space_cond); pthread_mutex_unlock(&pool->lock); // 执行任务 task.task_function(task.client_fd); // 处理完成后重新注册到epoll struct epoll_event event; event.events = EPOLLIN | EPOLLET; event.data.fd = task.client_fd; epoll_ctl(epoll_fd, EPOLL_CTL_MOD, task.client_fd, &event); } return NULL; } // 添加任务到线程池 void add_task(ThreadPool *pool, void (*task_function)(int), int client_fd) { pthread_mutex_lock(&pool->lock); // 等待队列空间 while (pool->queue_size == pool->queue_capacity) { pthread_cond_wait(&pool->space_cond, &pool->lock); } // 添加任务 pool->task_queue[pool->tail].task_function = task_function; pool->task_queue[pool->tail].client_fd = client_fd; pool->tail = (pool->tail + 1) % pool->queue_capacity; pool->queue_size++; pthread_cond_signal(&pool->task_cond); pthread_mutex_unlock(&pool->lock); } // 处理HTTP请求的函数 void handle_request(int client_fd) { char buffer[BUFFER_SIZE]; ssize_t bytes_read = recv(client_fd, buffer, BUFFER_SIZE - 1, 0); if (bytes_read > 0) { buffer[bytes_read] = '\0'; // 简单的HTTP响应 const char *response = "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n" "Content-Length: 31\r\n\r\n" "Hello from C epoll + thread pool!"; send(client_fd, response, strlen(response), 0); } // 注意:实际应用中需要处理Keep-Alive close(client_fd); } int main() { // 创建服务器套接字 int server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd == -1) { perror("socket"); exit(EXIT_FAILURE); } // 设置SO_REUSEADDR int opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // 绑定地址 struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(PORT); if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { perror("bind"); exit(EXIT_FAILURE); } // 监听 if (listen(server_fd, SOMAXCONN) == -1) { perror("listen"); exit(EXIT_FAILURE); } // 创建epoll实例 epoll_fd = epoll_create1(0); if (epoll_fd == -1) { perror("epoll_create1"); exit(EXIT_FAILURE); } // 添加服务器套接字到epoll struct epoll_event event; event.events = EPOLLIN | EPOLLET; event.data.fd = server_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) { perror("epoll_ctl: server_fd"); exit(EXIT_FAILURE); } // 初始化线程池 thread_pool_init(&thread_pool, THREAD_POOL_SIZE, 256); // 事件循环 struct epoll_event events[MAX_EVENTS]; printf("Server running on port %d...\n", PORT); while (1) { int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_wait"); exit(EXIT_FAILURE); } 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 == -1) { perror("accept"); continue; } // 设置非阻塞 set_nonblocking(client_fd); // 添加到epoll event.events = EPOLLIN | EPOLLET; event.data.fd = client_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) == -1) { perror("epoll_ctl: client_fd"); close(client_fd); } } // 客户端数据 else if (events[i].events & EPOLLIN) { int client_fd = events[i].data.fd; // 从epoll中暂时移除 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, NULL); // 添加到线程池 add_task(&thread_pool, handle_request, client_fd); } } } close(server_fd); return 0; } 网页加载非常缓慢
08-05
#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/epoll.h> #include <fcntl.h> #include <pthread.h> #include <dirent.h> #include <sys/stat.h> #include <time.h> #include <ctype.h> #include <errno.h> #define PORT 8080 #define MAX_EVENTS 1024 #define THREAD_POOL_SIZE 8 #define BUFFER_SIZE 4096 #define WEB_ROOT "web" // 当前目录作为根目录 // 线程池结构 typedef struct { pthread_t *threads; int thread_count; pthread_mutex_t lock; pthread_cond_t cond; int shutdown; } ThreadPool; // 任务结构 typedef struct { int client_fd; struct sockaddr_in client_addr; } Task; // 全局任务队列 Task *task_queue; int task_count = 0; int task_capacity = 0; ThreadPool thread_pool; // HTTP响应头 const char *http_header = "HTTP/1.1 200 OK\r\n" "Server: SimpleWebServer\r\n" "Connection: close\r\n" "Content-Type: %s\r\n" "Content-Length: %ld\r\n\r\n"; // 十六进制字符转换 int hex_char_to_int(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return 0; } // URL解码 void url_decode(char *dst, const char *src) { char a, b; while (*src) { if (*src == '%' && (a = src[1]) && (b = src[2]) && isxdigit(a) && isxdigit(b)) { *dst++ = (hex_char_to_int(a) << 4) | hex_char_to_int(b); src += 3; } else if (*src == '+') { *dst++ = ' '; src++; } else { *dst++ = *src++; } } *dst = '\0'; } // 文件扩展名到MIME类型映射 const char *extension_to_type(const char *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, "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, "json") == 0) return "application/json"; if (strcmp(ext, "txt") == 0) return "text/plain"; if (strcmp(ext, "svg") == 0) return "image/svg+xml"; return "application/octet-stream"; } // 获取文件扩展名 const char *get_file_extension(const char *filename) { const char *dot = strrchr(filename, '.'); if (!dot || dot == filename) return ""; return dot + 1; } // 发送404错误响应 void send_404(int client_fd) { const char *response = "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/html; charset=UTF-8\r\n" "Connection: close\r\n" "Content-Length: 86\r\n\r\n" "<html><head><title>404 Not Found</title></head><body><h1>404 Not Found</h1></body></html>"; send(client_fd, response, strlen(response), 0); } // 发送400错误响应 void send_400(int client_fd) { const char *response = "HTTP/1.1 400 Bad Request\r\n" "Content-Type: text/html; charset=UTF-8\r\n" "Connection: close\r\n" "Content-Length: 84\r\n\r\n" "<html><head><title>400 Bad Request</title></head><body><h1>400 Bad Request</h1></body></html>"; send(client_fd, response, strlen(response), 0); } // 发送500错误响应 void send_500(int client_fd) { const char *response = "HTTP/1.1 500 Internal Server Error\r\n" "Content-Type: text/html; charset=UTF-8\r\n" "Connection: close\r\n" "Content-Length: 92\r\n\r\n" "<html><head><title>500 Internal Error</title></head><body><h1>500 Internal Server Error</h1></body></html>"; send(client_fd, response, strlen(response), 0); } // 处理客户端请求 void handle_request(int client_fd, struct sockaddr_in client_addr) { char buffer[BUFFER_SIZE]; ssize_t bytes_read = recv(client_fd, buffer, BUFFER_SIZE - 1, 0); if (bytes_read <= 0) { close(client_fd); return; } buffer[bytes_read] = '\0'; // 解析HTTP请求 char method[16], path[256], protocol[16]; if (sscanf(buffer, "%s %s %s", method, path, protocol) != 3) { send_400(client_fd); close(client_fd); return; } // 日志输出 char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); printf("[%s] %s %s %s\n", client_ip, method, path, protocol); // URL解码 char decoded_path[256]; url_decode(decoded_path, path); printf("Decoded path: %s\n", decoded_path); // 只处理GET请求 if (strcmp(method, "GET") != 0 && strcmp(method, "POST") != 0) { send_400(client_fd); close(client_fd); return; } // 安全处理路径 char safe_path[512]; if (strcmp(decoded_path, "/") == 0) { // 尝试多个可能的默认文件 const char *default_files = {"Index.html"}; int found = 0; snprintf(safe_path, sizeof(safe_path), "%s/%s", WEB_ROOT, default_files); if (access(safe_path, F_OK) == 0) { found = 1; } if (!found) { strcpy(safe_path, WEB_ROOT "/Index.html"); } } else { // 处理路径中的斜杠问题 if (decoded_path[0] == '/') { snprintf(safe_path, sizeof(safe_path), "%s%s", WEB_ROOT, decoded_path); } else { snprintf(safe_path, sizeof(safe_path), "%s/%s", WEB_ROOT, decoded_path); } } printf("Final path: %s\n", safe_path); // 防止路径遍历攻击 if (strstr(safe_path, "..")) { printf("Path traversal attempt detected: %s\n", safe_path); send_404(client_fd); close(client_fd); return; } // 检查文件是否存在 struct stat file_stat; if (stat(safe_path, &file_stat) < 0) { perror("stat failed"); send_404(client_fd); close(client_fd); return; } // 检查是否是普通文件 if (!S_ISREG(file_stat.st_mode)) { printf("Not a regular file: %s\n", safe_path); send_404(client_fd); close(client_fd); return; } // 检查文件权限 if (access(safe_path, R_OK) != 0) { perror("access failed"); send_404(client_fd); close(client_fd); return; } // 打开文件 FILE *file = fopen(safe_path, "rb"); if (!file) { perror("fopen failed"); send_500(client_fd); close(client_fd); return; } // 获取MIME类型 const char *ext = get_file_extension(safe_path); const char *mime_type = extension_to_type(ext); // 构建HTTP响应头 char header[1024]; snprintf(header, sizeof(header), http_header, mime_type, file_stat.st_size); // 发送响应头 ssize_t sent = send(client_fd, header, strlen(header), 0); if (sent <= 0) { perror("send header failed"); fclose(file); close(client_fd); return; } // 发送文件内容 char file_buffer[BUFFER_SIZE]; size_t bytes; while ((bytes = fread(file_buffer, 1, sizeof(file_buffer), file)) > 0) { sent = send(client_fd, file_buffer, bytes, 0); if (sent <= 0) { perror("send file content failed"); break; } } fclose(file); close(client_fd); // 打印日志 printf("[%s] %s %s - Sent %ld bytes\n", client_ip, method, decoded_path, file_stat.st_size); } // 线程工作函数 void *worker_thread(void *arg) { while (1) { pthread_mutex_lock(&thread_pool.lock); // 等待任务 while (task_count == 0 && !thread_pool.shutdown) { pthread_cond_wait(&thread_pool.cond, &thread_pool.lock); } if (thread_pool.shutdown) { pthread_mutex_unlock(&thread_pool.lock); pthread_exit(NULL); } // 获取任务 Task task = task_queue[--task_count]; pthread_mutex_unlock(&thread_pool.lock); // 处理任务 handle_request(task.client_fd, task.client_addr); } return NULL; } // 初始化线程池 void thread_pool_init(ThreadPool *pool, int thread_count) { pool->threads = (pthread_t *)malloc(thread_count * sizeof(pthread_t)); if (!pool->threads) { perror("malloc failed"); exit(EXIT_FAILURE); } pool->thread_count = thread_count; pool->shutdown = 0; pthread_mutex_init(&pool->lock, NULL); pthread_cond_init(&pool->cond, NULL); // 初始化任务队列 task_capacity = 256; task_queue = (Task *)malloc(task_capacity * sizeof(Task)); if (!task_queue) { perror("malloc failed"); exit(EXIT_FAILURE); } task_count = 0; // 创建工作线程 for (int i = 0; i < thread_count; i++) { if (pthread_create(&pool->threads[i], NULL, worker_thread, NULL) != 0) { perror("pthread_create failed"); exit(EXIT_FAILURE); } } } // 添加任务到线程池 void add_task(int client_fd, struct sockaddr_in client_addr) { pthread_mutex_lock(&thread_pool.lock); // 如果任务队列已满,扩大容量 if (task_count == task_capacity) { task_capacity *= 2; Task *new_queue = (Task *)realloc(task_queue, task_capacity * sizeof(Task)); if (!new_queue) { perror("realloc failed"); pthread_mutex_unlock(&thread_pool.lock); close(client_fd); return; } task_queue = new_queue; } // 添加任务 task_queue[task_count].client_fd = client_fd; task_queue[task_count].client_addr = client_addr; task_count++; // 通知工作线程 pthread_cond_signal(&thread_pool.cond); pthread_mutex_unlock(&thread_pool.lock); } // 设置文件描述符为非阻塞 void set_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); if (flags == -1) { perror("fcntl F_GETFL failed"); return; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { perror("fcntl F_SETFL failed"); } } int main() { int server_fd, epoll_fd; struct sockaddr_in address; struct epoll_event event, events[MAX_EVENTS]; socklen_t addrlen = sizeof(address); // 创建服务器socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置socket选项 int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { perror("setsockopt"); close(server_fd); exit(EXIT_FAILURE); } // 绑定地址和端口 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"); close(server_fd); exit(EXIT_FAILURE); } // 开始监听 if (listen(server_fd, 128) < 0) { perror("listen"); close(server_fd); exit(EXIT_FAILURE); } printf("Server listening on port %d\n", PORT); printf("Web root: %s\n", WEB_ROOT); // 创建epoll实例 epoll_fd = epoll_create1(0); if (epoll_fd < 0) { perror("epoll_create1"); close(server_fd); exit(EXIT_FAILURE); } // 添加服务器socketepoll event.events = EPOLLIN; event.data.fd = server_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) < 0) { perror("epoll_ctl: server_fd"); close(server_fd); close(epoll_fd); exit(EXIT_FAILURE); } // 初始化线程池 thread_pool_init(&thread_pool, THREAD_POOL_SIZE); printf("Thread pool initialized with %d threads\n", THREAD_POOL_SIZE); // 主循环 while (1) { int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds < 0) { 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 < 0) { perror("accept"); continue; } // 设置非阻塞 set_nonblocking(client_fd); // 添加客户端socketepoll event.events = EPOLLIN | EPOLLET; event.data.fd = client_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) < 0) { perror("epoll_ctl: client_fd"); close(client_fd); continue; } // 打印新连接信息 char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); printf("New connection from %s:%d\n", client_ip, ntohs(client_addr.sin_port)); } else { // 处理客户端请求 int client_fd = events[i].data.fd; // 获取客户端地址 struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); if (getpeername(client_fd, (struct sockaddr *)&client_addr, &addr_len) < 0) { perror("getpeername failed"); close(client_fd); continue; } // 将任务添加到线程池 add_task(client_fd, client_addr); // 从epoll中移除,避免重复处理 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, NULL); } } } // 清理 close(server_fd); close(epoll_fd); // 关闭线程池 thread_pool.shutdown = 1; pthread_cond_broadcast(&thread_pool.cond); for (int i = 0; i < thread_pool.thread_count; i++) { pthread_join(thread_pool.threads[i], NULL); } free(thread_pool.threads); free(task_queue); return 0; } 以这一版为准 运行后,windows浏览器偶尔会对GET请求报错:net:ERR_CONTENT_LENGTH_MISMATCH
最新发布
08-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值