libevent的使用过程比较简单,就是创建一个监听事件,包含fd,事件回调等参数,然后添加到libevent事件里面,调用libevent
事件调度函数loop起来。看下面的例子
using namespce std;
#define SERVER_ADDR "127.0.0.1"
#define SERVER_PORT 8888
//事件base
struct event_base* base;
//读事件回调函数
void onRead(int iCliFd,short iEvent,void *arg)
{
int iLen;
char buf[1500];
iLen recv(iCliFd,buf,1500,0);
if(iLen <= 0)
{
cout <<"Client Close"
struct event *pEvRead = (struct event*)arg;
event_del(pEvRead);
delete pEvRead;
close(iCliFd);
return ;
}
buf[iLen] = 0;
cout << "Client Info:" << buf << endl;
struct bufferevent* buf_ev;
buf_ev = bufferevent_new(iCliFd, NULL, NULL, NULL, NULL);// 创建一个新的事件
/*
每个 bufferevent 有四个水位:
读取低水位 : 读取操作使得输入缓冲区的数据量在此级别或者更高时 ,读取回调将被调用。默认值为 0,所以每个读取操作都会导致读取回调被调用。
读取高水位 : 输入缓冲区中的数据量达到此级别后, bufferevent 将停止读取,直到输入缓冲区中足够量的数据被抽取 ,使得数据量低于此级别 。默认值是无限 ,所以永远不会因为输入缓冲区的大小而停止读取。
写入低水位 : 写入操作使得输出缓冲区的数据量达到或者低于此级别时 ,写入回调将被调用。默认值是 0,所以只有输出缓冲区空的时候才会调用写入回调。
写入高水位 : bufferevent 没有直接使用这个水位。它在 bufferevent 用作另外一 个 bufferevent 的底层传输端口时有特殊意义。
*/
char MESSAGE[]="welcome to server..";
bufferevent_write(buf_ev, MESSAGE, strlen(MESSAGE));
}
//连接请求事件回调函数
void onAccept(int iSvrFd,short iEvent,void *arg)//Listen回调函数
{// Listen事件也是监听的读事件,
int iCliFd;
struct sockaddr_in sCliAddr;
socklen_t iSinSize = sizeof(sCliAddr);
iCliFd = accept(iSvrFd,(struct sockaddr*)&sCliAddr,&iSinSize);
//链接注册为新事件(EV_PERSIST 为事件触发后不默认删除)
struct event* pEvRead = new event;
event_set(pEvRead, iCliFd, EV_READ|EV_PERSIST, onRead, pEvRead);
event_base_set(base,pEvRead);
event_add(pEvRead,NULL);
struct bufferevent* buf_ev;
buf_ev = bufferevent_new(iCliFd,NULL,NULL,NULL,NULL);
char MESSAGE[]="welcome to server..";
//这里看一下读取水位的作用,
/*
buf_ev->wm_read.high = 4096; // 如果本地应用层buffer比4096小,就不读取了。原因是每次读取都是按4096为单位读取的
static void bufferevent_inbuf_wm_check(struct bufferevent *bev)
{
if (!bev->wm_read.high)
return;
if (!(bev->enabled & EV_READ))
return;
if (evbuffer_get_length(bev->input) < bev->wm_read.high)
return;
bufferevent_trigger(bev, EV_READ, BEV_OPT_DEFER_CALLBACKS);
}
*/
bufferevent_write(buf_ev, MESSAGE, strlen(MESSAGE));//监听到事件后,
cout<<"a client connect:"<<iclifd<<endl;< p="">
}
int main()
{
int iSvrFd;
struct sockaddr_in sSvrAddr;
memset(&sSvrAddr,0,sizeof(sSvrAddr));
sSvrAddr.sin_family = AF_INET;
sSvrAddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
sSvrAddr.sin_port = htons(SERVER_PORT);
iSvrFd = socket(AF_INET,SOCK_STREAM,0);
bind(iSvrFd,(struct sockaddr*)&sSvrAddr,sizeof(sSvrAddr));
listen(iSvrFd,10);
//初始化base
base = (struct event_base*)event_init();
struct event evListen;
//设置事件
event_set(&evListen, iSvrFd, EV_READ|EV_PERSIST, onAccept, NULL);
/*
默认情况下,任何时候一个挂起的事件被激活(因为他的fd准备好了读或者写,或者因为他的超时过期了),它会在回调函数执行之前变为非挂起。如果你想让事件再次挂起,你需要在回调函数内部调用event_add()
。
如果一个事件被设置了EV_PERSIST
,那么这个事件就是持续化的,意思就是这个事件会保持挂起状态,即使回调函数被执行。如果你想让它变为非挂起状态,可以在回调函数中调用event_del()
。
任何时候事件的回调函数触发都会重置持续化事件中的超时状态。因此,如果的事件有EV_READ/EV_PERSIST
并且设置了5秒超时,那么有两种情况会触发这个事件:
-
当socket可以进行读取的时候
-
当5s超时到期的时
*/
//设置为base 事件base
event_base_set(base,&evListn);
//添加事件
event_add(&evListen,NULL);// 每个event 添加都有一个超时参数,
//如果不设置就是默认的超时参数
//添加事件
event_base_dispatch(base);//主循环LOOP
return 0;
}
//libevent内部超时事件实现
static void
timeout_process(struct event_base *base)
{
/* Caller must hold lock. */
struct timeval now;
struct event *ev;
if (min_heap_empty_(&base->timeheap)) {
return;
}
gettime(base, &now);
while ((ev = min_heap_top_(&base->timeheap))) { // 最小堆弹出,
//这里的定时器是一个小根堆结构
if (evutil_timercmp(&ev->ev_timeout, &now, >))
break;
/* delete this event from the I/O queues */
event_del_nolock_(ev, EVENT_DEL_NOBLOCK);
event_debug(("timeout_process: event: %p, call %p",
ev, ev->ev_callback));
event_active_nolock_(ev, EV_TIMEOUT, 1);
}
}
event_base_dispatch()-->timeout_process()
-->堆定时器处理。
epoll事件激活
const struct eventop epollops = {
"epoll",
epoll_init,
epoll_nochangelist_add,
epoll_nochangelist_del,
epoll_dispatch,
epoll_dealloc,
1, /* need reinit */
EV_FEATURE_ET|EV_FEATURE_O1|EV_FEATURE_EARLY_CLOSE,
0
};
res = evsel->dispatch(base, tv_p);
epoll_dispatch(struct event_base *base, struct timeval *tv)
{
res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
//激活事件
for (i = 0; i < res; i++) {
int what = events[i].events;
short ev = 0;
#ifdef USING_TIMERFD
if (events[i].data.fd == epollop->timerfd)
continue;
#endif
if (what & (EPOLLHUP|EPOLLERR)) {
ev = EV_READ | EV_WRITE;
} else {
if (what & EPOLLIN)
ev |= EV_READ;
if (what & EPOLLOUT)
ev |= EV_WRITE;
if (what & EPOLLRDHUP)
ev |= EV_CLOSED;
}
if (!ev)
continue;
evmap_io_active_(base, events[i].data.fd, ev | EV_ET);
}
}