(1)event_base
每一个线程都有且仅有一个event_base,暂且称之为“事件管理器”吧(我自己随便起的名字),对应着一个struct event_base结构体,负责管理schedule托管给它的一系列事件(即下面要介绍的event)。当一个事件发生时,它负责在适当的时间(不一定是立即)去调用相关的回调函数。当回调函数执行完之后,再返回schedule其他事件。
(2)event 与 event_new函数
event_base内部有一个事件管理循环,阻塞在epoll/kqueue等系统调用上,直至有事件发生为止。event_base中管理的对象就是事件(event),这些事件通过event_new来创建和绑定。
struct event* listen_event = event_new( base, // 事件管理器对象 listener, // 监听的对象,如socket EV_READ | EV_PERSIST, // 事件类型及属性 do_accept, // 回调函数 (void*)base); // 传递给回调函数的参数
这里面涉及到一点libevent相关的类型声明,一个是事件类型及属性,包括如下种类:
(a)EV_TIMEOUT: 超时 (b)EV_READ: 只要网络缓冲中还有数据,回调函数就会被触发 (c)EV_WRITE: 只要塞给网络缓冲的数据被写完,回调函数就会被触发 (d)EV_SIGNAL: POSIX信号量,参考manual吧 (e)EV_PERSIST: 不指定这个属性的话,回调函数被触发后事件会被删除 (f)EV_ET: Edge-Trigger边缘触发,参考EPOLL_ET
回调函数的声明原型为:
typedef void(* event_callback_fn)( evutil_socket_t sockfd, // 关联的句柄\文件描述符 short event_type, // 事件类型 void *arg) // 传递给回调函数的参数
(3)event_add 函数
创建好的事件对象用event_add函数添加到消息循环队列中。
event_add( listen_event, // 事件对象 NULL); // struct timeval* 类型指针,用于设置超时时间,NULL表示无超时设置
(4)event_base_dispatch 函数
事件准备就绪之后,利用event_base_dispatch 函数启动消息循环。
event_base_dispatch(base); // 事件管理器对象
其实多看两遍就看熟悉了,libevent最简单的模型就是这四步,真的很简单。不过下面的例子还涉及到另一个问题,在网络编程中,当tcp服务器段accept建立了一个新的socket之后,如何准备读操作和写操作呢?我参考的原文中对这个问题给出了比较详细的历史描述。历史上,write和read事件分开管理,各自管理数据缓冲区等内容,操作起来非常的麻烦。在libevent2版本开始,引进了为bufferevent类型的管理器,这个管理器能够同时管理write\read\error事件。下面简要介绍一下使用步骤:
<A>设置SOCKET为非阻塞模式
evutil_make_socket_nonblocking(listener);
<B>创建bufferevent对象
struct bufferevent *bev = bufferevent_socket_new( base, // 事件管理器 fd, // 关联的句柄\文件描述符 BEV_OPT_CLOSE_ON_FREE); // 参数
<C>设置回调函数
bufferevent_setcb( bev, // bufferevent对象 read_cb, // 读操作回调函数 NULL, // 写操作回调函数 error_cb, // 错误处理回调函数 arg); // 参数
<D>启用事件管理
bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST);
<E>执行回调函数
这里涉及到三个回调函数:write_cb、read_cb和error_cb,其函数原型分别是:
// read_cb和write_cb的原型是 void read_or_write_callback(struct bufferevent *bev, void *arg) // error_cb的原型是 void error_cb(struct bufferevent *bev, short error, void *arg)
WSAData wsaData;
WSAStartup(MAKEWORD(2, 1), &wsaData); // Windows环境下网络编程必备
int ret = 0;
evutil_socket_t listener;
listener = socket(AF_INET, SOCK_STREAM, 0);
evutil_make_listen_socket_reuseable(listener); // 通用函数:设置SOCKET复用
// 绑定本地地址
SOCKADDR_IN sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ADDR_ANY;
sin.sin_port = htons(6789);
if (bind(listener, (SOCKADDR *)&sin, sizeof(sin)) < 0)
{
cout << "bind出错!" << endl;
return -1;
}
if (listen(listener, 32) < 0)
{
cout << "listen出错!" << endl;
return -1;
}
cout << "正在监听……" << endl;
// <A>设置SOCKET无阻塞模式
evutil_make_socket_nonblocking(listener);
// (1)创建“事件管理器”
struct event_base* base = event_base_new();
if (NULL == base)
{
cout << "event_base_new出错!" << endl;
return -1;
}
// (2)创建事件
struct event* listen_event = event_new(base, listener, EV_READ | EV_PERSIST, do_accept, (void*)base);
// (3)添加事件
event_add(listen_event, NULL);
// (4)启动事件管理循环
event_base_dispatch(base);
cout << "Done!" << endl;
return 0;
}
void do_accept(evutil_socket_t listener, short event, void *arg)
{
struct event_base* base = (struct event_base *)arg;
SOCKADDR_IN sin;
int slen = sizeof sin;
evutil_socket_t fd = accept(listener, (SOCKADDR *)&sin, &slen);
if (fd < 0)
{
cout << "accept出错!" << endl;
return;
}
//if (fd > FD_SETSIZE)
//{
// cout << "accept返回fd超出FD_SETSIZE限制" << endl;
// return;
//}
cout << "accept:fd=" << fd << endl;
// <B>创建“读写事件管理器”,自libevent2之后才有的
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
// <C>设置“读写事件管理器”的回调函数
bufferevent_setcb(bev, read_cb, NULL, error_cb, arg);
// <D>启动“读写事件管理器”
bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST);
}
void read_cb(struct bufferevent *bev, void *arg)
{
#define MAX_LINE 256
char szLine[MAX_LINE + 1];
evutil_socket_t fd = bufferevent_getfd(bev);
int n = 0;
while (n = bufferevent_read(bev, szLine, MAX_LINE), n > 0)
{
szLine[n] = '\0';
cout << "Read Line:" << szLine << endl;
bufferevent_write(bev, szLine, n);
}
}
void write_cb(struct bufferevent *bev, void *arg)
{
}
void error_cb(struct bufferevent *bev, short event, void *arg)
{
evutil_socket_t fd = bufferevent_getfd(bev);
cout << "error:fd=" << fd << endl;
if (event & BEV_EVENT_TIMEOUT)
{
cout << "Time out!" << endl;
}
else if (event & BEV_EVENT_EOF)
{
cout << "EOF!" << endl;
}
else if (event & BEV_EVENT_ERROR)
{
cout << "Error!" << endl;
}
bufferevent_free(bev);
}