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
📈 性能优化建议
- 重用句柄:不要频繁创建/销毁句柄
- 缓冲区管理:使用内存池
- 连接池:复用 TCP 连接
- 批量操作:合并小文件操作
- 合适的线程数:根据 CPU 核心数设置
🎉 恭喜!
完成本教程后,你应该能够:
✅ 理解 libuv 的事件循环模型
✅ 使用 libuv 进行异步 I/O 操作
✅ 构建 TCP/UDP 服务器
✅ 处理文件和进程
✅ 实现高性能网络应用
✅ 调试和优化 libuv 程序
下一步学习建议:
- 阅读 http://docs.libuv.org/
- 研究 Node.js 源码了解实际应用
- 学习 libuv 的内部实现原理
- 参与开源项目,实践所学知识
记住:实践是最好的老师!尝试修改示例代码,添加新功能,解决实际问题。

浙公网安备 33010602011771号