Libevent库的介绍以及使用示例
1、Libevent概述
Libevent是一个I/O框架库,具有如下特点:
①跨平台支持。
②统一事件源。Libevent对I/O事件,信号和定时事件提供统一的处理
③线程安全。Libevent使用libevent_pthreads库来提供线程安全支持
④基于Reactor模式实现(即主线程负责事件的产生,其余线程负责对事件的处理)
2、Libevent使用模型
首先注册事件,需要有描述符fd,事件以及回调函数fun。然后交给libevent,libevent通过底层封装的I/O复用方法来进行事件循环的检测,最后调用回调函数进行处理
所以对于用户只需要进行:
①调用libevent实例
②注册事件
③启动事件循环
对于调用回调函数,这个由与用户之前在注册事件的时候已经写好了回调函数,所以最后通过libevent进行处理
3、Libevent支持的事件类型
#define EV_TIMEOUT //定时事件
#define EV_READ //可读事件
#define EV_WRITE //可写事件
#define EV_SIGNAL //信号事件
#define EV_PERSIST //永久事件
/*边沿触发事件,需要I/O复用系统调用支持,比如epoll*/
#define EV_ET
4、示例一:简单使用Libevent注册信号事件以及定时事件
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <unistd.h>
#include <event.h>
#include <signal.h>
void signal_cb(int fd,short event,void* arg)
{
if(event&EV_SIGNAL)
{
printf("sig=%d\n",fd);
}
}
void timeout_cb(int fd,short event,void* arg)
{
if(event&EV_TIMEOUT)
{
printf("time out\n");
}
}
int main()
{
//调用libevent示例
struct event_base* base=event_init();
//注册信号事件
struct event* signal_event=evsignal_new(base,SIGINT,signal_cb,NULL);
event_add(signal_event,NULL);
//注册超时事件
struct timeval tv = {2,0};
struct event* timeout_event=evtimer_new(base,timeout_cb,NULL);
event_add(timeout_event,&tv);
//启动事件循环
event_base_dispatch(base);
//free
event_free(signal_event);
event_free(timeout_event);
event_base_free(base);
}
由于上述代码中并没有将注册的事件变为永久事件,因此一次之后就结束了
所以程序运行结果如下:
5、 示例二:Libevent实现TCP服务器
服务器端:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <event.h>
//创建监听套接字
int socket_init()
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
return -1;
}
struct sockaddr_in saddr;
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;
}
void recv_cb(int fd,short event,void* arg)
{
if(event&EV_READ)
{
char buff[1024]={0};
int n=recv(fd,buff,1024,0);
if(n<=0)
{
struct event** p_cev=(struct event**)arg;
event_free(*p_cev);
free(p_cev);
close(fd);
printf("client close\n");
return ;
}
printf("recv:%s\n",buff);
send(fd,"ok",2,0);
}
}
void accept_cb(int fd,short event,void* arg)
{
struct event_base* base=(struct event_base*)arg;
if(event&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);
struct event** p_cev=(struct event**)malloc(sizeof(struct event*));
if(p_cev==NULL)
{
return ;
}
*p_cev=event_new(base,c,EV_READ|EV_PERSIST,recv_cb,p_cev);
if(*p_cev==NULL)
{
close(c);
return ;
}
event_add(*p_cev,NULL);
}
}
int main()
{
struct event_base* base=event_init();
int sockfd=socket_init();
assert(sockfd!=-1);
struct event* sock_ev=event_new(base,sockfd,EV_READ|EV_PERSIST,accept_cb,base);
event_add(sock_ev,NULL);
event_base_dispatch(base);
event_free(sock_ev);
event_base_free(base);
return 0;
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main()
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
assert(sockfd!=-1);
struct sockaddr_in saddr;
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=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res!=-1);
while(1)
{
printf("please input:\n");
char buff[1024]={0};
fgets(buff,1024,stdin);
if(strncmp(buff,"end",3)==0)
{
break;
}
int n=send(sockfd,buff,strlen(buff),0);
if(n<=0)
{
printf("send error\n");
break;
}
memset(buff,0,1024);
n=recv(sockfd,buff,1024,0);
if(n<=0)
{
printf("recv error\n");
}
printf("buff=%s\n",buff);
}
close(sockfd);
exit(0);
}
程序运行结果: