/** * webserver_pipeline.c — 实验3 任务3: Web服务器业务分割模型 * * 架构: * main 接受连接 → read_msg 线程池 (解析HTTP请求) * → filename_queue → read_file 线程池 (读取文件) * → msg_queue → send_msg 线程池 (发送响应) * * 三个线程池 + 两个消息队列构成流水线处理模型。 * 包含监控线程定时输出性能参数。 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "thpool.h" /* ======================== 常量定义 ======================== */ #define VERSION 23 #define BUFSIZE 8096 #define ERROR 42 #define LOG 44 #define FORBIDDEN 403 #define NOTFOUND 404 #define READ_MSG_THREADS 4 #define READ_FILE_THREADS 4 #define SEND_MSG_THREADS 4 #define FQ_SIZE 64 #define MQ_SIZE 64 #define MONITOR_INTERVAL 5 #ifndef SIGCLD #define SIGCLD SIGCHLD #endif /* ======================== 文件类型表 ======================== */ struct { char *ext; char *filetype; } extensions[] = { {"gif", "image/gif" }, {"jpg", "image/jpg" }, {"jpeg", "image/jpeg" }, {"png", "image/png" }, {"ico", "image/ico" }, {"zip", "image/zip" }, {"gz", "image/gz" }, {"tar", "image/tar" }, {"htm", "text/html" }, {"html", "text/html" }, {0, 0} }; /* ======================== 消息队列数据结构 ======================== */ typedef struct { char filename[512]; char filetype[64]; int socket_fd; int hit; } fq_item_t; typedef struct { fq_item_t *items; int cap; int inpos; int outpos; sem_t mutex; sem_t avail; sem_t ready; int len; } filename_queue_t; typedef struct { char *filebuf; long filesize; char filetype[64]; int socket_fd; int hit; } mq_item_t; typedef struct { mq_item_t *items; int cap; int inpos; int outpos; sem_t mutex; sem_t avail; sem_t ready; int len; } msg_queue_t; /* ======================== 统计数据 ======================== */ typedef struct { int read_msg_active; int read_file_active; int send_msg_active; long long read_msg_active_us; long long read_file_active_us; long long send_msg_active_us; long long read_msg_block_us; long long read_file_block_us; long long send_msg_block_us; long read_msg_tasks; long read_file_tasks; long send_msg_tasks; int read_msg_active_max; int read_msg_active_min; int read_file_active_max; int read_file_active_min; int send_msg_active_max; int send_msg_active_min; int fq_len; int mq_len; pthread_mutex_t lock; } stats_t; /* ======================== 全局变量 ======================== */ static volatile int server_running = 1; static int listen_fd = -1; /* 供信号处理器关闭 */ static filename_queue_t fq; static msg_queue_t mq; static stats_t stats; static threadpool read_msg_pool; /* ======================== 函数声明 ======================== */ void logger(int type, const char *s1, const char *s2, int socket_fd); long calculate_time_diff(struct timespec start, struct timespec end); void ctrlc_handler(int sig); /* 带超时的信号量等待——1秒超时,返回0=成功,-1=超时/错误 */ int sem_timedwait_1s(sem_t *sem); /* 可中断的队列操作——server_running=0 时返回 -1 */ int fq_insert_try(filename_queue_t *qp, fq_item_t item); int fq_remove_try(filename_queue_t *qp, fq_item_t *out); int mq_insert_try(msg_queue_t *qp, mq_item_t item); int mq_remove_try(msg_queue_t *qp, mq_item_t *out); void fq_init(filename_queue_t *qp, int cap); void mq_init(msg_queue_t *qp, int cap); void read_msg_worker(void *arg); void *read_file_thread_func(void *arg); void *send_msg_thread_func(void *arg); void *monitor_thread_func(void *arg); /* ======================== 工具函数实现 ======================== */ void logger(int type, const char *s1, const char *s2, int socket_fd) { char logbuffer[BUFSIZE * 2]; char timebuffer[64]; struct timeval tv; gettimeofday(&tv, NULL); struct tm *tm_info = localtime(&tv.tv_sec); strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", tm_info); char full_timebuffer[256]; snprintf(full_timebuffer, sizeof(full_timebuffer), "[%s.%03ld]", timebuffer, tv.tv_usec / 1000); switch (type) { case ERROR: snprintf(logbuffer, sizeof(logbuffer), "%s [ERROR] %s:%s Errno=%d pid=%d", full_timebuffer, s1, s2, errno, getpid()); break; case FORBIDDEN: if (write(socket_fd, "HTTP/1.1 403 Forbidden\nContent-Length: 185\n" "Connection: close\nContent-Type: text/html\n\n" "\n403 Forbidden\n\n" "

Forbidden

\n" "The requested URL, file type or operation is not allowed " "on this simple static file webserver.\n" "\n", 271) < 0) {} snprintf(logbuffer, sizeof(logbuffer), "%s [FORBIDDEN] %s:%s", full_timebuffer, s1, s2); break; case NOTFOUND: if (write(socket_fd, "HTTP/1.1 404 Not Found\nContent-Length: 136\n" "Connection: close\nContent-Type: text/html\n\n" "\n404 Not Found\n\n" "

Not Found

\n" "The requested URL was not found on this server.\n" "\n", 224) < 0) {} snprintf(logbuffer, sizeof(logbuffer), "%s [NOT FOUND] %s:%s", full_timebuffer, s1, s2); break; case LOG: snprintf(logbuffer, sizeof(logbuffer), "%s [INFO] %s:%s:%d", full_timebuffer, s1, s2, socket_fd); break; } #ifdef DEBUG { int log_fd; if ((log_fd = open("server_pipeline.log", O_CREAT | O_WRONLY | O_APPEND, 0644)) >= 0) { if (write(log_fd, logbuffer, strlen(logbuffer)) < 0) {} if (write(log_fd, "\n", 1) < 0) {} close(log_fd); } } #endif } long calculate_time_diff(struct timespec start, struct timespec end) { long diff_sec = end.tv_sec - start.tv_sec; long diff_nsec = end.tv_nsec - start.tv_nsec; return diff_sec * 1000 + diff_nsec / 1000000; } void ctrlc_handler(int sig) { (void)sig; printf("\n[Ctrl+C] 正在关闭服务器...\n"); server_running = 0; /* 关闭监听 socket 以唤醒阻塞在 accept() 的主线程 */ if (listen_fd >= 0) { shutdown(listen_fd, SHUT_RDWR); close(listen_fd); listen_fd = -1; } } /* ======================== 可中断的信号量等待 ======================== */ /** * sem_timedwait_1s — 等待信号量最多1秒 * 返回 0=成功获取, -1=超时或出错 */ int sem_timedwait_1s(sem_t *sem) { struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) == -1) return -1; ts.tv_sec += 1; return sem_timedwait(sem, &ts); } /* ======================== 可中断队列操作 ======================== */ void fq_init(filename_queue_t *qp, int cap) { qp->items = calloc((size_t)cap, sizeof(fq_item_t)); qp->cap = cap; qp->inpos = 0; qp->outpos = 0; qp->len = 0; sem_init(&qp->mutex, 0, 1); sem_init(&qp->avail, 0, (unsigned int)cap); sem_init(&qp->ready, 0, 0); } void mq_init(msg_queue_t *qp, int cap) { qp->items = calloc((size_t)cap, sizeof(mq_item_t)); qp->cap = cap; qp->inpos = 0; qp->outpos = 0; qp->len = 0; sem_init(&qp->mutex, 0, 1); sem_init(&qp->avail, 0, (unsigned int)cap); sem_init(&qp->ready, 0, 0); } /** * fq_insert_try — 尝试插入 Filename Queue,可被 Ctrl+C 中断 * 返回 0=成功, -1=服务器关闭中 */ int fq_insert_try(filename_queue_t *qp, fq_item_t item) { while (server_running) { if (sem_timedwait_1s(&qp->avail) != 0) continue; /* 超时,重新检查 server_running */ /* 获取到空位 */ sem_wait(&qp->mutex); qp->items[qp->inpos] = item; qp->inpos = (qp->inpos + 1) % qp->cap; qp->len++; sem_post(&qp->mutex); sem_post(&qp->ready); return 0; } return -1; /* 服务器关闭 */ } /** * fq_remove_try — 尝试从 Filename Queue 取出,可被 Ctrl+C 中断 * 返回 0=成功, -1=服务器关闭中 */ int fq_remove_try(filename_queue_t *qp, fq_item_t *out) { while (server_running) { if (sem_timedwait_1s(&qp->ready) != 0) continue; sem_wait(&qp->mutex); *out = qp->items[qp->outpos]; qp->outpos = (qp->outpos + 1) % qp->cap; qp->len--; sem_post(&qp->mutex); sem_post(&qp->avail); return 0; } return -1; } int mq_insert_try(msg_queue_t *qp, mq_item_t item) { while (server_running) { if (sem_timedwait_1s(&qp->avail) != 0) continue; sem_wait(&qp->mutex); qp->items[qp->inpos] = item; qp->inpos = (qp->inpos + 1) % qp->cap; qp->len++; sem_post(&qp->mutex); sem_post(&qp->ready); return 0; } return -1; } int mq_remove_try(msg_queue_t *qp, mq_item_t *out) { while (server_running) { if (sem_timedwait_1s(&qp->ready) != 0) continue; sem_wait(&qp->mutex); *out = qp->items[qp->outpos]; qp->outpos = (qp->outpos + 1) % qp->cap; qp->len--; sem_post(&qp->mutex); sem_post(&qp->avail); return 0; } return -1; } /* ======================== 流水线阶段实现 ======================== */ typedef struct { int socket_fd; int hit; } read_msg_arg_t; void read_msg_worker(void *arg) { read_msg_arg_t *p = (read_msg_arg_t *)arg; int fd = p->socket_fd; int hit = p->hit; free(arg); if (!server_running) { close(fd); return; } struct timespec t_start, t_end; long i, ret, buflen, len; int j; char *fstr; char buffer[BUFSIZE + 1]; clock_gettime(CLOCK_MONOTONIC, &t_start); logger(LOG, "[Stage1] Read Msg start", "", fd); ret = read(fd, buffer, BUFSIZE); if (ret <= 0) { logger(FORBIDDEN, "[Stage1] Failed to read browser request", "", fd); clock_gettime(CLOCK_MONOTONIC, &t_end); goto done_active; } buffer[ret] = '\0'; #ifdef DEBUG printf("[Stage1] 第%d个客户请求: %s\n", hit, buffer); #endif for (i = 0; i < ret; i++) if (buffer[i] == '\r' || buffer[i] == '\n') buffer[i] = '*'; if (strncmp(buffer, "GET ", 4)) { logger(FORBIDDEN, "[Stage1] Only simple GET operation supported", buffer, fd); clock_gettime(CLOCK_MONOTONIC, &t_end); goto done_active; } for (i = 4; i < BUFSIZE; i++) { if (buffer[i] == ' ') { buffer[i] = 0; break; } } for (j = 0; j < (int)i - 1; j++) { if (buffer[j] == '.' && buffer[j + 1] == '.') { logger(FORBIDDEN, "[Stage1] Parent directory not supported", buffer, fd); clock_gettime(CLOCK_MONOTONIC, &t_end); goto done_active; } } if (!strncmp(&buffer[0], "GET /\0", 6)) strcpy(buffer, "GET /index.html"); buflen = strlen(buffer); fstr = NULL; for (i = 0; extensions[i].ext != 0; i++) { len = strlen(extensions[i].ext); if (!strncmp(&buffer[buflen - len], extensions[i].ext, len)) { fstr = extensions[i].filetype; break; } } if (fstr == NULL) { logger(FORBIDDEN, "[Stage1] File extension type not supported", buffer, fd); clock_gettime(CLOCK_MONOTONIC, &t_end); goto done_active; } /* 构造 filename queue 项目并推入 */ { fq_item_t item; strncpy(item.filename, &buffer[5], sizeof(item.filename) - 1); item.filename[sizeof(item.filename) - 1] = '\0'; strncpy(item.filetype, fstr, sizeof(item.filetype) - 1); item.filetype[sizeof(item.filetype) - 1] = '\0'; item.socket_fd = fd; item.hit = hit; logger(LOG, "[Stage1] Pushing to filename_queue", item.filename, hit); clock_gettime(CLOCK_MONOTONIC, &t_end); { long long elapsed_us = (t_end.tv_sec - t_start.tv_sec) * 1000000LL + (t_end.tv_nsec - t_start.tv_nsec) / 1000LL; pthread_mutex_lock(&stats.lock); stats.read_msg_active_us += elapsed_us; stats.read_msg_tasks++; pthread_mutex_unlock(&stats.lock); } /* 可中断的队列插入 */ { struct timespec b_start, b_end; clock_gettime(CLOCK_MONOTONIC, &b_start); int rc = fq_insert_try(&fq, item); clock_gettime(CLOCK_MONOTONIC, &b_end); long long block_us = (b_end.tv_sec - b_start.tv_sec) * 1000000LL + (b_end.tv_nsec - b_start.tv_nsec) / 1000LL; pthread_mutex_lock(&stats.lock); stats.read_msg_block_us += block_us; pthread_mutex_unlock(&stats.lock); if (rc != 0) { /* 服务器关闭中 */ close(fd); return; } } } return; done_active: clock_gettime(CLOCK_MONOTONIC, &t_end); { long long elapsed_us = (t_end.tv_sec - t_start.tv_sec) * 1000000LL + (t_end.tv_nsec - t_start.tv_nsec) / 1000LL; pthread_mutex_lock(&stats.lock); stats.read_msg_active_us += elapsed_us; stats.read_msg_tasks++; pthread_mutex_unlock(&stats.lock); } close(fd); } /** * 阶段2: Read File — 持久线程,从 filename_queue 取任务,读文件,推入 msg_queue */ void *read_file_thread_func(void *arg) { int thread_id = *(int *)arg; free(arg); printf("[ReadFile Thread %d] 启动\n", thread_id); prctl(PR_SET_NAME, "pipeline-rf", 0, 0, 0); while (server_running) { struct timespec b_start, b_end; fq_item_t item; /* 可中断的队列取出 */ clock_gettime(CLOCK_MONOTONIC, &b_start); int rc = fq_remove_try(&fq, &item); clock_gettime(CLOCK_MONOTONIC, &b_end); { long long block_us = (b_end.tv_sec - b_start.tv_sec) * 1000000LL + (b_end.tv_nsec - b_start.tv_nsec) / 1000LL; pthread_mutex_lock(&stats.lock); stats.read_file_block_us += block_us; stats.read_file_active++; pthread_mutex_unlock(&stats.lock); } if (rc != 0 || !server_running) break; struct timespec t_start, t_end; clock_gettime(CLOCK_MONOTONIC, &t_start); logger(LOG, "[Stage2] Read File start", item.filename, item.hit); int file_fd = open(item.filename, O_RDONLY); if (file_fd == -1) { logger(NOTFOUND, "[Stage2] Failed to open file", item.filename, item.socket_fd); close(item.socket_fd); clock_gettime(CLOCK_MONOTONIC, &t_end); goto done_file_active; } long filesize = (long)lseek(file_fd, 0, SEEK_END); lseek(file_fd, 0, SEEK_SET); char *filebuf = (char *)malloc((size_t)filesize); if (filebuf == NULL) { logger(ERROR, "[Stage2] malloc failed", item.filename, item.socket_fd); close(file_fd); close(item.socket_fd); clock_gettime(CLOCK_MONOTONIC, &t_end); goto done_file_active; } ssize_t total_read = 0; while (total_read < filesize) { ssize_t n = read(file_fd, filebuf + total_read, (size_t)(filesize - total_read)); if (n <= 0) break; total_read += n; } close(file_fd); #ifdef DEBUG printf("[Stage2] 读取文件 %s, 大小 %ld 字节, hit=%d\n", item.filename, filesize, item.hit); #endif { mq_item_t mitem; mitem.filebuf = filebuf; mitem.filesize = filesize; strncpy(mitem.filetype, item.filetype, sizeof(mitem.filetype) - 1); mitem.filetype[sizeof(mitem.filetype) - 1] = '\0'; mitem.socket_fd = item.socket_fd; mitem.hit = item.hit; logger(LOG, "[Stage2] Pushing to msg_queue", item.filename, item.hit); clock_gettime(CLOCK_MONOTONIC, &t_end); { long long elapsed_us = (t_end.tv_sec - t_start.tv_sec) * 1000000LL + (t_end.tv_nsec - t_start.tv_nsec) / 1000LL; pthread_mutex_lock(&stats.lock); stats.read_file_active_us += elapsed_us; stats.read_file_tasks++; stats.read_file_active--; pthread_mutex_unlock(&stats.lock); } /* 可中断的队列插入 */ { struct timespec b2_start, b2_end; clock_gettime(CLOCK_MONOTONIC, &b2_start); int rc2 = mq_insert_try(&mq, mitem); clock_gettime(CLOCK_MONOTONIC, &b2_end); long long b2_us = (b2_end.tv_sec - b2_start.tv_sec) * 1000000LL + (b2_end.tv_nsec - b2_start.tv_nsec) / 1000LL; pthread_mutex_lock(&stats.lock); stats.read_file_block_us += b2_us; pthread_mutex_unlock(&stats.lock); if (rc2 != 0) { free(filebuf); close(item.socket_fd); } } } continue; done_file_active: clock_gettime(CLOCK_MONOTONIC, &t_end); { long long elapsed_us = (t_end.tv_sec - t_start.tv_sec) * 1000000LL + (t_end.tv_nsec - t_start.tv_nsec) / 1000LL; pthread_mutex_lock(&stats.lock); stats.read_file_active_us += elapsed_us; stats.read_file_tasks++; stats.read_file_active--; pthread_mutex_unlock(&stats.lock); } } printf("[ReadFile Thread %d] 退出\n", thread_id); return NULL; } /** * 阶段3: Send Msg — 持久线程,从 msg_queue 取任务,发送响应,释放资源 */ void *send_msg_thread_func(void *arg) { int thread_id = *(int *)arg; free(arg); printf("[SendMsg Thread %d] 启动\n", thread_id); prctl(PR_SET_NAME, "pipeline-sm", 0, 0, 0); while (server_running) { struct timespec b_start, b_end; mq_item_t item; /* 可中断的队列取出 */ clock_gettime(CLOCK_MONOTONIC, &b_start); int rc = mq_remove_try(&mq, &item); clock_gettime(CLOCK_MONOTONIC, &b_end); { long long block_us = (b_end.tv_sec - b_start.tv_sec) * 1000000LL + (b_end.tv_nsec - b_start.tv_nsec) / 1000LL; pthread_mutex_lock(&stats.lock); stats.send_msg_block_us += block_us; stats.send_msg_active++; pthread_mutex_unlock(&stats.lock); } if (rc != 0 || !server_running) break; struct timespec t_start, t_end; clock_gettime(CLOCK_MONOTONIC, &t_start); logger(LOG, "[Stage3] Send Msg start", "", item.hit); char header[BUFSIZE]; int header_len = snprintf(header, sizeof(header), "HTTP/1.1 200 OK\n" "Server: nweb-pipeline/%d.0\n" "Content-Length: %ld\n" "Connection: close\n" "Content-Type: %s\n\n", VERSION, item.filesize, item.filetype); if (write(item.socket_fd, header, (size_t)header_len) < 0) { logger(ERROR, "[Stage3] Failed to write HTTP header", "", item.socket_fd); free(item.filebuf); close(item.socket_fd); clock_gettime(CLOCK_MONOTONIC, &t_end); goto done_send_active; } ssize_t total_sent = 0; while (total_sent < item.filesize) { ssize_t n = write(item.socket_fd, item.filebuf + total_sent, (size_t)(item.filesize - total_sent)); if (n < 0) { logger(ERROR, "[Stage3] Failed to write file content", "", item.socket_fd); break; } total_sent += n; } #ifdef DEBUG printf("[Stage3] 发送响应完成, hit=%d, 大小=%ld 字节\n", item.hit, item.filesize); #endif free(item.filebuf); close(item.socket_fd); clock_gettime(CLOCK_MONOTONIC, &t_end); { long long elapsed_us = (t_end.tv_sec - t_start.tv_sec) * 1000000LL + (t_end.tv_nsec - t_start.tv_nsec) / 1000LL; pthread_mutex_lock(&stats.lock); stats.send_msg_active_us += elapsed_us; stats.send_msg_tasks++; stats.send_msg_active--; pthread_mutex_unlock(&stats.lock); } continue; done_send_active: clock_gettime(CLOCK_MONOTONIC, &t_end); { long long elapsed_us = (t_end.tv_sec - t_start.tv_sec) * 1000000LL + (t_end.tv_nsec - t_start.tv_nsec) / 1000LL; pthread_mutex_lock(&stats.lock); stats.send_msg_active_us += elapsed_us; stats.send_msg_tasks++; stats.send_msg_active--; pthread_mutex_unlock(&stats.lock); } } printf("[SendMsg Thread %d] 退出\n", thread_id); return NULL; } /* ======================== 监控线程 ======================== */ void *monitor_thread_func(void *arg) { (void)arg; printf("[Monitor] 监控线程启动, 每 %d 秒输出一次性能参数\n", MONITOR_INTERVAL); while (server_running) { /* 每秒检查一次,共 MONITOR_INTERVAL 秒 */ for (int s = 0; s < MONITOR_INTERVAL && server_running; s++) sleep(1); if (!server_running) break; pthread_mutex_lock(&stats.lock); stats.fq_len = fq.len; stats.mq_len = mq.len; stats.read_msg_active = thpool_num_threads_working(read_msg_pool); if (stats.read_msg_active > stats.read_msg_active_max) stats.read_msg_active_max = stats.read_msg_active; if (stats.read_msg_active < stats.read_msg_active_min) stats.read_msg_active_min = stats.read_msg_active; if (stats.read_file_active > stats.read_file_active_max) stats.read_file_active_max = stats.read_file_active; if (stats.read_file_active < stats.read_file_active_min) stats.read_file_active_min = stats.read_file_active; if (stats.send_msg_active > stats.send_msg_active_max) stats.send_msg_active_max = stats.send_msg_active; if (stats.send_msg_active < stats.send_msg_active_min) stats.send_msg_active_min = stats.send_msg_active; long long total_active_us = stats.read_msg_active_us + stats.read_file_active_us + stats.send_msg_active_us; long long total_block_us = stats.read_msg_block_us + stats.read_file_block_us + stats.send_msg_block_us; long total_tasks = stats.read_msg_tasks + stats.read_file_tasks + stats.send_msg_tasks; long avg_rm_active = stats.read_msg_tasks > 0 ? (long)(stats.read_msg_active_us / stats.read_msg_tasks) : 0; long avg_rf_active = stats.read_file_tasks > 0 ? (long)(stats.read_file_active_us / stats.read_file_tasks) : 0; long avg_sm_active = stats.send_msg_tasks > 0 ? (long)(stats.send_msg_active_us / stats.send_msg_tasks) : 0; long avg_rm_block = stats.read_msg_tasks > 0 ? (long)(stats.read_msg_block_us / stats.read_msg_tasks) : 0; long avg_rf_block = stats.read_file_tasks > 0 ? (long)(stats.read_file_block_us / stats.read_file_tasks) : 0; long avg_sm_block = stats.send_msg_tasks > 0 ? (long)(stats.send_msg_block_us / stats.send_msg_tasks) : 0; printf("\n"); printf("╔══════════════════════════════════════════════════════════════╗\n"); printf("║ 业务分割模型 (Pipeline) 性能监控报告 ║\n"); printf("╠══════════════════════════════════════════════════════════════╣\n"); printf("║ 处理请求总数: %6ld ║\n", stats.read_msg_tasks); printf("╠══════════════════════════════════════════════════════════════╣\n"); printf("║ 线程池 │ 活跃 │ 最高 │ 最低 │ 平均活跃 │ 平均阻塞 ║\n"); printf("║ │(当前)│ │ │ 时间(us) │ 时间(us) ║\n"); printf("╠──────────────────────────────────────────────────────────────╣\n"); printf("║ Read Msg │ %4d │ %4d │ %4d │ %8ld │ %9ld ║\n", stats.read_msg_active, stats.read_msg_active_max, stats.read_msg_active_min, avg_rm_active, avg_rm_block); printf("║ Read File │ %4d │ %4d │ %4d │ %8ld │ %9ld ║\n", stats.read_file_active, stats.read_file_active_max, stats.read_file_active_min, avg_rf_active, avg_rf_block); printf("║ Send Msg │ %4d │ %4d │ %4d │ %8ld │ %9ld ║\n", stats.send_msg_active, stats.send_msg_active_max, stats.send_msg_active_min, avg_sm_active, avg_sm_block); printf("╠══════════════════════════════════════════════════════════════╣\n"); printf("║ 消息队列 │ 当前长度 │ 容量 ║\n"); printf("╠──────────────────────────────────────────────────────────────╣\n"); printf("║ Filename Queue │ %6d │ %4d ║\n", stats.fq_len, FQ_SIZE); printf("║ Msg Queue │ %6d │ %4d ║\n", stats.mq_len, MQ_SIZE); printf("╠══════════════════════════════════════════════════════════════╣\n"); printf("║ 累计活跃时间: %lld us 累计阻塞时间: %lld us ║\n", total_active_us, total_block_us); printf("║ 累计总任务数: %ld ║\n", total_tasks); printf("╚══════════════════════════════════════════════════════════════╝\n"); printf("\n"); pthread_mutex_unlock(&stats.lock); } printf("[Monitor] 监控线程退出\n"); return NULL; } /* ======================== 主函数 ======================== */ int main(int argc, char **argv) { int i, port, socketfd, hit; socklen_t length; static struct sockaddr_in cli_addr; static struct sockaddr_in serv_addr; if (argc < 3 || argc > 3 || !strcmp(argv[1], "-?")) { (void)printf( "hint: webserver_pipeline Port-Number Top-Directory\t\tv%d\n\n" "\tPipeline (业务分割) 版本 — 3线程池 + 2消息队列\n\n" "\tExample: ./webserver_pipeline 8088 .\n\n" "\tOnly Supports:", VERSION); for (i = 0; extensions[i].ext != 0; i++) (void)printf(" %s", extensions[i].ext); (void)printf( "\n\tNot Supported: .., Java, Javascript, CGI\n" "\tNot Supported: / /etc /bin /lib /tmp /usr /dev /sbin\n"); exit(0); } if (signal(SIGINT, ctrlc_handler) == SIG_ERR) (void)printf("警告: 无法设置 SIGINT 信号处理\n"); if (!strncmp(argv[2], "/", 2) || !strncmp(argv[2], "/etc", 5) || !strncmp(argv[2], "/bin", 5) || !strncmp(argv[2], "/lib", 5) || !strncmp(argv[2], "/tmp", 5) || !strncmp(argv[2], "/usr", 5) || !strncmp(argv[2], "/dev", 5) || !strncmp(argv[2], "/sbin", 6)) { (void)printf("ERROR: Bad top directory %s\n", argv[2]); exit(3); } if (chdir(argv[2]) == -1) { perror("Failed to change directory"); exit(4); } port = atoi(argv[1]); if (port < 1 || port > 60000) { fprintf(stderr, "Invalid port number: %d\n", port); exit(1); } if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket creation failed"); exit(1); } int optval = 1; setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons((unsigned short)port); if (bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { perror("Bind failed"); exit(1); } if (listen(listen_fd, 64) < 0) { perror("Listen failed"); exit(1); } printf("╔══════════════════════════════════════════════════════════╗\n"); printf("║ 业务分割模型 (Pipeline) Web 服务器 v%d ║\n", VERSION); printf("║ 端口: %-5d 目录: %-30s ║\n", port, argv[2]); printf("╠══════════════════════════════════════════════════════════╣\n"); printf("║ Accept → [Read Msg:%d] → FQ → [Read File:%d] ║\n", READ_MSG_THREADS, READ_FILE_THREADS); printf("║ → MQ → [Send Msg:%d] → 客户端 ║\n", SEND_MSG_THREADS); printf("╚══════════════════════════════════════════════════════════╝\n"); memset(&stats, 0, sizeof(stats)); stats.read_msg_active_min = 9999; stats.read_file_active_min = 9999; stats.send_msg_active_min = 9999; pthread_mutex_init(&stats.lock, NULL); fq_init(&fq, FQ_SIZE); mq_init(&mq, MQ_SIZE); printf("[Init] 消息队列: FQ(%d) MQ(%d)\n", FQ_SIZE, MQ_SIZE); read_msg_pool = thpool_init(READ_MSG_THREADS); printf("[Init] Read Msg 线程池: %d 线程\n", READ_MSG_THREADS); pthread_t *read_file_threads = malloc(sizeof(pthread_t) * READ_FILE_THREADS); for (i = 0; i < READ_FILE_THREADS; i++) { int *tid = malloc(sizeof(int)); *tid = i; pthread_create(&read_file_threads[i], NULL, read_file_thread_func, tid); } printf("[Init] Read File 线程池: %d 线程\n", READ_FILE_THREADS); pthread_t *send_msg_threads = malloc(sizeof(pthread_t) * SEND_MSG_THREADS); for (i = 0; i < SEND_MSG_THREADS; i++) { int *tid = malloc(sizeof(int)); *tid = i; pthread_create(&send_msg_threads[i], NULL, send_msg_thread_func, tid); } printf("[Init] Send Msg 线程池: %d 线程\n", SEND_MSG_THREADS); pthread_t monitor_thread; pthread_create(&monitor_thread, NULL, monitor_thread_func, NULL); printf("[Init] 监控线程: 间隔 %d 秒\n\n", MONITOR_INTERVAL); /* ---- 主循环 ---- */ for (hit = 1; server_running; hit++) { length = sizeof(cli_addr); socketfd = accept(listen_fd, (struct sockaddr *)&cli_addr, &length); if (socketfd < 0) { if (!server_running) break; continue; } read_msg_arg_t *new_arg = malloc(sizeof(read_msg_arg_t)); new_arg->socket_fd = socketfd; new_arg->hit = hit; thpool_add_work(read_msg_pool, read_msg_worker, new_arg); } /* ---- 关闭 ---- */ printf("\n[Main] 正在关闭...\n"); printf("[Main] 等待 Read Msg 线程池...\n"); thpool_wait(read_msg_pool); thpool_destroy(read_msg_pool); printf("[Main] 等待 Read File / Send Msg / Monitor 线程...\n"); for (i = 0; i < READ_FILE_THREADS; i++) pthread_join(read_file_threads[i], NULL); for (i = 0; i < SEND_MSG_THREADS; i++) pthread_join(send_msg_threads[i], NULL); pthread_join(monitor_thread, NULL); free(read_file_threads); free(send_msg_threads); free(fq.items); free(mq.items); pthread_mutex_destroy(&stats.lock); sem_destroy(&fq.mutex); sem_destroy(&fq.avail); sem_destroy(&fq.ready); sem_destroy(&mq.mutex); sem_destroy(&mq.avail); sem_destroy(&mq.ready); printf("\n╔══════════════════════════════════════════════════════════╗\n"); printf("║ 服务器已关闭 - 最终统计 ║\n"); printf("╠══════════════════════════════════════════════════════════╣\n"); printf("║ Read Msg : %6ld Read File: %6ld ║\n", stats.read_msg_tasks, stats.read_file_tasks); printf("║ Send Msg : %6ld ║\n", stats.send_msg_tasks); printf("╚══════════════════════════════════════════════════════════╝\n"); logger(LOG, "Pipeline webserver exit normally", "", 0); return 0; }