libuv介绍

82 阅读5分钟

1.简介

libuv是一个跨平台的支持事件驱动的异步I/O的库,使开发者可以以非阻塞的方式执行文件I/O操作、网络通信、子进程管理等。

libuv的主要特点包括:

  • 事件循环:libuv有一个基于事件循环的模型,它不断地轮询事件,并在事件发生时调用相应的回调函数。
  • 异步I/O:libuv提供了异步文件I/O和网络I/O的接口,使得开发者可以执行I/O操作而不阻塞主线程。
  • 线程池:libuv使用线程池来处理一些不能以非阻塞方式执行的I/O操作,如文件系统操作在某些操作系统上。
  • DNS解析:libuv提供了异步DNS解析的接口。
  • 高分辨率时钟:libuv提供了高精度的时间测量接口。

libuv的使用通常涉及以下几个步骤:

  • 初始化:使用uv_loop_init初始化事件循环。
  • 创建句柄:根据需要创建相应的句柄,如TCP句柄、UDP句柄等。
  • 启动事件循环:使用uv_run启动事件循环。
  • 关闭句柄:在不再需要句柄时,使用uv_close关闭句柄。
  • 清理资源:在程序结束时,使用uv_loop_close清理事件循环。

2.常用接口介绍

uv_loop_t - 事件循环

  • uv_loop_init(uv_loop_t*):初始化一个事件循环。
  • uv_run(uv_loop_t*,uv_run_mode):开始运行事件循环。uv_run_mode 可以是 UV_RUN_DEFAULT、UV_RUN_ONCE 或UV_RUN_NOWAIT。
  • uv_loop_close(uv_loop_t*):关闭事件循环并释放相关资源。

uv_handle_t - 句柄基类

  • uv_handle_size(uv_handle_type):返回特定类型句柄的大小。
  • uv_close(uv_handle_t*, uv_close_cb):关闭一个句柄并释放资源。当句柄关闭完成后,会调用 uv_close_cb 回调函数。

uv_tcp_t - TCP 句柄

  • uv_tcp_init(uv_loop_t*, uv_tcp_t*):初始化一个 TCP 句柄。
  • uv_tcp_bind(uv_tcp_t*, const struct sockaddr*, unsigned int):将 TCP句柄绑定到指定的地址和端口。
  • uv_tcp_connect(uv_connect_t*, uv_tcp_t*, const struct sockaddr*, uv_connect_cb):异步连接到服务器。连接成功或失败时会调用 uv_connect_cb 回调函数。

uv_udp_t - UDP 句柄

  • uv_udp_init(uv_loop_t*, uv_udp_t*):初始化一个 UDP 句柄。
  • uv_udp_bind(uv_udp_t*, const struct sockaddr*, unsigned int):将 UDP句柄绑定到指定的地址端口。
  • uv_udp_recv_start(uv_udp_t*, uv_alloc_cb,uv_udp_recv_cb):开始接收 UDP 数据。uv_alloc_cb 用于分配接收缓冲区,uv_udp_recv_cb用于处理接收到的数据。

uv_timer_t - 定时器

  • uv_timer_init(uv_loop_t*, uv_timer_t*):初始化一个定时器。
  • uv_timer_start(uv_timer_t*, uv_timer_cb, uint64_t,uint64_t):启动定时器。uv_timer_cb 是定时器超时时的回调函数,uint64_t 参数指定第一次超时时间和重复间隔。
  • uv_timer_stop(uv_timer_t*):停止定时器。

uv_work_t - 工作线程

  • uv_queue_work(uv_loop_t*, uv_work_t*, uv_work_cb, uv_after_work_cb):将工作推送到 libuv 的线程池中执行。uv_work_cb 是在线程池中执行的工作函数,uv_after_work_cb 是工作完成后在事件循环线程中调用的回调函数。

uv_process_t - 进程

  • uv_spawn(uv_loop_t*, uv_process_t*, constuv_process_options_t*):创建一个新进程。
  • uv_process_kill(uv_process_t*, int):发送信号到进程。

uv_fs_t - 文件系统操作

  • uv_fs_open(uv_loop_t*, uv_fs_t*, const char*, int, int,uv_fs_cb):异步打开文件。
  • uv_fs_read(uv_loop_t*, uv_fs_t*, uv_file, const uv_buf_t*, unsigned int, int64_t, uv_fs_cb):异步读取文件。
  • uv_fs_write(uv_loop_t*, uv_fs_t*, uv_file, const uv_buf_t*, unsigned int, int64_t, uv_fs_cb):异步写入文件。

3.环境搭建

下载地址:github.com/libuv/libuv 下载完成,进行解压,然后使用cmake编译。 configure->Generate->Open Project 在这里插入图片描述 生成库如下图所示: 在这里插入图片描述 拷贝头文件和lib、dll目录到demo程序,然后配置visual sudio环境。具体步骤请看前面文章配置。

4.示例

TCP服务端:

#include <iostream>
#include <string>
extern "C"
{
#include "uv.h"
}


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

void on_read(uv_stream_t* client, ssize_t nread, const uv_buf_t* buf) {
	if (nread < 0) {
		// 如果读取错误或连接已关闭,释放内存并关闭客户端
		uv_close((uv_handle_t*)client, NULL);
		free(buf->base);
		return;
	}

	std::string message = "Hello, World!\n";
	uv_write_t* req = (uv_write_t*)malloc(sizeof(uv_write_t));
	uv_buf_t wrbuf = uv_buf_init((char*)message.c_str(), message.length());
	uv_write(req, client, &wrbuf, 1, [](uv_write_t* req, int status) {
		free(req);
		if (status < 0) {
			std::cerr << "Write error: " << uv_strerror(status) << std::endl;
		}
	});

	// 释放内存
	free(buf->base);
}

void on_new_connection(uv_stream_t* server, int status) {
	if (status < 0) {
		std::cerr << "New connection error: " << uv_strerror(status) << std::endl;
		return;
	}

	uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
	uv_tcp_init(uv_default_loop(), client);
	if (uv_accept(server, (uv_stream_t*)client) == 0) {
		uv_read_start((uv_stream_t*)client, on_alloc, on_read);
	}
	else {
		uv_close((uv_handle_t*)client, NULL);
	}
}

int main() 
{
	uv_tcp_t server;
	uv_tcp_init(uv_default_loop(), &server);

	struct sockaddr_in addr;
	uv_ip4_addr("0.0.0.0", 8080, &addr);

	uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
	int r = uv_listen((uv_stream_t*)&server, 128, on_new_connection);
	if (r) {
		std::cerr << "Listen error: " << uv_strerror(r) << std::endl;
		return 1;
	}

	std::cout << "Listening on port 8080..." << std::endl;
	uv_run(uv_default_loop(), UV_RUN_DEFAULT);

	return 0;
}

TCP客户端:

void on_connect(uv_connect_t* req, int status) {
	if (status < 0) {
		std::cerr << "Connect error: " << uv_strerror(status) << std::endl;
		return;
	}

	// 连接成功,发送数据
	uv_stream_t* stream = req->handle;
	std::string message = "Hello, Server!\n";
	uv_write_t* write_req = (uv_write_t*)malloc(sizeof(uv_write_t));
	uv_buf_t buf = uv_buf_init((char*)message.c_str(), message.length());
	uv_write(write_req, stream, &buf, 1, [](uv_write_t* req, int status) {
		free(req);
		if (status < 0) {
			std::cerr << "Write error: " << uv_strerror(status) << std::endl;
		}
	});
}

void on_read(uv_stream_t* client, ssize_t nread, const uv_buf_t* buf) {
	if (nread < 0) {
		// 如果读取错误或连接已关闭,释放内存并关闭客户端
		uv_close((uv_handle_t*)client, NULL);
		free(buf->base);
		return;
	}

	// 打印接收到的数据
	std::cout.write(buf->base, nread);

	// 释放内存
	free(buf->base);
}

int main() {
	uv_tcp_t* socket = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
	uv_tcp_init(uv_default_loop(), socket);

	struct sockaddr_in dest;
	uv_ip4_addr("127.0.0.1", 8080, &dest);

	uv_connect_t* connect_req = (uv_connect_t*)malloc(sizeof(uv_connect_t));
	uv_tcp_connect(connect_req, socket, (const struct sockaddr*)&dest, on_connect);

	// 开始读取数据
	uv_read_start((uv_stream_t*)socket, [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
		buf->base = (char*)malloc(suggested_size);
		buf->len = suggested_size;
	}, on_read);

	std::cout << "Connecting to server..." << std::endl;
	uv_run(uv_default_loop(), UV_RUN_DEFAULT);

	// 清理资源
	free(socket);
	free(connect_req);

	return 0;
}