Libevent——高性能I/O框架库
底层封装了select,poll,epoll,便于使用
I/O框架库以库函数的形式,封装了较为底层的系统调用,给应用程序提供了一组更便于使用的接口。
特点:1.跨平台,2.统一事件源(I/O事件、信号、定时事件),3.libevent_pthreads库保证线程安全,4.基于Reactor模式(主线程只负责事件检测)
Libevent框架:事件注册交给libevent,事件循环自动用select、poll、epoll检测事件,检测到后通知libevent调用注册的回调函数。
Libevent使用
使用分为三步:
1.注册事件交给libevent
struct event_base *实例名= event_init();//struct event_base创建实例,初始化
struct event *事件名= event_new(实例,信号,事件,回调函数,arg)//需要自己写回调函数
void 回调函数名(int fd, short event, void *arg)
{
处理方式
}event_add(事件名, NULL);
2.启动事件循环,内部自动调用select或poll或epoll
event_base_dispatch(实例名);
3.处理完成后释放
event_free(事件名);
event_base_free(实例名);
其中libevent支持的事件类型有:
#define EV_TIMEOUT 0x01 /* 定时事件 */
#define EV_READ 0x02 /* 可读事件 */
#define EV_WRITE 0x04 /* 可写事件 */
#define EV_SIGNAL 0x08 /* 信号事件 */
#define EV_PERSIST 0x10 /* 永久事件 *///永久性事件取出检测完后还会放回待检测的链表中,可以一直检测
/* 边沿触发事件,需要 I/O 复用系统调用支持,比如 epoll */
#define EV_ET 0x20
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <event.h>//libevent
#include <signal.h>//信号
//处理信号事件的回调函数
void signal_cb(int fd, short event, void *arg)
{
if(event & EV_SIGNAL)//event短整型直接按位与比较
printf("%d Signal triggered\n",fd);
}
//处理定时事件的回调函数
void timeout_cb(int fd, short event, void *arg)
{
if(event & EV_TIMEOUT)
printf("timeout\n");
}
int main()
{
struct event_base *base = event_init();//struct event_base创建实例,初始化
assert(base!=NULL);
//1.注册事件
//定义一个信号事件
// event_new(属于的实例,信号,事件,处理事件的回调函数,传给回调函数的参数)
struct event*signal_event = event_new(base,SIGINT,EV_SIGNAL|EV_PERSIST,signal_cb,NULL);
assert(signal_event!=NULL);
//添加事件
event_add(signal_event, NULL);
//创建定时事件
//struct event *timeout_event = evtimer_new(base, timeout_cb, NULL);
struct event*timeout_event = event_new(base,-1,EV_TIMEOUT,timeout_cb,NULL);
//等待时长
struct timeval tv = { 5, 0 };
//添加事件
event_add(timeout_event, &tv);
//2.启动事件循环
event_base_dispatch(base);
//3.返回处理后free掉注册信息和实例
event_free(signal_event);
event_free(timeout_event);
event_base_free(base);
exit(0);
}
当I/O,信号,定时事件的队列都为空,则event_base_dispatch结束,程序退出。
将信号注册为EV_PERSIST永久性事件 ,所以程序不会自动结束(一直有事件需要处理)
若将定时事件注册为永久性事件,它每隔设定的秒都会调用回调函数
写一个TCP服务器端
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<event.h>
struct mess
{
struct event * ev;
//int cout;
};
int create_socket()
void recv_cb(int fd, short ev,void*arg)
{
struct mess * s = (struct mess*)arg;
if(ev & EV_READ)
{
char buff[128] = {0};
int n = recv(fd,buff,127,0);
if(n<=0)
{
event_free(s->ev);//关闭时要释放实例,需要获取地址,所以前面弄了一个结构体
free(s);
close(fd);
printf("client close\n");
return;
}
printf("buff=%s\n",buff);
send(fd,"ok",2,0);
}
}
void accept_cb(int fd, short ev,void*arg)
{
struct event_base*base = (struct event_base*)arg;
if(ev&EV_READ)//生成读事件了
{
struct sockaddr_in caddr;
int len = sizeof(caddr);
int c = accept(fd,(struct sockaddr*)&caddr,&len);
if(c<0)return;
printf("accept c=%d\n",c);
//用recv_cb处理读事件
struct event* c_ev = event_new(base,c,EV_READ|EV_PERSIST,recv_cb,base);
if(c_ev==NULL)
{
close(c);
return;
}
event_add(c_ev,NULL);
}
}
int main()
{
int sockfd = create_socket()
assert(sockfd!=-1);
struct event_base*base = event_init();
assert(base!=NULL);
//产生事件,用accept_cb处理
struct event*sock_ev = event_new(base,sockfd,EV_READ|EV_PERSIST,accept_cb,NULL);
assert(sock_ev!=NULL);
event_add(sock_ev, NULL);//事件,超时时间
event_base_dispatch(base);
event_free(sock_ev);
event_base_free(base);
exit(0);
}
//创建套接字
int create_socket()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
return -1;
struct sockaddr_in addr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(res==-1)return -1;
res=listen(sockfd,5);
if(res==-1)return -1;
return sockfd;
}