在linux下学习了一段时间网络编程,主体的编程思路懂得差不多了(windows下做过,基本差不多),所以决定看些开源的库来实际学习一下.故选择了相对简练的libevent进行代码阅读.在读的过程中,参考了一位已经将libevent解析的很清楚的大神的博客.http://blog.youkuaiyun.com/sparkliang/article/details/4957667
这里算是自己学习过程中的一些结果写出来,供大家参考.源码使用的也是1.4.13稳定版.
一.支持多种网络模型原理
在libevent官网上,大家都看到libevent支持各种不同的网络模型,而且还支持跨平台.其实这部分如果从C++的编程思想来看,大家可以很容易想到,利用类的继承加上虚函数实现.
1.首先构建一个基类,定义接口函数
2.继承这个基类,实现多个不同网络模型方法(接口)
3.使用指向子类的基类指针实现统一访问
由于libevent使用纯C代码实现,所以上面的部分只是给大家回忆一下C++怎么实现的而已,但是基本思想差不多一致.下面结合libevent的实际代码说明.
1.基类与接口,本部分代码位于event-internal.h文件中
struct eventop {
const char *name;
void *(*init)(struct event_base *);
int (*add)(void *, struct event *);
int (*del)(void *, struct event *);
int (*dispatch)(struct event_base *, void *, struct timeval *);
void (*dealloc)(struct event_base *, void *);
/* set if we need to reinitialize the event base */
int need_reinit;
};
eventop就是这个基类,这个也算是整个libevent工作过程中最实际干活的部分.eventop使用函数指针的方法去构建统一的接口,实现的接口包括初始化,添加事件,删除事件,分发事件,注销五个.事件的概念后续再说.
2.不同网络模型构建,本部分代码位于WIN32-Code/win32.c,devpoll.c,epoll.c,evport.c,kqueue.c,poll.c,select.c
本版本libevent支持7种不同网络模型,从上述文件即可看出.这里选用select.c进行解释,下面的代码位于select.c文件
static void *select_init (struct event_base *);
static int select_add (void *, struct event *);
static int select_del (void *, struct event *);
static int select_dispatch (struct event_base *, void *, struct timeval *);
static void select_dealloc (struct event_base *, void *);
const struct eventop selectops = {
"select",
select_init,
select_add,
select_del,
select_dispatch,
select_dealloc,
0
};
可以看出,首先第一步定义了5个select接口函数,并且构建了eventop实例selectops,注意这是个全局变量.同样大家可以从其他六个文件中找到相同的代码与全局变量定义.
下面的代码位于event.c文件中
#ifdef HAVE_EVENT_PORTS
extern const struct eventop evportops;
#endif
#ifdef HAVE_SELECT
extern const struct eventop selectops;
#endif
#ifdef HAVE_POLL
extern const struct eventop pollops;
#endif
#ifdef HAVE_EPOLL
extern const struct eventop epollops;
#endif
#ifdef HAVE_WORKING_KQUEUE
extern const struct eventop kqops;
#endif
#ifdef HAVE_DEVPOLL
extern const struct eventop devpollops;
#endif
#ifdef WIN32
extern const struct eventop win32ops;
#endif
/* In order of preference */
static const struct eventop *eventops[] = {
#ifdef HAVE_EVENT_PORTS
&evportops,
#endif
#ifdef HAVE_WORKING_KQUEUE
&kqops,
#endif
#ifdef HAVE_EPOLL
&epollops,
#endif
#ifdef HAVE_DEVPOLL
&devpollops,
#endif
#ifdef HAVE_POLL
&pollops,
#endif
#ifdef HAVE_SELECT
&selectops,
#endif
#ifdef WIN32
&win32ops,
#endif
NULL
};
上面的代码使用预编译的方式来声明七种全局网络方法,具体使用那个网络方法就看编译环境了.3.统一使用方法
这里用的时候就涉及到libevent的核心成员event_base了,event_base就类似于线程池模型中的管理模块一样.所有的事件都是由它管理的.先来看看event_base的结构定义.下面的代码位于event-internal.h中.
struct event_base {
const struct eventop *evsel;
void *evbase;
int event_count; /* counts number of total events */
int event_count_active; /* counts number of active events */
int event_gotterm; /* Set to terminate loop */
int event_break; /* Set to terminate loop immediately */
/* active event management */
struct event_list **activequeues;
int nactivequeues;
/* signal handling info */
struct evsignal_info sig;
struct event_list eventqueue;
struct timeval event_tv;
struct min_heap timeheap;
struct timeval tv_cache;
};
可以看得出来,event_base有eventop这个一个成员变量evsel,很明显,这个变量就是后续实际干活的实例了.
现在来看event_base是怎么初始化的,常用的是这么一个函数event_base_new().下面代码位于event.c文件中,已经删除无关部分
struct event_base *
event_base_new(void)
{
int i;
struct event_base *base;
if ((base = calloc(1, sizeof(struct event_base))) == NULL)
event_err(1, "%s: calloc", __func__);
/*删除部分代码*/
base->evbase = NULL;
for (i = 0; eventops[i] && !base->evbase; i++) {
base->evsel = eventops[i];
base->evbase = base->evsel->init(base);
}
/*删除部分代码*/
return (base);
}
这个event_base的evsel是使用咱们在第二步说到的全局变量赋值的,这样整个地方就很明了了.
要是还有些问题的话,再来看看event_base的核心函数event_base_loop,这是整个libevent事件的分发核心了.位于event.c文件中,已删除部分无关部分
int
event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
void *evbase = base->evbase;
struct timeval tv;
struct timeval *tv_p;
int res, done;
/* clear time cache */
base->tv_cache.tv_sec = 0;
if (base->sig.ev_signal_added)
evsignal_base = base;
done = 0;
while (!done) {
/* Terminate the loop if we have been asked to */
/* 删除部分代码 */
/* clear time cache */
base->tv_cache.tv_sec = 0;
res = evsel->dispatch(base, evbase, tv_p);
if (res == -1)
return (-1);
gettime(base, &base->tv_cache);
/* 删除部分代码 */
}
/* clear time cache */
base->tv_cache.tv_sec = 0;
event_debug(("%s: asked to terminate loop.", __func__));
return (0);
}
从上面的整个过程中,已经很清晰的解释了libevent是怎么构建不同网络模型的.
剩下的部分咱们后续再说,重点还没说到....