libevent就是对这些高效IO的封装,提供统一的API,便于开发。
libevent默认为单线程,可以配置为多线程。
一个线程一个event_base,对应一个 struct event_base 和时间管理器,用 schedule 托管给它一系列的event。
当时间发生后,event_base 会在合适的时间(不一定立即)调用绑定于该事件的函数,直到函数运行完,再返回schedule其余事件。
struct event_base *base = event_base_new();
assert(base != NULL);
event_base 内部阻塞于 epoll / kqueue
每个事件对应一个 struct event ,可以是监听一个 fd / posix 信号量
struct event 使用 event_new 创建绑定, event_add 来启用
补充下epoll的两个触发模式
如果accept 获得和client通信的sockfd之后,马上进行 recv() / send() , 线程就会阻塞于此。
因此应该创建一个event来托管这个sockfd。
从libevent2开始,提供了bufferevent
struct bufferevent 内建立了两个event(read / write)和对应的缓冲区:
当有数据被读入 input 时,read_cb 立刻调用
当output被输出完毕的时候,write_cb 立刻调用
* I/O 事件 *
void get_time(int fd, short event, struct event *arg)
{
localtime_r(&now, &t);
asctime_r(&t, buf);
write(fd, buf, strlen(buf));
//get system time, then write it
}
void connect_accept(int fd, short event, void *arg)
{
//offer a callback function to the event, accept a connection
struct sockaddr_in socket_in;
socklen_t len = sizeof(socket_in);
int accept_fd = accept(fd, (struct sockaddr *) &socket_in, &len);
struct event *ev = (struct event *)malloc(sizeof(struct event));
event_set(ev, accept_fd, EV_WRITE, (void *)get_time, ev);
event_add(ev, NULL);
}
int main(void)
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in socket_in;
bind(sockfd, (struct sockaddr *)&socket_in, sizeof(socket_in));
listen(sockfd, 5);
event_init(); //libevent initialization
struct event ev;
// 设置事件为可读,持续,监听的是sockfd,回调函数connect_accept
event_set(&ev, sockfd, EV_READ|EV_PERSIST, connect_accept, &ev);
//添加事件,未设置超时时间
event_add(&ev, NULL);
//进入libevent主循环,等待事件发生
event_dispatch();
return 0;
}
信号处理事件
static void signal_cb(int fd, short event, void *arg)
{
struct event *signal = arg;
printf("%s: got signal %d\n", __func__, EVENT_SIGNAL(signal)); // __func__ 是调用的函数名
if (called >= 2)
event_del(signal);
called++;
}
int main (int argc, char **argv)
{
struct event signal_int;
event_init(); //libevent initialization
event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb, &signal_int);
//设置事件属性为信号触发、持续,回调函数为con_accept()
event_add(&signal_int, NULL); //添加事件
event_dispatch();//进入libevent主循环
return 0;
}
常规超时处理
static void timeout_catch(int fd, short event, void *arg)
{
struct timeval tv;
struct event *timeout = arg;
int newtime = time(NULL);
printf("%s : called at %d\n", __func__, newtime - lasttime);
lasttime = newtime;
evutil_timerclear(&tv);
tv.tv_sec = 1;
event_add(timeout, &tv);
}
int main(void)
{
struct event timeout;
struct timeval tv;
event_init();
evtimer_set(&timeout, timeout_catch, &timeout);
//等价于
event_set(timeout, -1, 0, timeout_catch, &timeout);
evutil_timerclear(&tv);
tv.tv_sec = 1;
event_add(&timeout, &tv);
lasttime = time(NULL);
event_dispath(); //enter the loop
return 0;
}