libhv学习笔记7:IO事件之write事件

今天主要分析下libhv中的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;
}

写数据的函数是hio_write()

int hio_write (hio_t* io, const void* buf, size_t len) {
	//如果连接已经关闭了,就记录log
    if (io->closed) {
        hloge("hio_write called but fd[%d] already closed!", io->fd);
        return -1;
    }
    int nwrite = 0;
    //加锁,控制对写队列的访问
    hrecursive_mutex_lock(&io->write_mutex);
    //如果写队列里面没有待发送的数据,就发送数据
    if (write_queue_empty(&io->write_queue)) {
try_write:
		//发送数据
        nwrite = __nio_write(io, buf, len);
        //printd("write retval=%d\n", nwrite);
        //检查发送状态
        if (nwrite < 0) {
        	//发送中断,将发送数据放入队列
            if (socket_errno() == EAGAIN) {
                nwrite = 0;
                hlogw("try_write failed, enqueue!");
                goto enqueue;
            }
            //发送失败
            else {
                // perror("write");
                io->error = socket_errno();
                goto write_error;
            }
        }
        //socket断开
        if (nwrite == 0) {
            goto disconnect;
        }
        //调用用户自定义发送回调函数
        __write_cb(io, buf, nwrite);
        //全部数据发送完成,返回
        if (nwrite == len) {
            //goto write_done;
            hrecursive_mutex_unlock(&io->write_mutex);
            return nwrite;
        }
enqueue:
		//当epoll/poll检测到可写事件时,调用hio_handle_events,发送剩余数据
        hio_add(io, hio_handle_events, HV_WRITE);
    }
    //如果数据没有发送完成,就将数据放入队列
    if (nwrite < len) {
        offset_buf_t rest;
        //拷贝的是完整的数据,而不是剩余未发送的数据
        rest.len = len;
        rest.offset = nwrite;
        // NOTE: free in nio_write
        HV_ALLOC(rest.base, rest.len);
        memcpy(rest.base, buf, rest.len);
        //初始化队列
        if (io->write_queue.maxsize == 0) {
            write_queue_init(&io->write_queue, 4);
        }
        //加入队列
        write_queue_push_back(&io->write_queue, &rest);
    }
    hrecursive_mutex_unlock(&io->write_mutex);
    return nwrite;
write_error:
disconnect:
    hrecursive_mutex_unlock(&io->write_mutex);
    hio_close(io);
    return nwrite;
}

这里可以看到,在hio_write 中,先尝试发送数据,如果数据没能完全发送,就将数据暂存到一个队列中,等到接收到可发送的事件时,再发送,调用的函数是hio_handle_events,这个函数在上一篇读事件中,是同一个函数

static void hio_handle_events(hio_t* io) {
    if ((io->events & HV_READ) && (io->revents & HV_READ)) {
        if (io->accept) {
            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;
}

我们再看下nio_write函数

static void nio_write(hio_t* io) {
    //printd("nio_write fd=%d\n", io->fd);
    int nwrite = 0;
    //加锁,控制队列访问
    hrecursive_mutex_lock(&io->write_mutex);
write:
    if (write_queue_empty(&io->write_queue)) {
        hrecursive_mutex_unlock(&io->write_mutex);
        if (io->close) {
            io->close = 0;
            hio_close(io);
        }
        return;
    }
    //从队列中,取出数据
    offset_buf_t* pbuf = write_queue_front(&io->write_queue);
    //计算“剩下未发送数据”的地址
    char* buf = pbuf->base + pbuf->offset;
    int len = pbuf->len - pbuf->offset;
    //发送数据
    nwrite = __nio_write(io, buf, len);
    //printd("write retval=%d\n", nwrite);
    //检查发送状态
    if (nwrite < 0) {
    	//发送中断,不做特殊处理,返回
        if (socket_errno() == EAGAIN) {
            //goto write_done;
            hrecursive_mutex_unlock(&io->write_mutex);
            return;
        }
        //发送失败,关闭io
        else {
            io->error = socket_errno();
            // perror("write");
            goto write_error;
        }
    }
    //io中断
    if (nwrite == 0) {
        goto disconnect;
    }
    //调用用户自定义发送回调函数
    __write_cb(io, buf, nwrite);
    pbuf->offset += nwrite;
    //已经完全发送,释放内存,从队列中删除
    if (nwrite == len) {
        HV_FREE(pbuf->base);
        write_queue_pop_front(&io->write_queue);
        // write next
        goto write;
    }
    hrecursive_mutex_unlock(&io->write_mutex);
    return;
write_error:
disconnect:
    hrecursive_mutex_unlock(&io->write_mutex);
    //关闭io
    hio_close(io);
}

整个逻辑还是比较清晰的,就是先尝试发送所有数据,如果没法完全发送出去,就放入一个数据队列,并添加监听可写事件,当io可写时,就从队列中读取数据,发送剩余数据,等待所有数据都发送完成后,再删除监听可写事件。
最后我们看一下hio_close函数

int hio_close (hio_t* io) {
	//如果io已经关闭,返回
    if (io->closed) return 0;
    //如果当前关闭的线程不是创建的线程,就自定义一个关闭事件,交给创建的线程处理
    if (hv_gettid() != io->loop->tid) {
        hevent_t ev;
        memset(&ev, 0, sizeof(ev));
        ev.cb = hio_close_event_cb;
        ev.userdata = io;
        ev.privdata = (void*)(uintptr_t)io->id;
        hloop_post_event(io->loop, &ev);
        return 0;
    }
    hrecursive_mutex_lock(&io->write_mutex);
    //如果还有数据未发送完成,且没发生错误以及io没有被关闭,则延迟关闭,延迟时间默认是60s
    if (!write_queue_empty(&io->write_queue) && io->error == 0 && io->close == 0) {
        hrecursive_mutex_unlock(&io->write_mutex);
        io->close = 1;
        hlogw("write_queue not empty, close later.");
        int timeout_ms = io->close_timeout ? io->close_timeout : HIO_DEFAULT_CLOSE_TIMEOUT;
        io->close_timer = htimer_add(io->loop, __close_timeout_cb, timeout_ms, 1);
        io->close_timer->privdata = io;
        return 0;
    }
    hrecursive_mutex_unlock(&io->write_mutex);
	
	//所有数据发送完成,置flag
    io->closed = 1;
    //清理IO
    hio_done(io);
    //删除所有io的定时器事件
    __close_cb(io);
    if (io->ssl) {
        hssl_free(io->ssl);
        io->ssl = NULL;
    }
    //关闭io
    if (io->io_type & HIO_TYPE_SOCKET) {
        closesocket(io->fd);
    }
    return 0;
}

最后看一下hio_done做了写什么

void hio_done(hio_t* io) {
    if (!io->ready) return;
    io->ready = 0;
	//从loop中删除io,删除io的监听事件
    hio_del(io, HV_RDWR);
	//释放发送数据队列内存
    offset_buf_t* pbuf = NULL;
    hrecursive_mutex_lock(&io->write_mutex);
    while (!write_queue_empty(&io->write_queue)) {
        pbuf = write_queue_front(&io->write_queue);
        HV_FREE(pbuf->base);
        write_queue_pop_front(&io->write_queue);
    }
    write_queue_cleanup(&io->write_queue);
    hrecursive_mutex_unlock(&io->write_mutex);
}

以上就是对libhv的IO的写事件的源码理解。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值