Archive for the ‘C’ Category

winpcap with libevent failed!

Saturday, December 27th, 2008

libevent简短笔记

作者: gashero

目录

1   简介

使用libevent的简短笔记,加上我自己积累的技巧。

2   初始化

2.1   头文件与编译标志

头文件 event.h

编译标志 -levent 。在Windows下因为用到了socket函数,所以要同时加上 -lwsock32

在MinGW下编译时可以用 sh configure --prefix=/c/other_c_lib 这类方式来指定安装路径,然后可以安装过去,更方便。

2.2   建立event句柄

初始化 libevent 可用函数 event_init() 和 event_base_new() 。

实用 event_set() 来初始化事件结构体,然后用 event_add() 添加到事件循环。然后用 event_dispatch() 来启动事件循环和发布事件。

基本不周流程:

struct event ev; event_init(); event_set(&ev,s,EV_READ|EV_PERSIST,callback,&ev); event_add(&ev,NULL); event_dispatch();

注意要在callback函数中的耗时处理之前再次调用 event_add() 再次加入事件循环。所以需要使用arg来传递ev对象过来,有时候如果需要传递的东西过多,就需要一个结构体。

2.3   函数声明格式

struct event_base* event_init() :初始化事件API。

void event_set(struct event* ev, int fd, short events, void(*fn)(int, short, void*), void* arg) :构造一个事件结构用于准备添加。ev为事件句柄,fd为文件描述符,events为几种事件的组合,然后是回调函数和附加传输参数。可用的events组合包括: EV_TIMEOUT 、 EV_READ 、 EV_WRITE 、 EV_SIGNAL 、 EV_PERSIST 。fn回调函数的第一个参数为文件描述符,第二个是事件常量数字,第三个为传递过来的附加参数。

#define EV_TIMEOUT      0x01 #define EV_READ         0x02 #define EV_WRITE        0x04 #define EV_SIGNAL       0x08 #define EV_PERSIST      0x10

int event_add(struct event* ev, struct timeval* tv) :添加事件到监控器,可以指定超时为参数tv。

int event_del(struct event* ev) :删除事件。

2.4   超时事件

事件的超时功能是在 event_add() 函数中的第二个参数指定的。是一个timeval结构体。当tv参数为NULL时就没有超时。而当传递进去timeval结构体时就可以提供理论上高达1微秒的精度的超时。这里一个超时示例:

struct timeval tv; tv.tv_sec=1; tv.tv_usec=0; event_add(&ev,tv);

超时发生时可以在回调函数fn中的event参数得到值为EV_TIMEOUT的事件,注意不要与其他类型事件混淆。

2.5   初始化小心

注意声明的 struct event 结构体实例的作用域,在 main() 函数或者全局的尚且没事。如果只是在某个函数内部声明,则出函数后该结构体被回收则会出错。最好将这个结构体放在堆里面:

struct event* ev_accept=(struct event*)malloc(sizeof(struct event));

3   回调函数

回调函数声明格式:

void fn(int fd, short event, void* arg);

其中fd为文件句柄,可以按照特定的环境使用不同的读写函数。event为事件类型,见如上定义的5种事件类型中的前4种。

注意需要在回调函数中再次调用该事件的 event_add() 函数来确保事件继续下去。所以至少需要使用arg来传递ev结构体,如果需要传递的东西很多,则可能需要用arg来传递一个自定义的结构体,包含多个结构体的指针。

4   IO缓存

5   MinGW下的使用

一直没能正常启动,调试中。现在是用1.4.9。 event_dispatch() 函数出错返回-1。

event_dispatch()

int event_dispatch(void) {     return (event_loop(0)); }

event_loop(int flags)

int event_loop(int flags) {     return event_base_loop(current_base, flags); }

event_base_loop(struct event_base *base, int flags)

int event_base_loop(struct event_base *base, int flags) { }

调试发现 event_base_loop() 函数中有两处可能返回-1。加上打印调试以后发现是来自 event.c:533 处返回的。其调用了函数 evsel->dispatch() 所致。

全局变量 current_base 的流程:

/* Global state */ struct event_base *current_base=NULL;  struct event_base *event_init(void) {     struct event_base *base=event_base_new();     if (base != NULL)         current_base=base;     return (base); }

返回-1的是 res=evsel->dispatch(base,evbase,tv_p) 。实际相当于 current_base->evsel->dispatch(current_base,current_base->evbase,tv_p) 。经查其通过最终调用的结构体是 win32ops 。跟踪到被调用的实际上是 WIN32-Code/win32.c:345 中的 win32_dispatch() 函数。

win32_dispatch() 中的-1来自于 select() 的返回。使用了 GetLastError() 得到了错误码10038,大概意思是”在非socket上执行操作”。原来Windows下的 select() 真的不支持文件描述符的操作。那就算了吧。晚上试试加IOCP插件。