libevent
libevent实际上就是对底层select/poll/epoll等进行了封装,每个event_base都有一种方法,该方法是select、poll、epoll、kqueue、devpoll、evport、win32。
event_base
使用libevent函数之前需要分配一个或多个event_base,每个event_base持有一个事件集合,可以检测以确定哪个事件是激活的,它相当于是一个底座,只要向底座上插入事件,然后不断的监控事件,等待事件发生调用回调函数即可。
event_base有两种:(1)默认的;(2)可配置的,指定使用的后台方法等;
event_loop
一旦创建好事件根基event_base,并且在根基上安插好事件之后,需要对事件循环监控(换句话说就是等待事件的到来,触发事件的回调函数),有两种方式可以达到上面描述的功能,即:event_base_dispatch和event_base_loop。
event
event是对事件的抽象,它是libevent基本操作单元。每个事件代表一组条件的集合,这些条件包括:
(1)文件描述符已经就绪、可以读取或者写入
(2)文件描述符变为就绪状态、可以读取或者写入(仅对于边沿触发IO)
(3)超时事件
(4)发生某信号
(5)用户触发事件
创建event,关联到event_base后,事件进入已初始化状态,添加到event_base后,进入未决状态,此状态下,如果事件触发,则进入激活状态,事件回调将被执行。
bufferevent
上面提到的event_base、event主要是用作对事件处理(监听、分发等)的抽象,而bufferevent则是对数据缓冲/事件的抽象。
(1)数据缓冲
bufferevent由一个底层的传输端口(如套接字)、一个读取缓冲区、一个写入缓冲区组成。与通常的事件在底层传输端口已经就绪,可以读取或者写入的时候执行回调不同的是,bufferevent在读取或者写入了足够量的数据之后调用用户提供的回调。前者是IO就绪后,调用事件回调,通过原生接口send/recv从IO缓冲区写入/读取数据。后者是IO就绪后,首先由bufferevent通过原生接口从IO缓冲区写入读取数据后,再回调用事件回调,相当于bufferevent在中间封装了一层。
bufferevent有一个输入缓冲区、一个输出缓冲区,它们的类型都是evbuffer。有数据要写入到bufferevent时,添加数据到输出缓冲区。bufferevent 中有数据供读取的时候,从输入缓冲区抽取(drain)数据。
每个bufferevent有两个数据相关的回调:一个读取回调和一个写入回调。默认情况下,从底层传输端口读取了任意量的数据之后会调用读取回调;输出缓冲区中足够量的数据被清空到底层传输端口后写入回调会被调用;通过调整bufferevent的读取和写入"水位(watermarks)"可以覆盖这些函数的默认行为。
(2)事件
bufferevent是对套接字的封装,具有相关的事件状态:链路建立、链路错误等。因此,它也是对事件的抽象,可以用它代替event,由event_base进行分发。
evbuffer
对bufferevent的缓冲区抽象,提供缓冲区读写等能力。
LibEvent集成步骤
(1)创建一个event_base、监听socket,设置为非阻塞,完成bind、listen;
(2)创建一个event,与event_base、监听socket关联;事件类型为EV_READ|EV_PERSIST,回调为do_accept
(3)调用event_add向event_base中添加event,调用event_base_dispatch启动事件循环;
(4)事件激活后,调用事件回调。然后通过两种方式进行读写:send/recv等原生接口、bufferevent;
使用bufferevent方式进行数据读写
(1)创建基于套接字的bufferevent,与event_base、socket关联。即此bufferevent操作的数据是对应fd的缓冲区数据,event_base可以分发bufferevent的事件;
(2)[调用bufferevent_socket_connect,如果bufferevent还未设置socket,将为其分配一个新的流套接字,并且设置为非阻塞的;如果已经设置socket,将告知libevent套接字还未连接,直到连接成功之前不应该对其进行读取或者写入操作];
(3)调用bufferevent_setcb()设置事件回调;
(4)调用bufferevent_enable/bufferevent_disable启用/禁用EV_READ、EV_WRITE、EV_READ|EV_WRITE事件。读取或者写入事件被禁用时,bufferevent将不会试图进行数据读取或者写入;
(5)调用bufferevent_setwatermark设置读取/写入的高低水位;
(6)在事件回调中通过bufferevent_get_input、bufferevent_get_output获取bufferevent的输入/输出缓冲区evbuffer,然后通过相关接口(evbuffer_readln、evbuffer_add、evbuffer_remove等)对evbuffer进行操作,实现数据读取与写入;也可以直接使用bufferevent_write/bufferevent_write_buffer/bufferevent_read/bufferevent_read_buffer等接口对bufferevent进行操作,实现数据读取与写入,其实质也是对evbuffer进行操作。