libhv学习笔记6:IO事件之read事件

本文深入探讨了Linux环境下,IO事件中的读事件处理流程。通过分析`hio_read()`函数及其相关回调函数,揭示了`on_rev`回调函数在何时被调用及其参数来源。在事件循环中,当接收到IO事件时,`hio_handle_events`被调用,进而执行读取操作。若未设置读缓冲区,将使用事件循环缓冲区,`on_rev`回调中需注意数据完整性。总结了从IO事件触发到回调函数执行的整个过程。
摘要由CSDN通过智能技术生成

今天主要分析下IO事件中的读事件的过程。
如有理解错误,欢迎批评指正。

int main()
{
    hloop_t* loop = hloop_new(0);
    
    // 客户端fd
    int connFd = ConnectTimeout("127.0.0.1",1234);
    //io事件
    hio_t * hio =  hio_get(loop,connFd);
	//设置read 回调函数
    hio_setcb_read(hio,on_rev);
    //注册监听读事件
    hio_read(hio);
    // 设置connect 回调函数
    hio_setcb_connect(hio,connt);
    //注册连接事件
    hio_connect(hio);
    
    //发送消息
    char a[20] ="hello libhv\r\n";
    hio_write(hio,a,10);

    hloop_run(loop);
    hloop_free(&loop);
    return 0;
}

探究目标:on_rev是在何时调用的,入参是怎么来的?

首先看一下hio_read(),位置:/libhv/event/nio.c

int hio_read (hio_t* io) {
    if (io->closed) {
        hloge("hio_read called but fd[%d] already closed!", io->fd);
        return -1;
    }
    return hio_add(io, hio_handle_events, HV_READ);
}
int hio_add(hio_t* io, hio_cb cb, int events) {
    printf("hio_add fd=%d io->events=%d events=%d\n", io->fd, io->events, events);
#ifdef OS_WIN
    // Windows iowatcher not work on stdio
    if (io->fd < 3) return -1;
#endif
    hloop_t* loop = io->loop;
    //如果事件没被激活,就添加事件
    if (!io->active) {
        EVENT_ADD(loop, io, cb);
        loop->nios++;
    }
	//激活io事件
    if (!io->ready) {
        hio_ready(io);
    }
	//添加事件的回调函数
    if (cb) {
        io->cb = (hevent_cb)cb;
    }
	//events 与io->events如果不相同的话,就加入新的监听事件events
	//比如原来是write事件,现在是read事件,那么(io->events & events)
    if (!(io->events & events)) {
        iowatcher_add_event(loop, io->fd, events);
        io->events |= events;
    }
    return 0;
}

注意看一下,这里的回调函数是hio_handle_events,并不是我们定义的回调函数on_rev
,看一下,这个回调函数做了什么

static void hio_handle_events(hio_t* io) {
    if ((io->events & HV_READ) && (io->revents & HV_READ)) {
        if (io->accept) {
        //处理accpet事件
            nio_accept(io);
        }
        else {
        //处理读事件
            nio_read(io);
        }
    }

    if ((io->events & HV_WRITE) && (io->revents & HV_WRITE)) {
        // NOTE: del HV_WRITE, if write_queue empty
        hrecursive_mutex_lock(&io->write_mutex);
        if (write_queue_empty(&io->write_queue)) {
            iowatcher_del_event(io->loop, io->fd, HV_WRITE);
            io->events &= ~HV_WRITE;
        }
        hrecursive_mutex_unlock(&io->write_mutex);
        if (io->connect) {
            // NOTE: connect just do once
            // ONESHOT
            io->connect = 0;

            nio_connect(io);
        }
        else {
        //处理写事件
            nio_write(io);
        }
    }

    io->revents = 0;
}

这个函数主要处理了一些IO事件,那么这个回调函数在哪里被回调的呢?
我们再回到事件循环里,事件被执行的那个部分代码

static int hloop_process_pendings(hloop_t* loop) {
    if (loop->npendings == 0) return 0;

    hevent_t* cur = NULL;
    hevent_t* next = NULL;
    int ncbs = 0;
    // NOTE: invoke event callback from high to low sorted by priority.
    for (int i = HEVENT_PRIORITY_SIZE-1; i >= 0; --i) {
        cur = loop->pendings[i];
        while (cur) {
            next = cur->pending_next;
            if (cur->pending) {
                if (cur->active && cur->cb) {
                    cur->cb(cur); //===>执行回调
                    ++ncbs;
                }
                cur->pending = 0;
                // NOTE: Now we can safely delete event marked as destroy.
                if (cur->destroy) {
                    EVENT_DEL(cur);
                }
            }
            cur = next;
        }
        loop->pendings[i] = NULL;
    }
    loop->npendings = 0;
    return ncbs;
}

所以hio_handle_events函数是在收到IO事件时,被执行,那么如果是读事件的话,就会执行 nio_read(io);

static void nio_read(hio_t* io) {
    printf("nio_read fd=%d\n", io->fd);
    void* buf;
    int len, nread;
read:
	//如果io事件没有分配缓冲区,就使用事件循环里的缓冲区
    if (io->readbuf.base == NULL || io->readbuf.len == 0) {
        hio_set_readbuf(io, io->loop->readbuf.base, io->loop->readbuf.len);
    }
    buf = io->readbuf.base;
    len = io->readbuf.len;
    //读取数据,返回实际读取到的数据
    nread = __nio_read(io, buf, len);
    //printd("read retval=%d\n", nread);
    if (nread < 0) {
        if (socket_errno() == EAGAIN) {
            //goto read_done;
            return;
        }
        else {
            io->error = socket_errno();
            // perror("read");
            goto read_error;
        }
    }
    //连接断开了,关闭连接
    if (nread == 0) {
        goto disconnect;
    }
    //调用用户注册的回调函数
    __read_cb(io, buf, nread);
    if (nread == len) {
        goto read;
    }
    return;
read_error:
disconnect:
    hio_close(io);
}

到这里,我们终于揭开了我们探究的目标:on_rev是在何时调用的,入参是怎么来的?

注意:这里我们可以看到,如果我们没有设置io的读缓冲区的话,那么默认是使用的事件循环的读缓冲区,所以,在on_rev回调中,如果解析出数据不完整的话,一定要copy到其他缓冲区中,否则下次收到读事件后,会被覆盖掉。

具体过程如下:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值