Libuv 从入门到精通完整教程

Libuv 从入门到精通完整教程

📚 教程大纲

阶段 1:基础入门 (1-2周)

  • 第 1 课:事件循环基础
  • 第 2 课:定时器
  • 第 3 课:异步文件操作
  • 第 4 课:TCP 服务器
  • 第 5 课:进程和线程

阶段 2:中级进阶 (2-3周)

  • 第 6 课:高级网络编程
  • 第 7 课:信号和进程间通信
  • 第 8 课:DNS 解析
  • 第 9 课:高级 I/O
  • 第 10 课:错误处理

阶段 3:高级应用 (3-4周)

  • 第 11 课:多线程编程
  • 第 12 课:流处理
  • 第 13 课:自定义异步操作
  • 第 14 课:性能优化
  • 第 15 课:实战项目

📦 环境准备

安装 libuv

# Ubuntu/Debian
sudo apt-get update
sudo apt-get install libuv1-dev

# CentOS/RHEL/Fedora
sudo yum install libuv-devel

# macOS
brew install libuv

# Windows (vcpkg)
vcpkg install libuv:x64-windows

验证安装

# 检查版本
pkg-config --modversion libuv
# 或
find /usr -name "uv.h" 2>/dev/null

🎯 阶段 1:基础入门

第 1 课:Hello Libuv - 事件循环基础

1.1 创建最简单的 libuv 程序

// 1_hello_libuv.c
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>

int main() {
    // 1. 获取默认的事件循环
    uv_loop_t *loop = uv_default_loop();
    
    printf("Hello Libuv!\n");
    printf("Libuv version: %s\n", uv_version_string());
    
    // 2. 运行事件循环
    // UV_RUN_DEFAULT: 运行直到没有活动句柄
    uv_run(loop, UV_RUN_DEFAULT);
    
    // 3. 关闭事件循环
    uv_loop_close(loop);
    
    return 0;
}

编译运行:

gcc -o 1_hello_libuv 1_hello_libuv.c -luv
./1_hello_libuv

1.2 事件循环的生命周期

// 1_loop_lifecycle.c
#include <stdio.h>
#include <uv.h>

void print_loop_info(uv_loop_t* loop) {
    printf("Loop alive handles: %lu\n", uv_loop_alive(loop));
    printf("Loop backend FD: %d\n", uv_backend_fd(loop));
    printf("Loop backend timeout: %d\n", uv_backend_timeout(loop));
}

int main() {
    // 创建事件循环
    uv_loop_t loop;
    uv_loop_init(&loop);
    
    printf("Loop initialized\n");
    print_loop_info(&loop);
    
    // 运行事件循环
    printf("\nRunning event loop...\n");
    
    // UV_RUN_NOWAIT: 轮询一次就返回
    // UV_RUN_ONCE: 处理一个事件
    // UV_RUN_DEFAULT: 运行直到没有活动
    uv_run(&loop, UV_RUN_DEFAULT);
    
    printf("\nAfter run:\n");
    print_loop_info(&loop);
    
    // 关闭事件循环
    uv_loop_close(&loop);
    printf("\nLoop closed\n");
    
    return 0;
}

第 2 课:定时器

2.1 一次性定时器

// 2_timer_once.c
#include <stdio.h>
#include <uv.h>

uv_timer_t timer_handle;
int counter = 0;

void timer_callback(uv_timer_t* handle) {
    counter++;
    printf("Timer fired! Count: %d\n", counter);
    
    if (counter >= 5) {
        printf("Stopping timer after 5 times\n");
        uv_timer_stop(handle);
    }
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    
    // 初始化定时器
    uv_timer_init(loop, &timer_handle);
    
    // 启动定时器
    // 参数: 定时器句柄, 回调函数, 延迟(ms), 间隔(ms)
    uv_timer_start(&timer_handle, timer_callback, 1000, 500);
    
    printf("Timer started. Will fire after 1s, then every 0.5s\n");
    
    uv_run(loop, UV_RUN_DEFAULT);
    
    // 清理
    uv_close((uv_handle_t*)&timer_handle, NULL);
    uv_run(loop, UV_RUN_DEFAULT);  // 处理关闭回调
    
    return 0;
}

2.2 多个定时器

// 2_multi_timers.c
#include <stdio.h>
#include <uv.h>

typedef struct {
    uv_timer_t timer;
    char* name;
    int count;
} my_timer_t;

void timer_callback(uv_timer_t* handle) {
    my_timer_t* my_timer = (my_timer_t*)handle;
    my_timer->count++;
    
    printf("[%s] Count: %d\n", my_timer->name, my_timer->count);
    
    if (my_timer->count >= 3) {
        printf("[%s] Stopping\n", my_timer->name);
        uv_timer_stop(handle);
    }
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    
    my_timer_t timer1, timer2;
    
    // 设置定时器1
    timer1.name = "Timer1";
    timer1.count = 0;
    uv_timer_init(loop, &timer1.timer);
    uv_timer_start(&timer1.timer, timer_callback, 1000, 1000);  // 每秒
    
    // 设置定时器2
    timer2.name = "Timer2";
    timer2.count = 0;
    uv_timer_init(loop, &timer2.timer);
    uv_timer_start(&timer2.timer, timer_callback, 500, 1500);  // 1.5秒间隔
    
    printf("Two timers started:\n");
    printf("  Timer1: starts at 1s, interval 1s\n");
    printf("  Timer2: starts at 0.5s, interval 1.5s\n");
    
    uv_run(loop, UV_RUN_DEFAULT);
    
    return 0;
}

第 3 课:异步文件操作

3.1 异步文件读取

// 3_async_file_read.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

uv_fs_t open_req;
uv_fs_t read_req;
uv_fs_t close_req;
char buffer[1024];

void on_read(uv_fs_t* req);
void on_open(uv_fs_t* req);
void on_close(uv_fs_t* req);

void on_open(uv_fs_t* req) {
    if (req->result < 0) {
        fprintf(stderr, "Error opening file: %s\n", uv_strerror(req->result));
        return;
    }
    
    printf("File opened successfully, fd: %lld\n", req->result);
    
    uv_fs_read(uv_default_loop(), &read_req, req->result,
               buffer, sizeof(buffer), -1, on_read);
}

void on_read(uv_fs_t* req) {
    if (req->result < 0) {
        fprintf(stderr, "Error reading file: %s\n", uv_strerror(req->result));
    } else if (req->result == 0) {
        // EOF
        uv_fs_close(uv_default_loop(), &close_req, open_req.result, on_close);
    } else {
        // 成功读取
        printf("Read %lld bytes:\n%.*s\n", 
               req->result, (int)req->result, buffer);
        
        // 继续读取
        uv_fs_read(uv_default_loop(), &read_req, open_req.result,
                   buffer, sizeof(buffer), -1, on_read);
    }
}

void on_close(uv_fs_t* req) {
    if (req->result < 0) {
        fprintf(stderr, "Error closing file: %s\n", uv_strerror(req->result));
    } else {
        printf("File closed successfully\n");
    }
    
    uv_fs_req_cleanup(&open_req);
    uv_fs_req_cleanup(&read_req);
    uv_fs_req_cleanup(&close_req);
}

int main(int argc, char** argv) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
        return 1;
    }
    
    uv_loop_t* loop = uv_default_loop();
    
    // 异步打开文件
    uv_fs_open(loop, &open_req, argv[1], O_RDONLY, 0, on_open);
    
    printf("Reading file: %s\n", argv[1]);
    
    uv_run(loop, UV_RUN_DEFAULT);
    
    return 0;
}

3.2 异步文件写入

// 3_async_file_write.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

uv_fs_t open_req;
uv_fs_t write_req;
uv_fs_t close_req;

void on_write(uv_fs_t* req);
void on_open(uv_fs_t* req);
void on_close(uv_fs_t* req);

void on_open(uv_fs_t* req) {
    if (req->result < 0) {
        fprintf(stderr, "Error opening file: %s\n", uv_strerror(req->result));
        return;
    }
    
    printf("File opened for writing, fd: %lld\n", req->result);
    
    const char* data = "Hello, Libuv File System!\nThis is async write.\n";
    uv_buf_t buf = uv_buf_init((char*)data, strlen(data));
    
    uv_fs_write(uv_default_loop(), &write_req, req->result,
                &buf, 1, -1, on_write);
}

void on_write(uv_fs_t* req) {
    if (req->result < 0) {
        fprintf(stderr, "Error writing file: %s\n", uv_strerror(req->result));
    } else {
        printf("Wrote %lld bytes\n", req->result);
    }
    
    uv_fs_close(uv_default_loop(), &close_req, open_req.result, on_close);
}

void on_close(uv_fs_t* req) {
    if (req->result < 0) {
        fprintf(stderr, "Error closing file: %s\n", uv_strerror(req->result));
    } else {
        printf("File closed successfully\n");
    }
    
    uv_fs_req_cleanup(&open_req);
    uv_fs_req_cleanup(&write_req);
    uv_fs_req_cleanup(&close_req);
}

int main(int argc, char** argv) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
        return 1;
    }
    
    uv_loop_t* loop = uv_default_loop();
    
    // 异步打开文件(创建或截断)
    uv_fs_open(loop, &open_req, argv[1], 
               O_WRONLY | O_CREAT | O_TRUNC, 0644, on_open);
    
    printf("Writing to file: %s\n", argv[1]);
    
    uv_run(loop, UV_RUN_DEFAULT);
    
    return 0;
}

编译注意:

gcc -o 3_async_file_write 3_async_file_write.c -luv
./3_async_file_write test.txt
cat test.txt

第 4 课:TCP 服务器

4.1 简单的 Echo 服务器

// 4_tcp_echo_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

#define DEFAULT_PORT 7000
#define DEFAULT_BACKLOG 128

uv_loop_t *loop;
struct sockaddr_in addr;

typedef struct {
    uv_write_t req;
    uv_buf_t buf;
} write_req_t;

void free_write_req(uv_write_t *req) {
    write_req_t *wr = (write_req_t*) req;
    free(wr->buf.base);
    free(wr);
}

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
    buf->base = (char*) malloc(suggested_size);
    buf->len = suggested_size;
}

void echo_write(uv_write_t *req, int status) {
    if (status) {
        fprintf(stderr, "Write error: %s\n", uv_strerror(status));
    }
    free_write_req(req);
}

void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
    if (nread > 0) {
        write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t));
        req->buf = uv_buf_init(buf->base, nread);
        
        // Echo back
        uv_write((uv_write_t*) req, client, &req->buf, 1, echo_write);
        return;
    }
    
    if (nread < 0) {
        if (nread != UV_EOF) {
            fprintf(stderr, "Read error: %s\n", uv_err_name(nread));
        }
        uv_close((uv_handle_t*) client, NULL);
    }
    
    free(buf->base);
}

void on_new_connection(uv_stream_t *server, int status) {
    if (status < 0) {
        fprintf(stderr, "New connection error: %s\n", uv_strerror(status));
        return;
    }
    
    uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
    uv_tcp_init(loop, client);
    
    if (uv_accept(server, (uv_stream_t*) client) == 0) {
        struct sockaddr_in client_addr;
        int addr_len = sizeof(client_addr);
        uv_tcp_getpeername(client, (struct sockaddr*)&client_addr, &addr_len);
        
        printf("New client connected: %s:%d\n", 
               inet_ntoa(client_addr.sin_addr), 
               ntohs(client_addr.sin_port));
        
        uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);
    } else {
        uv_close((uv_handle_t*) client, NULL);
    }
}

int main() {
    loop = uv_default_loop();
    
    uv_tcp_t server;
    uv_tcp_init(loop, &server);
    
    uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr);
    uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
    
    int r = uv_listen((uv_stream_t*) &server, DEFAULT_BACKLOG, on_new_connection);
    if (r) {
        fprintf(stderr, "Listen error: %s\n", uv_strerror(r));
        return 1;
    }
    
    printf("Echo server listening on port %d\n", DEFAULT_PORT);
    printf("Connect using: telnet localhost %d\n", DEFAULT_PORT);
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

4.2 TCP 客户端

// 4_tcp_client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

uv_loop_t *loop;
uv_tcp_t socket;
uv_connect_t connect_req;
uv_write_t write_req;

typedef struct {
    uv_write_t req;
    uv_buf_t buf;
} write_req_t;

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
    buf->base = (char*) malloc(suggested_size);
    buf->len = suggested_size;
}

void on_write(uv_write_t *req, int status) {
    if (status) {
        fprintf(stderr, "Write error: %s\n", uv_strerror(status));
    }
    
    write_req_t *wr = (write_req_t*) req;
    free(wr->buf.base);
    free(wr);
}

void on_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
    if (nread > 0) {
        printf("Received: %.*s", (int)nread, buf->base);
    } else if (nread < 0) {
        if (nread != UV_EOF) {
            fprintf(stderr, "Read error: %s\n", uv_err_name(nread));
        }
        uv_close((uv_handle_t*) stream, NULL);
    }
    
    free(buf->base);
}

void on_connect(uv_connect_t *req, int status) {
    if (status < 0) {
        fprintf(stderr, "Connection error: %s\n", uv_strerror(status));
        return;
    }
    
    printf("Connected to server!\n");
    
    // 发送消息
    write_req_t *wr = (write_req_t*) malloc(sizeof(write_req_t));
    const char *message = "Hello from libuv client!\n";
    wr->buf = uv_buf_init(strdup(message), strlen(message));
    
    uv_write((uv_write_t*) wr, req->handle, &wr->buf, 1, on_write);
    
    // 开始读取响应
    uv_read_start((uv_stream_t*) &socket, alloc_buffer, on_read);
}

void send_message(const char* message) {
    write_req_t *wr = (write_req_t*) malloc(sizeof(write_req_t));
    wr->buf = uv_buf_init(strdup(message), strlen(message));
    uv_write((uv_write_t*) wr, (uv_stream_t*) &socket, &wr->buf, 1, on_write);
}

int main() {
    loop = uv_default_loop();
    
    uv_tcp_init(loop, &socket);
    
    struct sockaddr_in dest;
    uv_ip4_addr("127.0.0.1", 7000, &dest);
    
    uv_tcp_connect(&connect_req, &socket, (const struct sockaddr*)&dest, on_connect);
    
    printf("Connecting to server at 127.0.0.1:7000\n");
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

测试:

# 启动服务器
gcc -o 4_tcp_echo_server 4_tcp_echo_server窗口中
./4_tcp_echo_server &
SERVER_PID=$!

# 启动客户端
gcc -o 4_tcp_client 4_tcp_client.c -luv
./4_tcp_client

# 使用 telnet 测试
telnet localhost 7000

第 5 课:进程和线程

5.1 创建子进程

// 5_child_process.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

uv_process_t child_req;
uv_process_options_t options;
uv_pipe_t pipe;

void on_exit(uv_process_t *req, int64_t exit_status, int term_signal) {
    printf("Process exited with status %lld, signal %d\n", exit_status, term_signal);
    uv_close((uv_handle_t*) req, NULL);
}

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
    buf->base = (char*) malloc(suggested_size);
    buf->len = suggested_size;
}

void read_stdout(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
    if (nread > 0) {
        printf("Child output: %.*s", (int)nread, buf->base);
    } else if (nread < 0) {
        uv_close((uv_handle_t*) stream, NULL);
    }
    free(buf->base);
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    
    char* args[3];
    args[0] = "ls";
    args[1] = "-la";
    args[2] = NULL;
    
    uv_pipe_init(loop, &pipe, 0);
    
    uv_stdio_container_t child_stdio[3];
    child_stdio[0].flags = UV_IGNORE;
    child_stdio[1].flags = (uv_stdio_flags)(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
    child_stdio[1].data.stream = (uv_stream_t*)&pipe;
    child_stdio[2].flags = UV_IGNORE;
    
    options.exit_cb = on_exit;
    options.file = args[0];
    options.args = args;
    options.stdio_count = 3;
    options.stdio = child_stdio;
    options.flags = UV_PROCESS_DETACHED;
    
    printf("Starting child process: %s %s\n", args[0], args[1]);
    
    int r = uv_spawn(loop, &child_req, &options);
    if (r) {
        fprintf(stderr, "Error spawning process: %s\n", uv_strerror(r));
        return 1;
    }
    
    printf("Child process started, pid: %d\n", child_req.pid);
    
    // 读取子进程输出
    uv_read_start((uv_stream_t*)&pipe, alloc_buffer, read_stdout);
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

5.2 工作线程

// 5_work_thread.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

#define FIB_UNTIL 10

uv_loop_t *loop;
uv_work_t fib_reqs[FIB_UNTIL];

long fib_(long n) {
    if (n == 0 || n == 1) return 1;
    return fib_(n-1) + fib_(n-2);
}

void fib(uv_work_t *req) {
    int n = *(int*) req->data;
    if (random() % 2) {
        // 模拟耗时计算
        uv_sleep(100);
    } else {
        uv_sleep(1000);
    }
    
    long fib = fib_(n);
    fprintf(stderr, "%dth fibonacci is %lu\n", n, fib);
}

void after_fib(uv_work_t *req, int status) {
    if (status == UV_ECANCELED) {
        fprintf(stderr, "Calculation of %dth fibonacci cancelled\n", *(int*)req->data);
    } else {
        fprintf(stderr, "Done calculating %dth fibonacci\n", *(int*)req->data);
    }
}

void signal_handler(uv_signal_t *handle, int signum) {
    printf("Signal received: %d\n", signum);
    int i;
    for (i = 0; i < FIB_UNTIL; i++) {
        uv_cancel((uv_req_t*) &fib_reqs[i]);
    }
    uv_signal_stop(handle);
}

int main() {
    loop = uv_default_loop();
    
    int data[FIB_UNTIL];
    int i;
    for (i = 0; i < FIB_UNTIL; i++) {
        data[i] = i;
        fib_reqs[i].data = (void*) &data[i];
        
        // 在工作线程中执行计算
        uv_queue_work(loop, &fib_reqs[i], fib, after_fib);
    }
    
    // 设置信号处理器
    uv_signal_t sig;
    uv_signal_init(loop, &sig);
    uv_signal_start(&sig, signal_handler, SIGINT);
    
    printf("Calculating first %d fibonacci numbers in thread pool\n", FIB_UNTIL);
    printf("Press Ctrl+C to cancel\n");
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

🚀 阶段 2:中级进阶

第 6 课:高级网络编程

6.1 异步 DNS 解析

// 6_async_dns.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

uv_loop_t *loop;
uv_getaddrinfo_t resolver;

void on_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) {
    if (status < 0) {
        fprintf(stderr, "getaddrinfo error: %s\n", uv_strerror(status));
        return;
    }
    
    char addr[17] = {'\0'};
    
    struct addrinfo *p = res;
    printf("DNS resolved for: %s\n", (char*)resolver->data);
    
    int i = 1;
    for (p = res; p != NULL; p = p->ai_next) {
        if (p->ai_family == AF_INET) {
            struct sockaddr_in *addr_in = (struct sockaddr_in*)p->ai_addr;
            uv_ip4_name(addr_in, addr, sizeof(addr));
            printf("  %d. IPv4: %s\n", i++, addr);
        } else if (p->ai_family == AF_INET6) {
            struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6*)p->ai_addr;
            uv_ip6_name(addr_in6, addr, sizeof(addr));
            printf("  %d. IPv6: %s\n", i++, addr);
        }
    }
    
    uv_freeaddrinfo(res);
    free(resolver->data);
}

void resolve_host(const char *hostname) {
    struct addrinfo hints;
    
    hints.ai_family = PF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = 0;
    
    resolver.data = (void*)strdup(hostname);
    
    printf("Resolving %s...\n", hostname);
    
    int r = uv_getaddrinfo(loop, &resolver, on_resolved, hostname, NULL, &hints);
    if (r) {
        fprintf(stderr, "getaddrinfo call error: %s\n", uv_strerror(r));
    }
}

int main() {
    loop = uv_default_loop();
    
    // 解析多个主机名
    resolve_host("www.google.com");
    resolve_host("www.github.com");
    resolve_host("localhost");
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

6.2 UDP 服务器

// 6_udp_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

#define DEFAULT_PORT 8888

uv_udp_t server;
struct sockaddr_in addr;

void on_send(uv_udp_send_t *req, int status) {
    if (status) {
        fprintf(stderr, "Send error: %s\n", uv_strerror(status));
    }
    free(req);
}

void on_read(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, 
             const struct sockaddr *addr, unsigned flags) {
    if (nread < 0) {
        fprintf(stderr, "Read error: %s\n", uv_err_name(nread));
        uv_close((uv_handle_t*) handle, NULL);
        free(buf->base);
        return;
    }
    
    if (nread > 0) {
        char sender[17] = {0};
        if (addr->sa_family == AF_INET) {
            uv_ip4_name((struct sockaddr_in*)addr, sender, sizeof(sender));
        } else if (addr->sa_family == AF_INET6) {
            uv_ip6_name((struct sockaddr_in6*)addr, sender, sizeof(sender));
        }
        
        printf("Received from %s: %.*s\n", sender, (int)nread, buf->base);
        
        // Echo back
        uv_udp_send_t *req = malloc(sizeof(uv_udp_send_t));
        uv_buf_t wbuf = uv_buf_init(buf->base, nread);
        
        struct sockaddr_in client_addr;
        memcpy(&client_addr, addr, sizeof(struct sockaddr_in));
        
        uv_udp_send(req, handle, &wbuf, 1, 
                   (const struct sockaddr*)&client_addr, on_send);
    }
    
    free(buf->base);
}

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
    buf->base = malloc(suggested_size);
    buf->len = suggested_size;
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    
    uv_udp_init(loop, &server);
    uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr);
    uv_udp_bind(&server, (const struct sockaddr*)&addr, 0);
    
    printf("UDP server listening on port %d\n", DEFAULT_PORT);
    
    // 加入多播组
    // uv_udp_set_broadcast(&server, 1);
    // uv_udp_set_multicast_loop(&server, 1);
    // uv_udp_set_multicast_ttl(&server, 32);
    
    int r = uv_udp_recv_start(&server, alloc_buffer, on_read);
    if (r) {
        fprintf(stderr, "Recv start error: %s\n", uv_strerror(r));
        return 1;
    }
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

第 7 课:信号和进程间通信

7.1 信号处理

// 7_signal_handler.c
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>

uv_loop_t *loop;
uv_signal_t sigint, sigterm, sigusr1;

void signal_callback(uv_signal_t *handle, int signum) {
    const char* signame = "";
    
    switch(signum) {
        case SIGINT:  signame = "SIGINT"; break;
        case SIGTERM: signame = "SIGTERM"; break;
        case SIGUSR1: signame = "SIGUSR1"; break;
        case SIGUSR2: signame = "SIGUSR2"; break;
        default:      signame = "UNKNOWN"; break;
    }
    
    printf("Signal received: %s (%d)\n", signame, signum);
    
    if (signum == SIGINT || signum == SIGTERM) {
        printf("Shutting down...\n");
        
        uv_signal_stop(&sigint);
        uv_signal_stop(&sigterm);
        uv_signal_stop(&sigusr1);
        
        // 停止事件循环
        uv_stop(loop);
    }
}

void timer_callback(uv_timer_t* handle) {
    static int count = 0;
    printf("Timer tick: %d\n", ++count);
}

int main() {
    loop = uv_default_loop();
    
    // 设置信号处理器
    uv_signal_init(loop, &sigint);
    uv_signal_start(&sigint, signal_callback, SIGINT);
    
    uv_signal_init(loop, &sigterm);
    uv_signal_start(&sigterm, signal_callback, SIGTERM);
    
    uv_signal_init(loop, &sigusr1);
    uv_signal_start(&sigusr1, signal_callback, SIGUSR1);
    
    // 发送 SIGUSR1 给自己
    printf("Process PID: %d\n", getpid());
    printf("Try sending signals:\n");
    printf("  kill -USR1 %d\n", getpid());
    printf("  kill -INT %d (Ctrl+C)\n", getpid());
    printf("  kill -TERM %d\n", getpid());
    
    // 添加一个定时器显示程序在运行
    uv_timer_t timer;
    uv_timer_init(loop, &timer);
    uv_timer_start(&timer, timer_callback, 1000, 1000);
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

测试:

gcc -o 7_signal_handler 7_signal_handler.c -luv
./7_signal_handler &
PID=$!

# 发送信号
kill -USR1 $PID
kill -INT $PID

7.2 命名管道(Named Pipe)通信

// 7_named_pipe.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

#ifdef _WIN32
# define PIPENAME "\\\\?\\pipe\\mypipe"
#else
# define PIPENAME "/tmp/mypipe.sock"
#endif

uv_pipe_t pipe_server;
uv_pipe_t pipe_client;

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
    buf->base = malloc(suggested_size);
    buf->len = suggested_size;
}

void on_client_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
    if (nread > 0) {
        printf("Server received: %.*s\n", (int)nread, buf->base);
        
        // Echo back
        uv_write_t *req = malloc(sizeof(uv_write_t));
        uv_write(req, client, buf, 1, NULL);
    } else if (nread < 0) {
        if (nread != UV_EOF) {
            fprintf(stderr, "Read error: %s\n", uv_err_name(nread));
        }
        uv_close((uv_handle_t*) client, NULL);
    }
    
    free(buf->base);
}

void on_new_connection(uv_stream_t *server, int status) {
    if (status < 0) {
        fprintf(stderr, "New connection error: %s\n", uv_strerror(status));
        return;
    }
    
    printf("New client connected to pipe\n");
    
    uv_pipe_init(uv_default_loop(), &pipe_client, 0);
    
    if (uv_accept(server, (uv_stream_t*)&pipe_client) == 0) {
        uv_read_start((uv_stream_t*)&pipe_client, alloc_buffer, on_client_read);
    } else {
        uv_close((uv_handle_t*)&pipe_client, NULL);
    }
}

void pipe_write(const char* message) {
    uv_write_t *req = malloc(sizeof(uv_write_t));
    uv_buf_t buf = uv_buf_init((char*)message, strlen(message));
    uv_write(req, (uv_stream_t*)&pipe_client, &buf, 1, NULL);
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    
    // 删除已存在的管道
    uv_fs_t unlink_req;
    uv_fs_unlink(loop, &unlink_req, PIPENAME, NULL);
    
    // 创建命名管道服务器
    uv_pipe_init(loop, &pipe_server, 0);
    
    int r = uv_pipe_bind(&pipe_server, PIPENAME);
    if (r) {
        fprintf(stderr, "Bind error: %s\n", uv_strerror(r));
        return 1;
    }
    
    r = uv_listen((uv_stream_t*)&pipe_server, 128, on_new_connection);
    if (r) {
        fprintf(stderr, "Listen error: %s\n", uv_strerror(r));
        return 1;
    }
    
    printf("Named pipe server listening on: %s\n", PIPENAME);
    printf("Connect using: echo 'hello' | nc -U %s\n", PIPENAME);
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

第 8 课:高级 I/O 操作

8.1 轮询(Polling)文件描述符

// 8_poll_example.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <uv.h>

uv_poll_t poll_handle;
int fd;

void poll_callback(uv_poll_t* handle, int status, int events) {
    if (status < 0) {
        fprintf(stderr, "Poll error: %s\n", uv_strerror(status));
        return;
    }
    
    if (events & UV_READABLE) {
        printf("File descriptor is readable\n");
        
        char buffer[1024];
        ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
        if (n > 0) {
            buffer[n] = '\0';
            printf("Read: %s\n", buffer);
        }
    }
    
    if (events & UV_WRITABLE) {
        printf("File descriptor is writable\n");
    }
    
    if (events & UV_DISCONNECT) {
        printf("File descriptor disconnected\n");
        uv_poll_stop(handle);
    }
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    
    // 创建一个管道用于测试
    int pipefd[2];
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return 1;
    }
    
    fd = pipefd[0];  // 读取端
    
    // 启动轮询
    uv_poll_init(loop, &poll_handle, fd);
    uv_poll_start(&poll_handle, UV_READABLE, poll_callback);
    
    printf("Polling file descriptor %d for readability\n", fd);
    printf("Write to the pipe: echo 'test' >&%d\n", pipefd[1]);
    
    // 在另一个线程中写入数据
    uv_thread_t thread;
    uv_thread_create(&thread, NULL, NULL, NULL);
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

8.2 异步文件系统监控

// 8_fs_watcher.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

uv_fs_event_t fs_event;
const char* path = ".";

void fs_event_callback(uv_fs_event_t* handle, const char* filename, 
                       int events, int status) {
    if (status < 0) {
        fprintf(stderr, "Error: %s\n", uv_strerror(status));
        return;
    }
    
    printf("Change detected in: %s\n", handle->path);
    
    if (filename) {
        printf("File: %s\n", filename);
    } else {
        printf("Filename not provided\n");
    }
    
    if (events & UV_RENAME) {
        printf("Event: RENAME\n");
    }
    if (events & UV_CHANGE) {
        printf("Event: CHANGE\n");
    }
    
    printf("\n");
}

int main(int argc, char** argv) {
    uv_loop_t *loop = uv_default_loop();
    
    uv_fs_event_init(loop, &fs_event);
    
    const char* watch_path = (argc > 1) ? argv[1] : path;
    
    int r = uv_fs_event_start(&fs_event, fs_event_callback, 
                              watch_path, UV_FS_EVENT_RECURSIVE);
    
    if (r) {
        fprintf(stderr, "Error starting fs event: %s\n", uv_strerror(r));
        return 1;
    }
    
    printf("Watching directory: %s\n", watch_path);
    printf("Events to watch:\n");
    printf("  UV_RENAME: File renamed\n");
    printf("  UV_CHANGE: File changed\n");
    printf("\nTry creating/modifying files in this directory\n");
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

🎯 阶段 3:高级应用

第 9 课:构建 HTTP 服务器

9.1 简单的 HTTP 服务器

// 9_http_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

#define HTTP_PORT 8080
#define RESPONSE_HEADER "HTTP/1.1 200 OK\r\n" \
                        "Content-Type: text/html\r\n" \
                        "Connection: close\r\n" \
                        "\r\n"
#define RESPONSE_BODY "<html><body><h1>Hello from libuv HTTP server!</h1></body></html>"

typedef struct {
    uv_write_t req;
    uv_buf_t buf;
} write_req_t;

void free_write_req(uv_write_t *req) {
    write_req_t *wr = (write_req_t*) req;
    free(wr->buf.base);
    free(wr);
}

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
    buf->base = malloc(suggested_size);
    buf->len = suggested_size;
}

void on_write(uv_write_t *req, int status) {
    if (status) {
        fprintf(stderr, "Write error: %s\n", uv_strerror(status));
    }
    
    // 关闭连接
    uv_close((uv_handle_t*)req->handle, NULL);
    free_write_req(req);
}

void send_response(uv_stream_t *client) {
    write_req_t *req = malloc(sizeof(write_req_t));
    
    // 构建响应
    const char* header = RESPONSE_HEADER;
    const char* body = RESPONSE_BODY;
    
    size_t total_len = strlen(header) + strlen(body);
    char* response = malloc(total_len + 1);
    strcpy(response, header);
    strcat(response, body);
    
    req->buf = uv_buf_init(response, total_len);
    
    uv_write((uv_write_t*) req, client, &req->buf, 1, on_write);
}

void on_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
    if (nread > 0) {
        // 解析HTTP请求
        printf("Received request:\n%.*s\n", (int)nread, buf->base);
        
        // 发送响应
        send_response(client);
    } else if (nread < 0) {
        if (nread != UV_EOF) {
            fprintf(stderr, "Read error: %s\n", uv_err_name(nread));
        }
        uv_close((uv_handle_t*) client, NULL);
    }
    
    free(buf->base);
}

void on_new_connection(uv_stream_t *server, int status) {
    if (status < 0) {
        fprintf(stderr, "New connection error: %s\n", uv_strerror(status));
        return;
    }
    
    uv_tcp_t *client = malloc(sizeof(uv_tcp_t));
    uv_tcp_init(uv_default_loop(), client);
    
    if (uv_accept(server, (uv_stream_t*) client) == 0) {
        struct sockaddr_in addr;
        int addr_len = sizeof(addr);
        uv_tcp_getpeername(client, (struct sockaddr*)&addr, &addr_len);
        
        char ip[INET_ADDRSTRLEN];
        uv_ip4_name(&addr, ip, sizeof(ip));
        printf("New HTTP connection from %s:%d\n", 
               ip, ntohs(addr.sin_port));
        
        uv_read_start((uv_stream_t*) client, alloc_buffer, on_read);
    } else {
        uv_close((uv_handle_t*) client, NULL);
    }
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    
    uv_tcp_t server;
    uv_tcp_init(loop, &server);
    
    struct sockaddr_in addr;
    uv_ip4_addr("0.0.0.0", HTTP_PORT, &addr);
    uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
    
    int r = uv_listen((uv_stream_t*) &server, 128, on_new_connection);
    if (r) {
        fprintf(stderr, "Listen error: %s\n", uv_strerror(r));
        return 1;
    }
    
    printf("HTTP server running on http://localhost:%d\n", HTTP_PORT);
    printf("Open in browser or use: curl http://localhost:%d\n", HTTP_PORT);
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

测试:

gcc -o 9_http_server 9_http_server.c -luv
./9_http_server &
curl http://localhost:8080

第 10 课:构建完整的聊天服务器

10.1 多客户端聊天室

// 10_chat_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

#define PORT 8888
#define MAX_CLIENTS 1000
#define BUFFER_SIZE 4096

typedef struct {
    uv_tcp_t handle;
    uv_write_t write_req;
    char name[32];
    char buffer[BUFFER_SIZE];
    int buffer_len;
} client_t;

uv_loop_t *loop;
uv_tcp_t server;
client_t *clients[MAX_CLIENTS];
int client_count = 0;

void broadcast(const char* message, int len, client_t* exclude);
void remove_client(client_t* client);

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
    buf->base = malloc(suggested_size);
    buf->len = suggested_size;
}

void on_write_end(uv_write_t *req, int status) {
    if (status) {
        fprintf(stderr, "Write error: %s\n", uv_strerror(status));
    }
    free(req);
}

void send_message(client_t* client, const char* message, int len) {
    uv_write_t *req = malloc(sizeof(uv_write_t));
    uv_buf_t buf = uv_buf_init((char*)message, len);
    uv_write(req, (uv_stream_t*)&client->handle, &buf, 1, on_write_end);
}

void handle_command(client_t* client, const char* command) {
    if (strncmp(command, "/name ", 6) == 0) {
        char old_name[32];
        strcpy(old_name, client->name);
        
        strncpy(client->name, command + 6, sizeof(client->name) - 1);
        client->name[sizeof(client->name) - 1] = '\0';
        
        char msg[256];
        int len = snprintf(msg, sizeof(msg), 
                          "[Server] %s changed name to %s\n", 
                          old_name, client->name);
        broadcast(msg, len, NULL);
    } else if (strcmp(command, "/list") == 0) {
        char msg[512] = "[Server] Online users: ";
        for (int i = 0; i < client_count; i++) {
            if (clients[i]) {
                strcat(msg, clients[i]->name);
                strcat(msg, " ");
            }
        }
        strcat(msg, "\n");
        send_message(client, msg, strlen(msg));
    } else {
        char msg[128];
        int len = snprintf(msg, sizeof(msg), 
                          "[Server] Unknown command. Available: /name <newname>, /list\n");
        send_message(client, msg, len);
    }
}

void on_client_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
    client_t* client = (client_t*)stream;
    
    if (nread > 0) {
        // 确保以null结尾
        if (nread >= BUFFER_SIZE) nread = BUFFER_SIZE - 1;
        buf->base[nread] = '\0';
        
        // 处理消息
        char* msg = buf->base;
        if (msg[nread-1] == '\n') msg[nread-1] = '\0';
        if (msg[nread-2] == '\r') msg[nread-2] = '\0';
        
        printf("[%s]: %s\n", client->name, msg);
        
        // 处理命令
        if (msg[0] == '/') {
            handle_command(client, msg);
        } else {
            // 广播消息
            char broadcast_msg[BUFFER_SIZE + 50];
            int len = snprintf(broadcast_msg, sizeof(broadcast_msg),
                              "[%s]: %s\n", client->name, msg);
            broadcast(broadcast_msg, len, client);
        }
    } else if (nread < 0) {
        if (nread != UV_EOF) {
            fprintf(stderr, "Read error: %s\n", uv_err_name(nread));
        }
        remove_client(client);
    }
    
    free(buf->base);
}

void broadcast(const char* message, int len, client_t* exclude) {
    for (int i = 0; i < client_count; i++) {
        if (clients[i] && clients[i] != exclude) {
            send_message(clients[i], message, len);
        }
    }
}

void remove_client(client_t* client) {
    // 通知其他用户
    char msg[128];
    int len = snprintf(msg, sizeof(msg), 
                      "[Server] %s has left the chat\n", client->name);
    broadcast(msg, len, client);
    
    // 从客户端列表移除
    for (int i = 0; i < client_count; i++) {
        if (clients[i] == client) {
            clients[i] = NULL;
            break;
        }
    }
    
    // 关闭连接
    uv_close((uv_handle_t*)&client->handle, (uv_close_cb)free);
    
    printf("Client disconnected: %s\n", client->name);
}

void on_new_connection(uv_stream_t *server, int status) {
    if (status < 0) {
        fprintf(stderr, "New connection error: %s\n", uv_strerror(status));
        return;
    }
    
    // 查找空闲位置
    int index = -1;
    for (int i = 0; i < MAX_CLIENTS; i++) {
        if (!clients[i]) {
            index = i;
            break;
        }
    }
    
    if (index == -1) {
        fprintf(stderr, "Max clients reached\n");
        return;
    }
    
    // 创建新客户端
    client_t* client = malloc(sizeof(client_t));
    uv_tcp_init(loop, &client->handle);
    snprintf(client->name, sizeof(client->name), "Guest%d", index + 1);
    client->buffer_len = 0;
    
    if (uv_accept(server, (uv_stream_t*)&client->handle) == 0) {
        clients[index] = client;
        if (index >= client_count) client_count = index + 1;
        
        // 获取客户端地址
        struct sockaddr_in addr;
        int addr_len = sizeof(addr);
        uv_tcp_getpeername(&client->handle, (struct sockaddr*)&addr, &addr_len);
        
        char ip[INET_ADDRSTRLEN];
        uv_ip4_name(&addr, ip, sizeof(ip));
        
        printf("New client connected: %s from %s:%d\n", 
               client->name, ip, ntohs(addr.sin_port));
        
        // 发送欢迎消息
        char welcome[256];
        int len = snprintf(welcome, sizeof(welcome),
                          "[Server] Welcome %s! There are %d users online.\n"
                          "Commands: /name <newname>, /list\n",
                          client->name, client_count);
        send_message(client, welcome, len);
        
        // 通知其他用户
        char join_msg[128];
        len = snprintf(join_msg, sizeof(join_msg),
                      "[Server] %s has joined the chat\n", client->name);
        broadcast(join_msg, len, client);
        
        // 开始读取
        uv_read_start((uv_stream_t*)&client->handle, alloc_buffer, on_client_read);
    } else {
        uv_close((uv_handle_t*)&client->handle, (uv_close_cb)free);
    }
}

int main() {
    loop = uv_default_loop();
    
    // 初始化客户端数组
    memset(clients, 0, sizeof(clients));
    
    // 创建TCP服务器
    uv_tcp_init(loop, &server);
    
    struct sockaddr_in addr;
    uv_ip4_addr("0.0.0.0", PORT, &addr);
    uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
    
    int r = uv_listen((uv_stream_t*)&server, 128, on_new_connection);
    if (r) {
        fprintf(stderr, "Listen error: %s\n", uv_strerror(r));
        return 1;
    }
    
    printf("Chat server started on port %d\n", PORT);
    printf("Connect using: telnet localhost %d\n", PORT);
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

编译和测试:

gcc -o 10_chat_server 10_chat_server.c -luv
./10_chat_server &

# 在多个终端连接测试
telnet localhost 8888

📁 构建系统

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(libuv_tutorial)

# 查找 libuv
find_package(libuv REQUIRED)

# 设置编译选项
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

# 添加所有示例
set(EXAMPLES
    1_hello_libuv
    1_loop_lifecycle
    2_timer_once
    2_multi_timers
    3_async_file_read
    3_async_file_write
    4_tcp_echo_server
    4_tcp_client
    5_child_process
    5_work_thread
    6_async_dns
    6_udp_server
    7_signal_handler
    7_named_pipe
    8_poll_example
    8_fs_watcher
    9_http_server
    10_chat_server
)

# 为每个示例创建可执行文件
foreach(example ${EXAMPLES})
    add_executable(${example} ${example}.c)
    target_link_libraries(${example} PRIVATE libuv::uv)
endforeach()

# 创建构建所有示例的目标
add_custom_target(all_examples DEPENDS ${EXAMPLES})

构建脚本

#!/bin/bash
# build.sh

# 创建构建目录
mkdir -p build
cd build

# 配置
cmake ..

# 构建所有示例
make -j$(nproc) all_examples

# 列出所有构建的程序
echo "Built examples:"
ls -1  *_* 2>/dev/null | grep -v "\." || find . -maxdepth 1 -type f -executable

📚 练习项目

项目 1:文件同步工具

实现一个监控目录变化并同步到备份目录的工具。

项目 2:HTTP 代理服务器

实现一个支持缓存的 HTTP 代理服务器。

项目 3:实时数据推送服务

实现 WebSocket 服务器推送实时数据。

项目 4:分布式任务队列

实现基于网络的任务分发和执行系统。


🔧 调试技巧

1. 启用调试输出

#define DEBUG 1

#ifdef DEBUG
#define DEBUG_PRINT(fmt, ...) \
    fprintf(stderr, "DEBUG: %s:%d:%s(): " fmt, \
            __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...)
#endif

2. 错误处理包装

#define UV_CHECK(call) \
    do { \
        int result = (call); \
        if (result < 0) { \
            fprintf(stderr, "Error at %s:%d: %s (%s)\n", \
                    __FILE__, __LINE__, uv_strerror(result), uv_err_name(result)); \
            exit(1); \
        } \
    } while(0)

// 使用
UV_CHECK(uv_listen((uv_stream_t*)&server, 128, on_new_connection));

3. 内存调试

#ifdef MEMORY_DEBUG
void* my_malloc(size_t size, const char* file, int line) {
    void* ptr = malloc(size);
    printf("malloc(%zu) at %s:%d -> %p\n", size, file, line, ptr);
    return ptr;
}

void my_free(void* ptr, const char* file, int line) {
    printf("free at %s:%d -> %p\n", file, line, ptr);
    free(ptr);
}

#define malloc(size) my_malloc(size, __FILE__, __LINE__)
#define free(ptr) my_free(ptr, __FILE__, __LINE__)
#endif

📈 性能优化建议

  1. 重用句柄:不要频繁创建/销毁句柄
  2. 缓冲区管理:使用内存池
  3. 连接池:复用 TCP 连接
  4. 批量操作:合并小文件操作
  5. 合适的线程数:根据 CPU 核心数设置

🎉 恭喜!

完成本教程后,你应该能够:

✅ 理解 libuv 的事件循环模型
✅ 使用 libuv 进行异步 I/O 操作
✅ 构建 TCP/UDP 服务器
✅ 处理文件和进程
✅ 实现高性能网络应用
✅ 调试和优化 libuv 程序

下一步学习建议

  1. 阅读 http://docs.libuv.org/
  2. 研究 Node.js 源码了解实际应用
  3. 学习 libuv 的内部实现原理
  4. 参与开源项目,实践所学知识

记住:实践是最好的老师!尝试修改示例代码,添加新功能,解决实际问题。

posted @ 2025-12-25 17:00  AngDH  阅读(45)  评论(0)    收藏  举报