libuv强调异步的编程风格,包括文件读写也是。下面就来介绍一下利用libuv进行文件读写的例子。
文件读写相关API介绍:
1.打开文件:
uv_fs_open(uv_loop_t* loop,uv_fs_t* req,const char* path,int flags,int mode,uv_fs_cb cb);
参数1:最终被uv_run启动的event-loop,如果只有一个loop的话可以使用libuv提供的默认loop:uv_default_loop();
参数2:与打开文件操作相关联的对象;
参数3:要打开的文件的路径名;
参数4:flag与标准的unix的open函数的flag相同;
参数5:mode与标准的unix的open函数的mode相同,如果忽略此项的话值可为0;
参数6:文件打开成功/失败后所调用的回调函数,函数原型:(uv_fs_cb)(uv_fs_t req);
返回值:文件描述符。
2.读文件:
int uv_fs_read(uv_loop_t* loop,uv_fs_t* req,uv_file file,const uv_buf_t bufs[],unsigned int nbufs,int64_t offset,uv_fs_cb cb);
参数1:event-loop;
参数2:与读文件操作相关联的对象;
参数3:实际为int型,文件描述符;
参数4:已经初始化完成的指向uv_buf_t结构体的指针,该结构体实际包含一个指向一个数组的指针以及数组中有效的数据长度。
结构体原型:
typedef struct uv_buf_t {
char* base;
size_t len;
} uv_buf_t;
常用uv_buf_init函数初始化
参数5:一般写1(在官方文档中没找到具体的介绍)
参数6:偏移量,一般写-1
参数7:读文件成功/失败所调用的回调函数,函数原型同uv_fs_open函数的回调函数。
返回值:读到的字符数,通常不会用到此值。
3.写文件:
uv_fs_write(uv_loop_t* loop,uv_fs_t* req,uv_file file,const uv_buf_t bufs[],unsigned int nbufs,int64_t offset,uv_fs_cb cb);
参数列表与返回值uv_fs_read类似。
4,关闭文件:
int uv_fs_close(uv_loop_t* loop,uv_fs_t* req,uv_file file,uv_fs_cb cb);
相关回调函数介绍
1.uv_fs_open函数的回调
函数原型:(uv_fs_cb)(uv_fs_t req);
其中req对应于uv_fs_open的参数2,req->result存储的则是文件描述符fd;
2.uv_fs_read函数的回调
函数原型:(uv_fs_cb)(uv_fs_t req);
其中req对应于uv_fs_open的参数2,req->result存储的则是从目标文件读到的字符数;
3.uv_fs_write函数的回调
函数原型:(uv_fs_cb)(uv_fs_t req);
其中req对应于uv_fs_open的参数2,req->result存储的则是成功写入目标文件的字符数;
4.总结:
同一个文件的打开、读、写所对应的uv_fs_t并不是一个,在这里不要弄混淆。此外,针对文件的打开、读写,所对应的回调函数的req->result的含义也不相同。
在这里贴上官方的实现cat命令的代码:
#include
#include
#include
#include
#include
void on_read(uv_fs_t *req);
uv_fs_t open_req;
uv_fs_t read_req;
uv_fs_t write_req;
static char buffer[1024];
static uv_buf_t iov;
void on_write(uv_fs_t *req) {
if (req->result < 0) {
fprintf(stderr, "Write error: %s\n", uv_strerror((int)req->result));
}
else {
uv_fs_read(uv_default_loop(), &read_req, open_req.result, &iov, 1, -1, on_read);
}
}
void on_read(uv_fs_t *req) {
if (req->result < 0) {
fprintf(stderr, "Read error: %s\n", uv_strerror(req->result));
}
else if (req->result == 0) {
uv_fs_t close_req;
// synchronous
uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
}
else if (req->result > 0) {
iov.len = req->result;
uv_fs_write(uv_default_loop(), &write_req, 1, &iov, 1, -1, on_write);
}
}
void on_open(uv_fs_t *req) {
// The request passed to the callback is the same as the one the call setup
// function was passed.
assert(req == &open_req);
if (req->result >= 0) {
iov = uv_buf_init(buffer, sizeof(buffer));
uv_fs_read(uv_default_loop(), &read_req, req->result,
&iov, 1, -1, on_read);
}
else {
fprintf(stderr, "error opening file: %s\n", uv_strerror((int)req->result));
}
}
int main(int argc, char **argv) {
uv_fs_open(uv_default_loop(), &open_req, argv[1], O_RDONLY, 0, on_open);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
uv_fs_req_cleanup(&open_req);
uv_fs_req_cleanup(&read_req);
uv_fs_req_cleanup(&write_req);
return 0;
}
代码的基本逻辑是:
1.打开目标文件,并在打开成功后在on_open回调函数中读取文件内容。
2.在读取文件成功后,将读取到的内容写到标准输出。
3.在写到标准输出成功后继续读取目标文件。
4.循环进行2、3步,直到读到文件的末尾。
5.关闭文件。
说明:在读文件的过程中,程序员无须指定一次uv_fs_read所读取的字符个数,这是因为libuv会根据buffer的大小和文件的大小自行决定应该读取多少字符。