libevent之reactor
转自http://www.cnblogs.com/secondtonone1/p/5535722.html 最近自学libevent事件驱动库,参考的资料为libevent2.2版本以及张亮提供的《Libevent源码深度剖析》, 参考资料:http://blog.csdn.net/sparkliang/article/details/4957667 libevent好处之类的就不赘述了,libevent和libiop,redis等一样都是采用事件回调机制,这种模式 被称作Reactor模式。正常事件处理流程是应用程序调用某个接口触发某个功能,而Reactor模式需要 我们将这些接口和宿主指针(谁调用这些接口)注册在Reactor,在合适的时机Reactor使用宿主指针 调用注册好的回调函数。
一: Reactor基本知识 Reactor 模式是编写高性能网络服务器的必备技术之一,它具有如下的优点: Reactor模式框架 1) Handle 意思为句柄,在Linux表示文件描述符,在windows是socket或者handle。 2)EventDemultiplexer 表示事件多路分发机制,调用系统提供的I/O多路复用机制, 比如select,epoll,程序先将关注的句柄注册到EventDemultiplexer上,当有关注的事件 到来时,触发EventDemultiplexer通知程序,程序调用之前注册好的回调函数完成消息 相应。对应到 libevent 中,依然是 select、 poll、 epoll 等,但是 libevent 使用结构体eventop 进行了封装,以统一的接口来支持这些 I/O 多路复用机制,达到了对外隐藏底层系统机制的目的。 3)Reactor——反应器 Reactor,是事件管理的接口,内部使用 event demultiplexer 注册、注销事件;并运行事 4) Event Handler——事件处理程序 二:如何使用libevent库提供的API 1)首先初始化 libevent 库,并保存返回的指针 2)设置event属性和回调函数 调用函数void event_set(struct event *ev,int fd,short event,void (*cb)(int, 每个参数的意义: ev:执行要初始化的 event 对象; 分别是关注的fd,关注的事件类型(读/写/信号),回调函数的参数void* arg,调用时由 event_base 负责传入,按顺序,实际上就是 event_set 时的 fd,event 和 arg; arg:传递给 cb 函数指针的参数; 由于定时事件不需要 fd,并且定时事件是根据添加时( event_add)的超时值设定的,因此 3)设置 event 从属的 event_base 看一下libevent提供的sample int main(int argc,char **argv) { struct event evfifo; #ifdef WIN32 HANDLE socket; /* Open a file. */ socket = CreateFileA("test.txt",0); line-height:1.5!important"> open File */ GENERIC_READ, open for reading */ 0,0); line-height:1.5!important"> do not share */ NULL,0); line-height:1.5!important"> no security */ OPEN_EXISTING,0); line-height:1.5!important"> existing file only */ FILE_ATTRIBUTE_NORMAL,0); line-height:1.5!important"> normal file */ NULL); no attr. template */ if (socket == INVALID_HANDLE_VALUE) return 1; #else struct stat st; const char *fifo = event.fifo"; int socket; if (lstat(fifo,&st) == 0) { if ((st.st_mode & S_IFMT) == S_IFREG) { errno = EEXIST; perror(lstat"); exit(1); } } unlink(fifo); if (mkfifo(fifo,0600) == -1) { perror(mkfifo"); exit(1); } Linux pipes are broken,we need O_RDWR instead of O_RDONLY */ #ifdef __linux socket = open(fifo,O_RDWR | O_NONBLOCK,128); line-height:1.5!important">0); #else socket = open(fifo,O_RDONLY | O_NONBLOCK,255); line-height:1.5!important">#endif if (socket == -open1); } fprintf(stderr,Write data to %sn",fifo); #endif Initalize the event library */ event_init(); Initalize one event */ #ifdef WIN32 event_set(&evfifo,(evutil_socket_t)socket,EV_READ,fifo_read,&evfifo); #else event_set(&evfifo,socket,0); line-height:1.5!important"> Add it to the active events,without a timeout */ event_add(&evfifo,NULL); event_dispatch(); #ifdef WIN32 CloseHandle(socket); return (0); } main函数里调用event_init()初始化一个event_base, 之后调用event_set对event设置了回调函数和读事件关注, event_add将此事件加入event队列里,超时设置为空 最后调用event_dispatch()进行事件轮训派发。 fifo_read是一个回调函数,格式就是之前说的cb格式 static void
fifo_read(evutil_socket_t fd,255); line-height:1.5!important">short event,255); line-height:1.5!important">void *arg)
{
char buf[255];
int len;
event *ev = arg;
#ifdef WIN32
DWORD dwBytesRead;
Reschedule this event */
event_add(ev,NULL);
fprintf(stderr,0); line-height:1.5!important">fifo_read called with fd: %d,event: %d,arg: %pnint)fd,255); line-height:1.5!important">event,arg);
#ifdef WIN32
len = ReadFile((HANDLE)fd,buf,255); line-height:1.5!important">sizeof(buf) - 1,&dwBytesRead,NULL);
Check for end of file. */
if (len && dwBytesRead == 0) {
fprintf(stderr,0); line-height:1.5!important">End Of File");
event_del(ev);
return;
}
buf[dwBytesRead] = ' |