今天主要分析下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的写事件的源码理解。