socket连接池SocketPool分析(十):libevent
- 1. 用到的libevent的5个接口
- 1.1. struct event_base* event_base_new()
- 1.2. struct event* event_new(struct event_base* base, evutil_socket_t listenfd, EV_READ, event_callback_fn callback_func, (void*)base)
- 1.3. int event_add(struct event* listen_event, const struct timeval *tv)
- 1.4. event_base_dispatch(struct event_base* base)
- 1.5. event_base_free(struct event_base* base)
- 2. bufferevent的接口
- 2.1. struct bufferevent * bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options)
- 2.2. void bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb eventcb, void *cbarg)
- 2.3. int bufferevent_enable(struct bufferevent *bufev, short event)
- 2.4. size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
- 2.5. int bufferevent_write(struct bufferevent *bufev,const void *data, size_t size)
- 2.6. bufferevent_free(bev);
- 3. 请看我的gtest_server,是使用libevent实现的:
- 4. 延伸阅读:
- 5. 《socket连接池SocketPool分析》系列文章总结:
我还在读大学的时候,C++的库听过最有名的是boost,其次就是这个libevent了,但是一直没有去了解,正好,借着复习UNP的时候学习一下这个大名鼎鼎的网络库。
libevent的官方文档:http://libevent.org
libevent是一个典型的reactor模型。有关reactor模型就请参见文章《socket连接池SocketPool分析(八): 并发服务器》。在这篇文章当中我只谈使用libevent。
用到的libevent的5个接口
- event_base_new()
- event_new()
- event_add()
- event_base_dispatch()
- event_base_free()
使用这些接口需要包含
event2/event.h
头文件。
struct event_base* event_base_new()
创建一个event_base的指针。类似于epoll
当中的创建一个句柄。
1 | int efd = epoll_create(FDSIZE); |
以后所有的接口都需要调用这个event_base
类型的指针,并且每个线程有且只有一个event_base
指针。
struct event* event_new(struct event_base* base, evutil_socket_t listenfd, EV_READ, event_callback_fn callback_func, (void*)base)
创建一个事件event。相当于epoll_ctl
当中的EPOLL_CTL_ADD
。
第一个参数是根据event_base_new()
创建的event_base
指针,第二个参数是socket描述符。
第三个是属性:
- EV_TIMEOUT: 超时
- EV_READ: 只要网络缓冲中还有数据,回调函数就会被触发
- EV_WRITE: 只要塞给网络缓冲的数据被写完,回调函数就会被触发
- EV_SIGNAL: POSIX信号量,参考manual吧
- EV_PERSIST: 不指定这个属性的话,回调函数被触发后事件会被删除
- EV_ET: Edge-Trigger边缘触发,参考EPOLL_ET
这些属性使用|
连接起来。
接下来就是回调函数callback_func,它的类型event_callback_fn是这样子的:
1 | typedef void(* event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg) |
最后的参数(void*)base
是传递给回调函数callback_func
的参数,就是传给了上面的那个void *arg
参数。
在我们使用epoll
当中,使用epoll_wait()
等待,等到有事件触发之后,我们还需要使用if
判断是哪一个文件描述符触发的事件。这里直接使用回调函数来做,省去了if
判断的功夫。
int event_add(struct event* listen_event, const struct timeval *tv)
相当于epoll_ctl
当中的EPOLL_CTL_ADD
。
这个函数就是把事件注册到event_base
指针上去。如果该事件已经在活动事件队列或者超时队列中,先从中删除。
注意的是这里没有像EPOLL_CTL_DEL
这样的删除事件的方法。因为可以使用EV_PERSIST
这个属性:不指定这个属性的话,回调函数被触发后事件会被删除。
所以这里不需要手动删除,而是自动删除。
第一个参数就是通过event_new
创建的事件,第二个参数则是设定超时值。我一般直接设为NULL,表示永不超时。
event_base_dispatch(struct event_base* base)
启动事件循环,这样才能开始处理发生的事件。循环将一直持续,直到不再有需要关注的事件,或者是遇到event_loopbreak()/event_loopexit()函数。
event_base_free(struct event_base* base)
释放event_base
指针,相当于epoll
当中的close(efd)
。
bufferevent的接口
libevent2提供了bufferevent,直接使用这个就可以不用再用read()
函数和write()
函数了,而是使用bufferevent_read()
和bufferevent_write()
来代替。
struct bufferevent * bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options)
创建一个bufferevent
类型的指针,接下来的bufferevent
接口全部都要用到这个指针。
void bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb eventcb, void *cbarg)
在bufferevent
指针上绑定读的回调函数,写的回调函数,和异常的回调函数。
int bufferevent_enable(struct bufferevent *bufev, short event)
启动bufferevet
,就相当于event_base_dispatch
。
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
调用bufferevent_read()从输入缓存里删除size字节,存到内存的data位置,返回被删除的字节数。
与read()
函数相比,read()
函数第一个参数是文件描述符,bufferevent_read()
函数第一个参数是bufferevent
指针。
int bufferevent_write(struct bufferevent *bufev,const void *data, size_t size)
添加数据到输出缓存里,调用bufferevent_write()把内存里data位置的size字节添加到输出缓存的末尾。
bufferevent_free(bev);
使用这个释放资源。
请看我的gtest_server
,是使用libevent实现的:
1 | #include <iostream> |
延伸阅读:
《socket连接池SocketPool分析》系列文章总结:
自从我写完《数据库连接池DBPool分析》系列文章到今天已经有2个月的时间了,没想到拖了这么久,但是终于写完了连接池的部分和对网络编程模式的部分,但是一个one loop per thread
我并没有实现,因为这个需要进程间通信和线程间通信的知识,我打算再复习一下《UNPv2》这本书,等复习完成之后,我再开始写。