libuv文件系统
1、读取和写入文件
如下为异步打开test.dat文件后,读取文件数据并将其写入到标准输出的示例,读取和写入的时候使用uv_buf_t类型来作为缓存:
#include "uv.h" #include <assert.h> uv_fs_t open_req, read_req, write_req; void on_write(uv_fs_t* req) { if (req->result < 0) { fprintf(stderr, "Write error: %s\n", uv_strerror((int)req->result)); } else { fprintf(stdout, "\nComplete: %d characters written\n", req->result); } } void on_read(uv_fs_t* req) { if (req->result < 0) { //error:uv_fs_t::result域为读取的数据量或错误码 fprintf(stderr, "Read error: %s\n", uv_strerror(req->result)); } else if (req->result == 0) { //读取到文件末尾 uv_fs_t close_req; uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL); if (req->data) free(req->data); } else if (req->result > 0) { //读取到数据 if (req->data) { auto iov = uv_buf_init((char*)(req->data), req->result); write_req.data = iov.base; uv_fs_write(uv_default_loop(), &write_req, 1/*标准输出*/, &iov, 1, -1, on_write); } } } void on_open(uv_fs_t* req) { assert(req == &open_req); if (req->result >= 0) { //打开正常:uv_fs_t::result域为打开文件的fd(文件描述符)/ handle(句柄) int size = 1024; auto iov = uv_buf_init((char*)malloc(size), size); //保存读取到的数据 read_req.data = iov.base; //handle和request都有一个data域,可以用来存储上下文信息 uv_fs_read(uv_default_loop(), &read_req, req->result, &iov, 1, -1, on_read); } else { //error fprintf(stderr, "error opening file: %s\n", uv_strerror((int)req->result)); //uv_strerror()可以获取错误信息,uv_err_name()可以获取错误名字 } } int main() { //int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) //参数flags与mode和标准的 Unix flags 相同,对应Windows,libuv会自动将参数转换为 Windows 环境下的相关标志位(flags) uv_fs_open(uv_default_loop(), &open_req, "test.dat", O_RDWR, 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); }
除了uv_fs_open()、uv_fs_read(),libuv还提供uv_fs_stat()、uv_fs_mkdir()、uv_fs_access()等文件或目录的操作方法。libuv还提供了监视文件和文件夹的变化的相关api,具体可以参考 File change events。
2、流式操作
也可以使用流式操作来进行读取或写入操作,通过uv_read_start()、uv_write()、uv_read_stop()方法,调用uv_read_start()后会读取数据直到可读数据为空或uv_read_stop()被调用。TCP套接字,UDP套接字,管道支持流式操作,可以为文件关联一个pipe来对文件进行uv_read_start()、uv_write()流式操作。如下分别为test.dat文件和标准输出关联了一个pipe管道,然后对文件进行读取,读取到数据后将其写入到标准输出。实际运行代码发现将管道关联到文件的方法会报错,原因不得而知:
#include "uv.h" #include <assert.h> uv_pipe_t stdout_pipe, file_pipe; void on_stdout_write(uv_write_t* req, int status) { free(req->data); free(req); } void on_read_file(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { if (nread > 0) { //读取到数据 uv_write_t* req = (uv_write_t*)malloc(sizeof(uv_write_t)); auto iov = uv_buf_init((char*)malloc(nread), nread); memcpy(iov.base, (*buf).base, nread); iov.len = nread; req->data = iov.base; uv_write(req, (uv_stream_t*)&stdout_pipe, &iov, 1, on_stdout_write/*写入数据回调*/); } else if (nread < 0) { //读取到结尾或发生错误 if (nread == UV_EOF) { /*end of file*/ }else if(nread == UV_ENOBUFS) { /*alloc_buf() returned a buffer of length 0*/ } uv_close((uv_handle_t*)&stdout_pipe, NULL); uv_close((uv_handle_t*)&file_pipe, NULL); } else { //there is no more reading left } if (buf->base) free(buf->base); } void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { *buf = uv_buf_init((char*)malloc(suggested_size), suggested_size); } int main() { uv_loop_t* loop = uv_default_loop(); uv_fs_t file_req; int fd = uv_fs_open(loop, &file_req, "test.dat", O_CREAT | O_RDWR, 0644, NULL/*回调为空表示同步模式*/); auto i = uv_pipe_init(loop, &file_pipe, 1); //创建有名管道 auto n = uv_pipe_open(&file_pipe, fd); //将管道和文件描述符关联起来,文件描述符会自动设为非阻塞 uv_pipe_init(loop, &stdout_pipe, 0); uv_pipe_open(&stdout_pipe, 1/*标准输出*/); auto r = uv_read_start((uv_stream_t*)&file_pipe, alloc_buffer/*提供保存读取到数据的缓存uv_buf_t的方法*/, on_read_file/*读取到数据的回调*/); uv_run(loop, UV_RUN_DEFAULT); }