Libevent是一个用于开发可伸缩网络服务器的事件通知库。libevent API提供了一种机制,用于在文件描述符上发生特定事件或达到超时后执行回调函数。此外,libevent还支持由于信号或常规超时引起的回调。
Libevent旨在取代事件驱动网络服务器中的事件循环。应用程序只需要调用event_dispatch(),然后动态地添加或删除事件,而不必更改事件循环。目前libevent支持/dev/poll、kqueue(2)、select(2)、poll(2)和epoll(4)。它还具有实时信号的实验支持。内部事件机制完全独立于公开的事件API,对libevent进行简单更新就可以提供新功能,而无需重新设计应用程序。因此,Libevent支持可移植的应用程序开发,并提供了操作系统上可用的最可伸缩的事件通知机制。Libevent也可以用于多线程应用程序。Libevent可以在Linux, *BSD, Mac OS X, Solaris和Windows上编译。
每个使用libevent的程序都必须包含
对于希望监视的每个文件描述符,必须声明一个事件结构并调用event_set()来初始化结构的成员。要启用通知,可以通过调用event_add()将结构添加到监视事件列表中。只要事件结构处于活动状态,它就必须保持分配状态,因此应该在堆上分配它。最后,调用event_dispatch()来循环和分派事件。
Libevent提供了常规事件回调之上的抽象。这种抽象称为缓冲事件。缓冲事件提供了自动填充和清空的输入和输出缓冲区。缓冲事件的用户不再直接处理I/O,而是从输入缓冲区读取并写入输出缓冲区。
一旦通过buffer使用event_new()初始化,bufferevent结构就可以通过bufferevent_enable()和bufferevent_disable()重复使用。不是直接读取和写入套接字,而是调用bufferevent_read()和bufferevent_write()。
当读取启用时,bufferevent将尝试从文件描述符读取并调用读取回调。每当输出缓冲区被耗尽到写入低水位(默认为0)以下时,就会执行写回调。
除了简单的计时器之外,libevent还可以将超时事件分配给文件描述符,当文件描述符上没有任何活动而经过一定时间时,就会触发这些事件。
timeout_set()函数的作用是:初始化一个事件结构以用作超时。
初始化后,必须使用timeout_add()激活事件。
要取消超时,调用timeout_del()。
libevent提供了一个异步DNS解析器,应该使用它来代替标准的DNS解析器函数。可以通过在程序中包含
libevent提供了一个非常简单的事件驱动的HTTP服务器,可以嵌入到程序中,用于服务HTTP请求。
要使用此功能,需要在程序中包含
使用evhttp_bind_socket()添加要侦听的地址和端口。
然后注册一个或多个回调来处理传入的请求。
每个URI都可以通过evhttp_set_cb()函数被分配回调。
通用回调函数也可以通过evhttp_set_gencb()注册;如果没有为给定URI注册其他回调,则调用此回调。
libevents为创建RPC服务器和客户端提供了一个框架。它负责对所有数据结构进行封送和解封送。
要浏览libevent API的完整文档,见reference
event2/event.h主libevent头文件
event2/ Buffer .h网络读写缓冲区管理
event2/ DNS .h异步DNS解析
event2/ HTTP .h嵌入式基于libevent的HTTP服务器
evrpc.h用于创建RPC服务器和客户端的框架
API及调用顺序为:
event_base()初始化event_base
event_set()初始化event
event_base_set()将event绑定到指定的event_base上
event_add()将event添加到事件链表上,注册事件
event_base_dispatch()循环、检测、分发事件
使用事件前需要先初始化。设置current_base全局变量,表示与事件没有关联的缺省基数。
函数已弃用,与当前的事件基相关联,导致多线程不安全。替换的安全函数:event_base_new
struct event_base* event_init ( void )
//Initialize the event API.
event_base_set(), event_base_new()可替换
event_base_new()做的工作主要就是对结构体event_base的初始化的作用,设置一些参数这类,event_init实际上就是对event_base_new的封装
int event_base_set (struct event_base * ,struct event * )
根据初始化参数,初始化
void event_set (struct event *, evutil_socket_t,short, void(*)(evutil_socket_t, short, void *), void*)
//Prepare an event structure to be added.
将event_base_set()将event绑定到指定的event_base上
int event_base_set ( struct event_base * ,struct event * )
//Associate a different event base with an event.
//Parameters:
//eb the event base
//ev the event
Parameters:
ev an event struct initialized via event_set()
timeout the maximum amount of time to wait for the event, or NULL to wait forever
Returns:
0 if successful, or -1 if an error occurred
根据时间类型添加到不同的列表中;
1.将event注册到event_base的I/O多路复用要监听的事件链表中
2.将event注册到event_base的已注册事件链表中
3.如果传入了超时时间,则删除旧的超时时间,重新设置,并将event添加到event_base的小根堆中;
如果没有传入超时时间,则不会添加到小根堆中。
函数内添加到I/O多路复用监听事件链表、已注册事件链表、小根堆中都是通过event_queue_insert()完成的,相应的删除工作都是通过event_queue_remove()完成的。
int event_add ( struct event * ,const struct timeval * );
//Add an event to the set of monitored events.
函数event_add()在event_set()中指定的事件发生时或至少在tv中指定的时间内调度ev事件的执行。
如果tv为NULL,则不会发生超时,只有在文件描述符上发生匹配事件时才会调用该函数。
ev参数中的事件必须已经被event_set()初始化,并且不能在调用event_set()时使用,直到它超时或被event_del()删除。
如果ev参数中的事件已经有一个预定的超时,旧的超时将被新的超时取代。
Parameters:
eb the event_base structure returned by event_init()
int event_base_dispatch ( struct event_base * )
//Threadsafe event dispatching loop.
调用了event_base_loop函数
event_base_loop()主要就是循环、检测、分发事件
按照代码流程;
1.信号标记被设置,则调用信号的回调函数
2.根据定时器最小时间,设置I/O多路复用的最大等待时间,这样即使没有I/O事件发生,也能在最小定时器超时时返回。
3.调用I/O多路复用,监听事件,将活跃事件添加到活跃事件链表中
4.检查定时事件,将就绪的定时事件从小根堆中删除,插入到活跃事件链表中
libevent的核心就event_base_loop();在这其中检测和分发通过I/O多路复用来完成,比如我们经常使用的poll和epoll,通过epoll.c就可以看到源码;其实原理与我们之前学习到的epoll编程是很类似的,只是多了一部分的处理方式,达到与整合系统互相呼应的效果;
https://www.monkey.org/~provos/libevent/doxygen-2.0.1/include_2event2_2event_8h.html#2efd0ac7e54428d1941f212bc16ed9ce