libevent官方文档学习笔记

libevent笔记:

1、  修改默认日志回调函数:回调函数格式<typedef void (*event_log_cb)(int severity, const char *msg);> 实现一个这样的函数,传入event_set_log_callback(),如果要恢复默认日志输出方式,则给该函数传入NULL。默认输出是到stderr。目前不建议在日志函数中调用libevent的API,否则可能导致意料之外的错误,未来可能修正该问题。

debug级日志默认不打开,需要在编译时让libevent支持,然后调用:
#define EVENT_DBG_NONE 0
#define EVENT_DBG_ALL 0xffffffffu
void event_enable_debug_logging(ev_uint32_t which);//打开debug日志,目前没有更多粒度的debug。
2、  在windows下,调用evthread_use_windows_threads,返回0表示使用windows的线程库,在Linux下,调用evthread_use_pthreads,返回0表示使用pthread线程库。
3、  已定义的锁类型:(1)0:普通的,非必须的递归锁;(2)EVTHREAD_LOCKTYPE_RECURSIVE:递归锁,不会阻塞再次请求它的线程。一旦被锁,其他线程可以任意多次请求该锁,直到锁被释放。(3)EVTHREAD_LOCKTYPE_READWRITE:允许多个线程拥有它进行读操作,但是只有一个线程可以持有它进行写操作。
        已定义的锁模型:(1)EVTHREAD_READ:只对READWRITE锁起作用:请求或释放读操作锁。(2)EVTHREAD_WRITE:只对READWRITE锁起作用:请求或释放写操作锁。(3)EVTHREAD_TRY:只对请求锁起作用:只有当可以立即请求到锁时请求锁。
        evthread_enable_lock_debugging:在任何锁被创建或使用前调用,安全起见,只有设置线程函数后(但是没启动线程)再调用,作用是调试锁异常,一旦锁发生(1)解锁未获得的锁(2)重复锁非递归锁时,发送assert失败。
4、  库能捕捉的event错误:(1)把一个未初始化的event结构当做已初始化;(2)重复初始化等待的event结构。调用event_enable_debug_mode启动以上错误的跟踪,但是会使用额外的内存和CPU,降低性能。该函数只能在event_base创建前使用。
5、  程序运行结束前需要释放所有events, event_base, bufferevent结构。
6、  程序运行结束时调用:libevent_global_shutdown释放库的全部剩余全局资源,但是不会释放上面的资源。不要在调用该函数后调用任何库函数,否则会导致不可预知的行为。
7、  一个event_base只能在一个线程进行循环,多线程时需要在每个线程创建一个event_base。
8、  已支持的event_base后台网络方法框架:select,poll,epoll,kqueue,devpoll,evport,win32。可以用宏关闭某个框架,也可以调用event_config_avoid_method方法。
9、  event_base_new:返回一个默认设置的event_base,后台会检测当前操作系统下最快的网络模型。
        event_base_new_with_config:参数event_config 保存了订制的event_base的参数。

        const char** event_get_supported_methods(void):返回event_base支持的方法

event_base_get_method:返回消息系统的底层框架,如kqueue,epoll。

        event_base使用完时,用event_base_free()释放。

        如果调用fork()后,还想继续使用event_base,需要调用event_reinit(base)。

        event_base_dispatch():启动event_base的循环,直到没有消息是等待或者活动的,或者调用了event_base_loopbreak或event_base_loopexit()。

10、libevent的基本操作单元是事件。
        每个事件代表了下列情况之一:(1)一个文件描述符用于准备读取或者准备写出;(2)一个文件描述符“变成”准备读取或者准备写出(只用于边界检测IO);(3)一个生命期超时;(4)一个信号发生;(5)一个用户定义的触发事件。
        事件生命周期:创建一个事件,和event_base关联被初始化,调用add在event_base中等待,状态变为pending,如果触发条件发生,状态变为active,调用用户处理回调。如果是持久性事件,则保持等待(回调后继续等待),如果是非持久性事件,当回调结束后停止等待。删除事件,会让它从等待变为非等待,添加事件,从非等待变为等待。
        构造事件对象:struct event * event_new(struct event_base *base, evutil_socket_t fd, short what, event_callback_fn cb, void *arg);
        事件回调:typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
        释放事件对象:void event_free(struct event *event);
        事件类型:#define EV_TIMEOUT      0x01
                            #define EV_READ         0x02
                            #define EV_WRITE        0x04
                            #define EV_SIGNAL       0x08
                            #define EV_PERSIST      0x10
                            #define EV_ET           0x20
        把事件变为等待:int event_add(struct event *ev, const struct timeval *tv);如果在已经等待的事件上调用带超时event_add,会重新开始计时等待。如果事件已经在等待,用NULL超时重新add,不会对等待产生影响。不要用time()函数设置超时时间,可能会导致时间计算错误,比如tv->tv_sec = time(NULL)+10;可能等待40年。。。
        把事件变为非等待、非活动:int event_del(struct event *ev);如果事件本身是非等待非活动的,不会产生任何影响。如果对一个活动态(activity)事件调用该函数,在回调函数执行前调用,那么回调函数不会再执行。
        int event_remove_timer(struct event *ev);:移除等待事件的超时属性而不删除其IO或信号属性。
        event_get_xxx:获取事件的相关属性
        struct event *event_base_get_running_event(struct event_base *base);获取当前运行的event,该函数只能在执行event_base循环的线程内调用,跨线程调用会导致未知行为。
        event_base_once:创建一次性使用的事件,在回调函数执行完后,库内部会释放event对象
        event_active:无论事件是否等待,都可以手动置为活动的。递归调用该函数,可能导致资源耗尽。
        event_base_init_common_timeout:一般情况下不同事件的timeout是不同的,所以库采用二叉堆的结构保存超时值,添加删除超时的时间复杂度是o(lgN),但是如果大量事件用同一个超时值,那么可以用该函数实现性能提升,改变超时值存储方式为双向链表,添加删除的时间复杂度为o(1)。返回值指向内部的一个timeval结构,如果使用相同超时值初始化event,需要用该返回值做为超时参数。
11、evutil_socket_t :代表了一个socket(即fd,文件描述符),windows下是一个SOCKET类型,Linux是一个整数(按序提供给创建socket者),socket()或者accept()函数返回
        evutil_make_socket_nonblocking:把一个socket设置为非阻塞
        evutil_make_listen_socket_reuseable:把地址变为可复用,即当使用该地址的监听socket一关闭,另一个socket可以立即使用该地址(对windows该函数无效)。
        evutil_make_socket_closeonexec:如果我们调用了exec(),则该socket需要关闭(对windows无效)。
        evutil_socketpair:设置两个互通的socket可以用于普通socketIO调用。
12、void evutil_secure_rng_get_bytes(void *buf, size_t n);:获取随机值,长度n,存储在buf
        int evutil_secure_rng_init(void);初始化随机数生成器,一般不需要调用
        void evutil_secure_rng_add_bytes(const char *dat, size_t datlen);增加用于生成随机数的信息熵池的大小,一般不需要调用
13、bufferevent:开发中经常需要用到缓冲区进行数据发送或者接收,一般过程是申请一块缓冲,可以发送时,把输入写入缓冲(长度不超过缓冲),等可以再次发送时,再次写入。bufferevent封装了该过程。
        (1)socket-based bufferevent:基于socket的bufferevent,封装了发送和接收数据操作的bufferevent,底层使用流socket(tcp),后台使用event_*接口。bufferevent内部有发送缓冲和接收缓冲,通过回调方式执行发送接收任务。
        (2)asynchronous-IO bufferevents:异步IO的,使用windowsIOCP接口发送接收数据,底层使用流socket。
        (3)filtering bufferevents:过滤的,在数据被发往底层bufferevent对象前,对将要输入或输出的数据进行处理,比如压缩或转换。
        (4)paired bufferevents:成对的,相互间传输数据。
          bufferevent目前只支持TCP协议。
13、watermarks:数据水位线,分四种:(1)Read low-water mark:读低水位线,当一个读事件发生,并且bufferevent输入缓冲保持该水平或稍高,则调用读回调函数。默认值是0,所以每个读回调中的读结果都会被处理。(2)Read high-water:如果bufferevent的输入缓冲达到这个等级,bufferevent会停止读过程,直到有足够的数据从输入缓冲取走,watermark再降到低位。默认值没有上限(所以不可能达到),所以我们永远不会因为输入缓冲大小的问题而停止读操作。(3)Write low-water mark:默认值是0,发送缓冲为空时调用写回调。(4)Write high-water mark:bufferevent对象不会直接使用该值,它在bufferevent底层传输时有特殊含义,和filter bufferevent相关。
14、bufferevent支持非数据相关的事件回调(和错误回调),包括:BEV_EVENT_READING,BEV_EVENT_WRITING,BEV_EVENT_ERROR(获取更多信息可调用EVUTIL_SOCKET_ERROR),BEV_EVENT_TIMEOUT,BEV_EVENT_EOF,BEV_EVENT_CONNECTED。
        延迟回调:一般来说当条件满足时读写回调会立即进行,但是可能发生各种异常,比如两个bufferevent,一个读一个写,有可能持续写导致缓冲区溢出,所以可以设置回调为延迟的,把它作为event_poll()队列中的一个事件,当对应操作执行完后执行本操作。
15、bufferevent的配置项:BEV_OPT_CLOSE_ON_FREE,BEV_OPT_THREADSAFE,BEV_OPT_DEFER_CALLBACKS,BEV_OPT_UNLOCK_CALLBACKS。
16、socket-based bufferevent用法:(1)创建struct bufferevent *bufferevent_socket_new(struct event_base *base,evutil_socket_t fd,enum bufferevent_options options);传递给它的socket必须是非阻塞的,如果传入的是-1,表示稍后会创建socket。
      (2-1)连接:int bufferevent_socket_connect(struct bufferevent *bev,struct sockaddr *address, int addrlen);如果上一步未传入socket,则本函数内部创建并设置为非阻塞时,如果已传入,则在连接成功前不能读取数据,但是可以向输出缓冲写入数据。成功连接后,回调收到BEV_EVENT_CONNECTED消息,但是如果用系统的connect()函数去连接,那么回调函数函数收到可写消息。
      (2-2)连接:bufferevent_socket_connect_hostname,通过解析主机名连接主机。
17、通用bufferevent函数:bufferevent_setcb:设置回调函数,三个回调(读写错误)的用户参数是共用的,改变了一个会作用到其他两个。
        bufferevent_enable:设置EV_READ, EV_WRITE, 或者EV_READ|EV_WRITE事件。默认情况下,新创建的bufferevent具有写事件,没有读事件。
        struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);获取输入缓冲
        struct evbuffer *bufferevent_get_output(struct bufferevent *bufev);获取输出缓冲
        如果读入因为数据太多被停滞或者输出因为数据量太少被停滞,这是从读入缓冲移除数据或向输入缓冲写入数据,会重新启动输入输出过程。
        int bufferevent_write(struct bufferevent *bufev,const void *data, size_t size);
        int bufferevent_write_buffer(struct bufferevent *bufev,struct evbuffer *buf);输出数据。
       size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
        int bufferevent_read_buffer(struct bufferevent *bufev,struct evbuffer *buf);读入数据
       void bufferevent_set_timeouts(struct bufferevent *bufev,const struct timeval *timeout_read, const struct timeval *timeout_write);设置读写超时,如果设置为NULL,则是移除超时。只有在读过程或者写过程中超时才有效,如果没有读写动作则超时不生效。
        int bufferevent_flush(struct bufferevent *bufev,short iotype, enum bufferevent_flush_mode state);启动flush,告诉bufferevent底层强行读取或写入尽可能多的数据。iotype可能是EV_READ, EV_WRITE, 或者EV_READ|EV_WRITE,state可能是BEV_NORMAL, BEV_FLUSH, or BEV_FINISHED。socket-based bufferevent不支持该函数。
18、非通用bufferevent函数(和bufferevent类型相关):
        int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd);
       evutil_socket_t bufferevent_getfd(struct bufferevent *bufev);只有socket-based bufferevent支持这组函数。
       void bufferevent_lock(struct bufferevent *bufev);
       void bufferevent_unlock(struct bufferevent *bufev);锁、解锁bufferevent。递归锁。
19、evbuffer:控制网络传输中缓冲区部分的功能。
       struct evbuffer *evbuffer_new(void);
       void evbuffer_free(struct evbuffer *buf);生成空的evbuffer,释放它和它的所有数据。
       int evbuffer_enable_locking(struct evbuffer *buf, void *lock);
       void evbuffer_lock(struct evbuffer *buf);
       void evbuffer_unlock(struct evbuffer *buf);默认情况下,evbuffer是线程不安全的,如果lock是NULL,libevent会创建一个新的lock。如果只对evbuffer执行单个单线程操作,则不需要加锁。
       size_t evbuffer_get_contiguous_space(const struct evbuffer *buf);evbuffer可能有多块存储空间,返回第一块的长度(连续存储空间长度)
       int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen);向buf写入数据。
       int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
       int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap);向evbuffer缓冲最后格式化添加数据,用法和标准库一样。
       int evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src);把src的数据转移到dst中(不是复制)       
       int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,
       size_t datlen);从src转移datlen长的数据到dst的末尾(不是复制)。
       int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size);
       int evbuffer_prepend_buffer(struct evbuffer *dst, struct evbuffer* src);在evbuffer缓冲头部之前前述两个操作。
       evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size):线性取出缓冲开头的size个字节。如果size大于缓冲实际长度则返回NULL。否则返回执行buf第一个字节的指针。
       int evbuffer_drain(struct evbuffer *buf, size_t len);移除buf开头的len数据。
       int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen);拷贝datlen数据到data,并从buf中移除。
       ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data, size_t datlen);

       ev_ssize_t evbuffer_copyout_from(struct evbuffer *buf,const struct evbuffer_ptr *pos, void *data_out, size_t datlen);拷贝缓冲开始或pos处的datlen字节,但不移除他们。

       enum evbuffer_eol_style {

        EVBUFFER_EOL_ANY,
        EVBUFFER_EOL_CRLF,
        EVBUFFER_EOL_CRLF_STRICT,
        EVBUFFER_EOL_LF,
        EVBUFFER_EOL_NUL
       };
       char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out,
       enum evbuffer_eol_style eol_style);按行读取数据,宏定义了行结尾字符。
       struct evbuffer_ptr {
               ev_ssize_t pos;
               struct {
                /* internal fields */
               } _internal;
       };指向evbuffer内部的一个位置,包含了可迭代访问的数据。pos是唯一可public访问的域。
       struct evbuffer_iovec {
               void *iov_base;
               size_t iov_len;
       };
       int evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len,struct evbuffer_ptr *start_at,struct evbuffer_iovec *vec_out, int n_vec);获取buffer的内存块指针,vec_out是n_vec个evbuffer_iovec数组,每一个保存一个buffer内部内存块指针和块长度。如果len为负,表示获取所有数据块,否则获取len长度的数据保存的内存块,返回值是实际可填充的evbuffer_iovec数量(可先调用一次用返回值新建vec数组再获取指针)。
       int evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size,struct evbuffer_iovec *vec, int n_vecs);重置buf的内存空间。vec指向重置后的内存块,目前只支持最多两个内存块。
       int evbuffer_commit_space(struct evbuffer *buf,struct evbuffer_iovec *vec, int n_vecs);直接在evbuffer的内存块中写入数据。
       int evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd);
       int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd,ev_ssize_t howmuch);把buffer开头的howmuch字节写入fd
       int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch);从fd读取howmuch字节到buffer中。
       一般情况下不需要调用这组函数,bufferevent底层会调用。
       typedef void (*evbuffer_ref_cleanup_cb)(const void *data,size_t datalen, void *extra);
       int evbuffer_add_reference(struct evbuffer *outbuf,const void *data, size_t datlen,evbuffer_ref_cleanup_cb cleanupfn, void *extra);用非拷贝的方式添加数据,即只保存指向数据的指针,因此指针生命周期需要足够长,当evbuffer不再需要该数据时,会调用回调函数。
       int evbuffer_add_file(struct evbuffer *output, int fd, ev_off_t offset,size_t length);直接添加文件数据,视操作系统决定是否需要拷贝文件内容到内存。fd代表已打开文件的描述符(不是socket)
       int evbuffer_add_buffer_reference(struct evbuffer *outbuf,struct evbuffer *inbuf);把源buf添加到目的buf,保存源buf指针而不拷贝内容。outbuf是源buf,inbuf是目的buf。不能嵌套使用,即一个buf即是源又是目的。
20、Connection listeners: accepting TCP connections:连接、监听、接受连接的TCP连接封装
       struct evconnlistener *evconnlistener_new(struct event_base *base,evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,evutil_socket_t fd);
       struct evconnlistener *evconnlistener_new_bind(struct event_base *base,
       evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
       const struct sockaddr *sa, int socklen);
       void evconnlistener_free(struct evconnlistener *lev);

猜你喜欢

转载自blog.csdn.net/blwinner/article/details/55102303