本文参照《libevent参考手册》做的笔记,如有错,感谢指正!(以后文章要写在word文档里,然后复制到博客上,刚才写的文章是网络原因还是怎么了没有保存好,就没了!一次心痛的教训。咳咳咳…)
简要:
利用libevent编写服务端程序,主要有3部分
1.base = event_base_new();创建主通知链base;
2.listener_event = event_new(); 创建要监听的事件,
event_add();并将其加入到主通知链中;
event_free(); 释放由event_new申请的结构体
3.event_base_dispatch();//主循环
【bufferevent内容只写了一点,先占坑,后续补上笔记】
1. Libevent组件
libevent 由下列组件构成:
� evutil:用于抽象不同平台网络实现差异的通用功能。
� event 和event_base:libevent 的核心,为各种平台特定的、基于事件的非阻塞IO 后
端提供抽象API,让程序可以知道套接字何时已经准备好,可以读或者写,并且处理基
本的超时功能,检测OS 信号。
� bufferevent:为libevent 基于事件的核心提供使用更方便的封装。除了通知程序套接字
已经准备好读写之外,还让程序可以请求缓冲的读写操作,可以知道何时IO 已经真正
发生。(bufferevent 接口有多个后端,可以采用系统能够提供的更快的非阻塞IO 方式,
如Windows 中的IOCP。)
� evbuffer:在bufferevent 层之下实现了缓冲功能,并且提供了方便有效的访问函数。
� evhttp:一个简单的HTTP 客户端/服务器实现。
� evdns:一个简单的DNS 客户端/服务器实现。
� evrpc:一个简单的RPC 实现。
2. Event_base结构体
使用libevent 函数之前需要分配一个或者多个event_base 结构体。每个event_base 结构
体持有一个事件集合,可以检测以确定哪个事件是激活的。
如果设置event_base 使用锁,则可以安全地在多个线程中访问它。然而,其事件循环只能
运行在一个线程中。如果需要用多个线程检测IO,则需要为每个线程使用一个event_base。
每个event_base 都有一种用于检测哪种事件已经就绪的“方法”,或者说后端。可以识别的
方法有:
� select
� poll
� epoll
� kqueue
� devpoll
� evport
� win32
3. Event_base的建立与释放
代码:
struct event_base *event_base_new(void);
void event_base_free(struct event_base *base);
event_base_new()函数分配并且返回一个新的具有默认设置的event_base。函数会检测
环境变量,返回一个到event_base 的指针。如果发生错误,则返回NULL。选择各种方法
时,函数会选择OS 支持的最快方法。
使用完event_base 之后,使用event_base_free()进行释放。
4. Libevent事件集合
libevent 的基本操作单元是事件。每个事件代表一组条件的集合,这些条件包括:
� 文件描述符已经就绪,可以读取或者写入
� 文件描述符变为就绪状态,可以读取或者写入(仅对于边沿触发IO)
� 超时事件
� 发生某信号
� 用户触发事件
所有事件具有相似的生命周期:
调用libevent 函数设置事件并且关联到event_base 之后,事件进入“已初始化(initialized)”状态。
↓
此时可以将事件添加到event_base 中,这使之进入“未决(pending)”状态。
↓
在未决状态下,如果触发事件的条件发生(比如说,文件描述符的状态改变,或者超时时间到达),则事件进入“激活(active)”状态,(用户提供的)事件回调函数将被执行。
↓
如果配置为“持久的(persistent)”,事件将保持为未决状态。
↓
否则,执行完回调后,事件不再是未决的状态。
↓
删除操作可以让未决事件成为非未决(已初始化)的状态;
↓
添加操作可以让非未决事件再次成为未决的状态。
5. libEvent创建事件
代码:
struct event *event_new(struct event_base *base, evutil_socket_t fd, short what, event_callback_fn cb, void *arg);
void event_free(struct event *event);
使用event_new()接口创建事件。
event_new()试图分配和构造一个用于base 的新的事件。
what 参数是上述标志的集合。
如果fd 非负,则它是将被观察其读写事件的文件。
事件被激活时,libevent 将调用cb 函数,
传递这些参数:
文件描述符fd,表示所有被触发事件的位字段,
以及构造事件时的arg 参数。
发生内部错误,或者传入无效参数时,event_new()将返回NULL。
所有新创建的事件都处于已初始化和非未决状态,调用event_add()可以使其成为未决的。
要释放事件,调用event_free()。对未决或者激活状态的事件调用event_free()是安全
的:在释放事件之前,函数将会使事件成为非激活和非未决的。
代码:
在非未决的事件上调用event_add()将使其在配置的event_base 中成为未决的。成功时
函数返回0,失败时返回-1。如果tv 为NULL,添加的事件不会超时。否则,tv 以秒和微秒指定超时值。如果对已经未决的事件调用event_add(),事件将保持未决状态,并在指定的超时时间被重新调度。
事件标记
� EV_TIMEOUT
这个标志表示某超时时间流逝后事件成为激活的。构造事件的时候,EV_TIMEOUT 标志是
被忽略的:可以在添加事件的时候设置超时,也可以不设置。超时发生时,回调函数的what
参数将带有这个标志。
� EV_READ
表示指定的文件描述符已经就绪,可以读取的时候,事件将成为激活的。
� EV_WRITE
表示指定的文件描述符已经就绪,可以写入的时候,事件将成为激活的。
� EV_SIGNAL
用于实现信号检测,请看下面的“构造信号事件”节。
� EV_PERSIST
表示事件是“持久的”,请看下面的“关于事件持久性”节。
� EV_ET
表示如果底层的event_base 后端支持边沿触发事件,则事件应该是边沿触发的。这个标志
影响EV_READ 和EV_WRITE 的语义。
事件持久性
默认情况下,每当未决事件成为激活的(因为fd 已经准备好读取或者写入,或者因为超时),
事件将在其回调被执行前成为非未决的。如果想让事件再次成为未决的,可以在回调函数中
再次对其调用event_add()。
然而,如果设置了EV_PERSIST 标志,事件就是持久的。这意味着即使其回调被激活,事
件还是会保持为未决状态。如果想在回调中让事件成为非未决的,可以对其调用event_del
()。
每次执行事件回调的时候, 持久事件的超时值会被复位。因此, 如果具有
EV_READ|EV_PERSIST 标志,以及5秒的超时值,则事件将在以下情况下成为激活的:
� 套接字已经准备好被读取的时候
� 从最后一次成为激活的开始,已经逝去5秒。
只有超时的事件
代码:
#define evtimer_new(base, callback, arg) \
event_new((base), -1, 0, (callback), (arg))
#define evtimer_add(ev, tv) \
event_add((ev),(tv))
#define evtimer_del(ev) \
event_del(ev)
#define evtimer_pending(ev, what, tv_out) \
event_pending((ev), (what), (tv_out))
为使用方便,libevent 提供了一些以evtimer_开头的宏,用于替代event_*调用来操作纯超
时事件。使用这些宏能改进代码的清晰性。
6. 运行循环事件
一旦有了一个已经注册了某些事件的event_base,就需要让libevent 等待事件并且通知事件的发生。
event_base_loop()
代码:
#define EVLOOP_ONCE 0x01
#define EVLOOP_NONBLOCK 0x02
int event_base_loop(struct event_base *base, int flags);
默认情况下,event_base_loop()函数运行event_base 直到其中没有已经注册的事件为
止。执行循环的时候,函数重复地检查是否有任何已经注册的事件被触发(比如说,读事件
的文件描述符已经就绪,可以读取了;或者超时事件的超时时间即将到达)。如果有事件被
触发,函数标记被触发的事件为“激活的”,并且执行这些事件。
flags 参数标志:
EVLOOP_ONCE,循环将等待某些事件成为激活的,执行激活的事件直到没有更多的事件可以执行,然会返回。
EVLOOP_NONBLOCK,循环不会等待事件被触发:循环将仅仅检测是否有事件已经就绪,可以立即触发,如果有,则执行事件的回调。
event_base_dispatch()
代码:
int event_base_dispatch(struct event_base *base);
event_base_dispatch ( ) 等同于没有设置标志的event_base_loop ( )。所以,event_base_dispatch ( ) 将一直运行, 直到没有已经注册的事件了, 或者调用了event_base_loopbreak()或者event_base_loopexit()为止
7. Bufferevent
很多时候,除了响应事件之外,应用还希望做一定的数据缓冲。比如说,写入数据的时候,
通常的运行模式是:
� 决定要向连接写入一些数据,把数据放入到缓冲区中
� 等待连接可以写入
� 写入尽量多的数据
� 记住写入了多少数据,如果还有更多数据要写入,等待连接再次可以写入
这种缓冲IO 模式很通用,libevent 为此提供了一种通用机制,即bufferevent。bufferevent
由一个底层的传输端口(如套接字),一个读取缓冲区和一个写入缓冲区组成。与通常的事
件在底层传输端口已经就绪,可以读取或者写入的时候执行回调不同的是, bufferevent 在
读取或者写入了足够量的数据之后调用用户提供的回调。
共享公用接口的bufferevent 类型
� 基于套接字的bufferevent:使用event_*接口作为后端,通过底层流式套接字发送或者
接收数据的bufferevent
� 异步IO bufferevent:使用Windows IOCP 接口,通过底层流式套接字发送或者接收数
据的bufferevent(仅用于Windows,试验中)
� 过滤型bufferevent:将数据传输到底层bufferevent 对象之前,处理输入或者输出数据
的bufferevent:比如说,为了压缩或者转换数据。
� 成对的bufferevent:相互传输数据的两个bufferevent。
回调函数
每个bufferevent 有两个数据相关的回调:一个读取回调和一个写入回调