【Linux系统编程必修课】:pthread_mutex互斥锁从入门到精通的7个步骤

第一章:pthread_mutex互斥锁的核心概念与作用

在多线程编程中,多个线程同时访问共享资源可能导致数据竞争和不一致状态。`pthread_mutex` 是 POSIX 线程库提供的一种同步机制,用于保护临界区,确保同一时间只有一个线程可以执行特定代码段。

互斥锁的基本工作原理

互斥锁(Mutex)是一种二元信号量,其状态为“加锁”或“解锁”。当一个线程成功获取锁后,其他试图获取该锁的线程将被阻塞,直到持有锁的线程释放它。
  • 初始化互斥锁使用 pthread_mutex_init()
  • 加锁操作通过 pthread_mutex_lock() 实现
  • 释放锁使用 pthread_mutex_unlock()
  • 销毁锁调用 pthread_mutex_destroy()

典型使用场景示例

以下代码展示了如何使用互斥锁保护全局变量的递增操作:

#include <pthread.h>

int shared_counter = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* increment(void* arg) {
    for (int i = 0; i < 100000; ++i) {
        pthread_mutex_lock(&mutex);     // 进入临界区前加锁
        ++shared_counter;                // 安全访问共享变量
        pthread_mutex_unlock(&mutex);   // 操作完成后释放锁
    }
    return NULL;
}
上述代码中,每次对 shared_counter 的修改都受到互斥锁保护,防止多个线程同时写入造成数据损坏。
常见互斥锁类型对比
类型行为特点
PTHREAD_MUTEX_NORMAL普通锁,不检测死锁
PTHREAD_MUTEX_ERRORCHECK增加错误检查,重复加锁返回错误
PTHREAD_MUTEX_RECURSIVE允许同一线程多次加锁

第二章:互斥锁的基础使用方法

2.1 互斥锁的初始化与销毁:静态与动态方式对比

静态初始化
在编译期即可完成互斥锁的初始化,适用于全局或静态变量场景。使用宏 PTHREAD_MUTEX_INITIALIZER 可直接赋值:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
该方式简洁高效,无需额外函数调用,但仅支持默认属性。
动态初始化
通过 pthread_mutex_init() 函数在运行时初始化,适用于堆分配或需自定义属性的场景:

pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t));
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutex_init(mutex, &attr);
配合 pthread_mutex_destroy() 显式释放资源,确保内存安全与灵活性。
对比分析
  • 静态方式:轻量、快速,但功能受限
  • 动态方式:灵活可控,支持定制属性,但需手动管理生命周期

2.2 加锁与解锁的基本操作:pthread_mutex_lock与unlock详解

在多线程编程中,互斥锁是保障共享数据安全访问的核心机制。POSIX线程库提供了`pthread_mutex_lock`和`pthread_mutex_unlock`函数,用于对互斥量进行加锁与解锁操作。
基本函数原型

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
`pthread_mutex_lock`会阻塞当前线程,直到获得锁;而`pthread_mutex_unlock`释放锁,允许其他等待线程获取访问权。
使用示例与分析

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_lock(&mtx);  // 进入临界区
// 安全访问共享资源
shared_data++;
pthread_mutex_unlock(&mtx); // 离开临界区
上述代码确保同一时刻仅有一个线程能执行临界区操作,避免数据竞争。若未正确配对使用lock/unlock,可能导致死锁或未定义行为。
  • 调用lock时,若锁已被占用,线程将休眠等待
  • unlock必须由持有锁的线程调用,否则引发未定义行为
  • 不可重复对非递归锁加锁,否则导致死锁

2.3 线程安全的临界区设计原则与实践案例

临界区的基本保障机制
在多线程环境中,临界区指一段访问共享资源的代码,必须确保同一时刻仅有一个线程执行。实现线程安全的核心是互斥控制。
  • 使用锁机制(如互斥锁、读写锁)保护共享数据
  • 避免死锁:遵循锁顺序、减少持有时间
  • 优先使用高级并发结构(如通道、原子操作)替代显式锁
Go语言中的实际应用
var mu sync.Mutex
var count int

func increment() {
    mu.Lock()
    defer mu.Unlock()
    count++
}
上述代码通过sync.Mutex确保对全局变量count的修改是原子的。每次调用increment时,必须先获取锁,防止多个goroutine同时进入临界区,从而避免数据竞争。
常见模式对比
机制适用场景性能开销
互斥锁频繁写操作中等
读写锁读多写少低(读)/中(写)
原子操作简单类型操作最低

2.4 使用互斥锁保护共享变量:一个计数器的多线程竞争实验

在并发编程中,多个 goroutine 同时访问共享变量会导致数据竞争。本节通过一个递增计数器的实验展示这一问题及其解决方案。
问题重现:无保护的共享计数器
以下代码在多个 goroutine 中并发递增一个全局变量:

var counter int

func worker() {
    for i := 0; i < 1000; i++ {
        counter++ // 数据竞争发生点
    }
}

// 启动10个worker
for i := 0; i < 10; i++ {
    go worker()
}
由于 counter++ 非原子操作(读取、加1、写回),多个 goroutine 并发执行会导致结果不一致。
解决方案:引入互斥锁
使用 sync.Mutex 可确保同一时间只有一个 goroutine 能访问临界区:

var (
    counter int
    mu      sync.Mutex
)

func worker() {
    for i := 0; i < 1000; i++ {
        mu.Lock()
        counter++
        mu.Unlock()
    }
}
每次修改前调用 Lock(),修改完成后立即 Unlock(),有效防止竞态条件。

2.5 错误处理机制:常见返回码及其应对策略

在分布式系统交互中,合理的错误处理是保障服务稳定的关键。API调用通常通过HTTP状态码或自定义返回码传递执行结果。
常见返回码分类
  • 2xx:请求成功,如200表示正常响应;
  • 4xx:客户端错误,如400参数错误、401未授权、404资源不存在;
  • 5xx:服务端异常,如500内部错误、503服务不可用。
典型错误处理代码示例
func handleResponse(resp *http.Response) error {
    switch resp.StatusCode {
    case 200:
        return nil
    case 400:
        return fmt.Errorf("invalid request parameters")
    case 401:
        return fmt.Errorf("authentication required")
    case 500:
        return fmt.Errorf("server internal error, retry needed")
    default:
        return fmt.Errorf("unexpected status: %d", resp.StatusCode)
    }
}
该函数根据HTTP状态码返回对应的错误信息,便于上层逻辑进行重试或提示。对于4xx类错误应校验输入合法性,而5xx则建议结合退避策略进行重试。

第三章:互斥锁的高级属性配置

3.1 设置互斥锁属性:递归锁、错误检查锁与默认锁类型分析

在多线程编程中,互斥锁的属性配置直接影响线程安全与程序健壮性。POSIX线程(pthread)提供了`pthread_mutexattr_t`结构体用于定制锁行为。
常见互斥锁类型对比
  • 默认锁(PTHREAD_MUTEX_DEFAULT):标准互斥锁,重复加锁导致死锁。
  • 递归锁(PTHREAD_MUTEX_RECURSIVE):同一线程可多次加锁,需等量解锁。
  • 错误检查锁(PTHREAD_MUTEX_ERRORCHECK):检测递归加锁并返回错误码。
代码示例:设置递归锁属性

pthread_mutexattr_t attr;
pthread_mutex_t mutex;

pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex, &attr);
上述代码初始化互斥锁属性,并将其设为递归类型,允许同一线程重复获取锁,避免自锁。最后将属性应用到实际锁变量中。

3.2 进程间互斥锁的应用场景与PTHREAD_PROCESS_SHARED详解

在多进程协同工作的场景中,多个进程可能需要访问同一块共享内存区域,例如日志写入、缓存更新或设备状态管理。此时,必须通过进程间互斥锁防止数据竞争。
PTHREAD_PROCESS_SHARED 属性的作用
POSIX 线程库支持通过设置互斥锁属性 PTHREAD_PROCESS_SHARED,使其可在多个进程间共享使用。若未设置此属性,互斥锁仅限于同一进程内的线程使用。

pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&mutex, &attr);
上述代码初始化一个可跨进程共享的互斥锁。其中 pthread_mutexattr_setpshared 将属性设为 PTHREAD_PROCESS_SHARED,确保锁可在 mmap 映射的共享内存中被多个进程访问。
典型应用场景
  • 多进程守护进程对配置文件的互斥读写
  • 高性能计算中共享内存缓冲区的访问控制
  • 嵌入式系统中多个进程协调访问硬件资源

3.3 自定义互斥锁属性的完整流程与调试技巧

初始化自定义互斥锁属性
在 POSIX 线程中,可通过 pthread_mutexattr_init 初始化互斥锁属性对象,进而配置其行为特性。常见属性包括进程共享性、类型(如递归锁)等。

pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // 设置为递归锁
上述代码将互斥锁类型设为递归锁,允许同一线程多次加锁。参数 PTHREAD_MUTEX_RECURSIVE 表示锁可重入,避免死锁。
调试与验证属性设置
使用 pthread_mutexattr_gettype 可验证属性设置是否生效,结合断言确保配置正确。
  • 确保属性对象在销毁前未被重复初始化
  • 检查系统是否支持所设属性(如跨进程共享)
  • 调试时打印返回码:0 表示成功,非零为错误类型

第四章:避免死锁与性能优化策略

4.1 死锁的四大条件分析及在互斥锁中的典型表现

死锁是多线程编程中常见的问题,尤其在使用互斥锁进行资源管理时极易发生。其产生必须同时满足四个必要条件。
死锁的四大必要条件
  • 互斥条件:资源一次只能被一个线程占用。
  • 持有并等待:线程已持有至少一个资源,又申请新的资源而被阻塞。
  • 不可剥夺条件:已分配的资源不能被其他线程强行抢占。
  • 循环等待条件:多个线程之间形成环形等待链。
互斥锁中的典型死锁场景
var mu1, mu2 sync.Mutex

func threadA() {
    mu1.Lock()
    time.Sleep(1 * time.Second)
    mu2.Lock() // 等待 threadB 释放 mu2
    mu2.Unlock()
    mu1.Unlock()
}

func threadB() {
    mu2.Lock()
    time.Sleep(1 * time.Second)
    mu1.Lock() // 等待 threadA 释放 mu1
    mu1.Unlock()
    mu2.Unlock()
}
上述代码中,threadA 持有 mu1 并请求 mu2,而 threadB 持有 mu2 并请求 mu1,形成循环等待,最终导致死锁。

4.2 避免死锁的最佳实践:加锁顺序与超时机制(pthread_mutex_trylock)

在多线程编程中,死锁常因无序加锁导致。确保所有线程以相同顺序获取多个互斥锁,可有效避免循环等待。
加锁顺序规范
当多个线程需同时持有多个锁时,应约定全局一致的加锁顺序。例如,始终先锁 A 再锁 B,杜绝反向加锁引发的死锁。
使用超时机制防止无限等待
POSIX 提供 pthread_mutex_trylock(),尝试获取锁失败时立即返回错误码而非阻塞:

int result = pthread_mutex_trylock(&mutex);
if (result == 0) {
    // 成功获得锁,执行临界区操作
    pthread_mutex_unlock(&mutex);
} else {
    // 锁被占用,可选择重试或放弃,避免死锁
}
该机制适用于资源探测、避免复杂锁依赖等场景,提升系统健壮性。结合固定加锁顺序与非阻塞尝试,能显著降低死锁发生概率。

4.3 条件变量与互斥锁协同:生产者-消费者模型实现

在并发编程中,条件变量与互斥锁的协同使用是解决线程同步问题的关键机制。通过结合两者,可高效实现生产者-消费者模型,避免资源竞争与忙等待。
核心机制解析
生产者向共享缓冲区添加数据,消费者从中取出数据。当缓冲区满时,生产者等待;当缓冲区空时,消费者阻塞。条件变量用于线程间通知状态变化,互斥锁保护共享数据的访问安全。
var (
    buffer     = make([]int, 0, 10)
    mutex      sync.Mutex
    notEmpty   sync.Cond
    notFull    sync.Cond
)

func init() {
    notEmpty.L = &mutex
    notFull.L = &mutex
}
上述代码初始化缓冲区与同步原语。notEmptynotFull 条件变量共用同一互斥锁,确保状态检查与等待操作的原子性。
生产者逻辑
生产者在插入前获取锁,若缓冲区满则调用 wait() 释放锁并等待;插入后唤醒消费者。
  • 加锁保护临界区
  • 循环检查缓冲区是否满
  • 插入数据并通知等待的消费者

4.4 互斥锁的性能瓶颈分析与减少争用的优化手段

在高并发场景下,互斥锁(Mutex)因串行化访问常成为性能瓶颈。当多个 goroutine 频繁竞争同一把锁时,会导致大量协程阻塞、上下文切换开销增加,进而降低系统吞吐量。
典型性能问题示例

var mu sync.Mutex
var counter int

func increment() {
    mu.Lock()
    counter++ // 临界区过长
    time.Sleep(time.Microsecond) // 模拟处理延迟
    mu.Unlock()
}
上述代码中,临界区包含非必要操作,延长了持锁时间,加剧争用。
优化策略
  • 缩小临界区:仅对共享数据操作加锁
  • 使用读写锁(sync.RWMutex)分离读写场景
  • 采用分片锁(Sharded Mutex)降低冲突概率
  • 利用无锁结构如 atomicsync/atomic 包进行轻量级同步
通过合理设计同步粒度,可显著提升并发性能。

第五章:从理论到工程实践的跨越与总结

架构演进中的权衡决策
在微服务架构落地过程中,团队面临服务粒度划分的挑战。过细拆分导致运维复杂度上升,而粗粒度过高则影响独立部署能力。某电商平台最终采用领域驱动设计(DDD)边界上下文作为服务划分依据,将订单、库存、支付分离为独立服务,通过异步消息解耦。
  • 使用 Kafka 实现最终一致性,降低系统间直接依赖
  • 引入 Saga 模式处理跨服务事务,避免分布式锁开销
  • 通过 OpenTelemetry 实现全链路追踪,提升问题定位效率
性能优化实战案例
某金融风控系统在高并发场景下出现响应延迟飙升。经分析发现瓶颈在于频繁的 JSON 序列化操作。采用预编译序列化方案后性能显著提升:

// 使用 easyjson 减少反射开销
//go:generate easyjson -no_std_marshalers model.go

type RiskEvent struct {
    UserID    int64  `json:"user_id"`
    Amount    float64 `json:"amount"`
    Timestamp int64  `json:"timestamp"`
}
// easyjson gen generates static marshaler code
可观测性体系建设
维度工具栈采样频率
日志EFK + Loki100%
指标Prometheus + Grafana15s
追踪Jaeger + OTLP10% 随机采样

客户端埋点 → Agent 收集 → 缓冲队列 → 存储引擎 → 查询接口 → 可视化面板

我的实验作业是做一个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需完成GET和POST等基础请求处理(必修) 6. 如何使其具备高并发性并实现(选修) 7. 使用Makefile构建(必修) 2. 系统架构描述 2.1. 系统框架图 通过http协议构建一个Web服务器,该服务器能够处理浏览器发过来的http请求,并根据http请求返回http响应给浏览器。该服务器的功能上可以存放各种各样的资源,用户可以利用浏览器来访问我的服务器资源,也可以提交数据给服务器,让服务器去处理结果,并返回给浏览器。 2.2. 模块划分 2.3. 模块描述 3. 4.
08-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值