惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
云风的 BLOG
云风的 BLOG
大猫的无限游戏
大猫的无限游戏
M
MIT News - Artificial intelligence
L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Recent Announcements
Recent Announcements
IT之家
IT之家
Google DeepMind News
Google DeepMind News
罗磊的独立博客
爱范儿
爱范儿
Last Week in AI
Last Week in AI
人人都是产品经理
人人都是产品经理
U
Unit 42
MongoDB | Blog
MongoDB | Blog
S
SegmentFault 最新的问题
B
Blog
博客园 - 叶小钗
月光博客
月光博客
Stack Overflow Blog
Stack Overflow Blog
V
Visual Studio Blog
C
Check Point Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

博客园 - 整鬼专家

反向代理服务 Http认证(鉴权) 跨域问题 SSE客户端C++实现(使用libcurl) 使用libcurl/curlpp进行http通信 零拷贝IO libuv进程 libuv线程 libuv网络操作 libuv概述 MySQL Cluster与Replication Redis集群 Redis持久化 Redis脚本 Redis进阶——事务、TTL、排序、消息通知、管道 Redis数据类型 Redis安装 行为设计模式 结构型设计模式
libuv文件系统
整鬼专家 · 2024-05-09 · via 博客园 - 整鬼专家

  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);
}