./configure --prefix=xxx --disable-openssl # 针对libevent 2.1.12版本 我这里不需要ssl
make
make install
#include
#include
#include
#include
#include
#include
void udp_read_cb(int fd, short ev, void *p)
{
char buff[1024] = "";
struct sockaddr_in sa;
int sock_len = sizeof(sockaddr_in);
memset(&sa,0,sock_len);
ssize_t len = recvfrom(fd,buff,1023,0,(struct sockaddr*)&sa,(socklen_t*)&sock_len);
if (len >= 0)
{
printf("recv:%s",buff);
} else {
evutil_closesocket(fd);
}
}
int udp_handle_loop()
{
/* 1.Creating an event_base */
struct event_base* base = event_base_new();
if (NULL == base)
{
perror("event_base_new failed");
return -1;
}
printf("event base create\n");
/* Working with events
* Libevent’s basic unit of operation is the event. Every event represents a set of conditions, including:
* A file descriptor being ready to read from or write to.
* A file descriptor becoming ready to read from or write to (Edge-triggered IO only).
* A timeout expiring.
* A signal occurring.
* A user-triggered event.
*/
struct event* timer_ev = evtimer_new(base,timer_cb,NULL);
event_add(timer_ev,NULL);
/* udp socket */
int udpSocket = socket(AF_INET,SOCK_DGRAM,0);
if (-1 == udpSocket)
{
perror("socket failed");
return -1;
}
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(8080);
sa.sin_addr.s_addr = INADDR_ANY;
int opt = 1;
setsockopt(udpSocket,SOL_SOCKET,SO_REUSEADDR,(const void*)&opt,sizeof(opt));
int ret = ::bind(udpSocket,(struct sockaddr*)&sa,sizeof(sa));
if (ret < 0)
{
perror("bind error");
return -1;
}
struct event* udp_ev = event_new(base,udpSocket,EV_READ | EV_PERSIST,udp_read_cb,NULL);
struct timeval val;
val.tv_usec = 150;
event_add(udp_ev,NULL);
/* 2.Working with an event loop */
event_base_dispatch(base);
event_base_free(base);
evutil_closesocket(udpSocket);
}
1) 事件源
Linux上是文件描述符,Windows上就是Socket或者Handle了,这里统一称为“句柄集”;程序在指定的句柄上注册关心的事件,比如I/O事件。
2) event demultiplexer——事件多路分发机制
由操作系统提供的I/O多路复用机制,比如select和epoll。
程序首先将其关心的句柄(事件源)及其事件注册到event demultiplexer上;
当有事件到达时,event demultiplexer会发出通知“在已经注册的句柄集中,一个或多个句柄的事件已经就绪”;
程序收到通知后,就可以在非阻塞的情况下对事件进行处理了。
对应到libevent中,依然是select、poll、epoll等,但是libevent使用结构体eventop进行了封装,以统一的接口来支持这些I/O多路复用机制,达到了对外隐藏底层系统机制的目的。
3) Reactor——反应器
Reactor,是事件管理的接口,内部使用event demultiplexer注册、注销事件;并运行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数处理事件。
对应到libevent中,就是event_base结构体。
源码里最终还是调用io复用的接口,例如select、poll、epoll等
(以上转载自https://zhuanlan.zhihu.com/p/371223583)
struct event_base *
event_base_new(void)
{
struct event_base *base = NULL;
struct event_config *cfg = event_config_new();
if (cfg) {
base = event_base_new_with_config(cfg);
event_config_free(cfg);
}
return base;
}
struct event_base *
event_base_new_with_config(const struct event_config *cfg)
{
...
for (i = 0; eventops[i] && !base->evbase; i++) {
if (cfg != NULL) {
/* determine if this backend should be avoided */
if (event_config_is_avoided_method(cfg,
eventops[i]->name))
continue;
if ((eventops[i]->features & cfg->require_features)
!= cfg->require_features)
continue;
}
/* also obey the environment variables */
if (should_check_environment &&
event_is_method_disabled(eventops[i]->name))
continue;
base->evsel = eventops[i];// eventops
base->evbase = base->evsel->init(base);
}
...
}
/* Array of backends in order of preference. */
static const struct eventop *eventops[] = {
#ifdef EVENT__HAVE_EVENT_PORTS
&evportops,
#endif
#ifdef EVENT__HAVE_WORKING_KQUEUE
&kqops,
#endif
#ifdef EVENT__HAVE_EPOLL
&epollops,
#endif
#ifdef EVENT__HAVE_DEVPOLL
&devpollops,
#endif
#ifdef EVENT__HAVE_POLL
&pollops,
#endif
#ifdef EVENT__HAVE_SELECT
&selectops,
#endif
#ifdef _WIN32
&win32ops,
#endif
NULL
};
const struct eventop selectops = {
"select",
select_init,
select_add,
select_del,
select_dispatch,
select_dealloc,
0, /* doesn't need reinit. */
EV_FEATURE_FDS,
0,
};
static int
select_dispatch(struct event_base *base, struct timeval *tv)
{
...
res = select(nfds, sop->event_readset_out,
sop->event_writeset_out, NULL, tv);
...
}
https://libevent.org/doc/
http://www.wangafu.net/~nickm/libevent-book/