Libevent
Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大;源代码相当精炼、易读;跨平台,支持 Windows、 Linux、 BSD 和 Mac Os;支持多种 I/O 多路复用技术, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定时器和信号等事件;注册事件优先级。
Libevent 已经被广泛的应用,作为底层的网络库;比如 memcached、 Vomit、 Nylon、 Netchat等等,同时libevent是一个事件通知库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue、IOCP等系统调用管理事件机制。著名分布式缓存软件memcached也是基于libevent,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。
我这里就先说说关于libevent的io方法
相关函数
1、event_init()
函数原型
struct event_base *event_init(void);
作用:创建libevent框架实例
所需头文件
#include <event.h>
返回值
成功:返回struct event_base*类型的框架
失败:NULL
2、event_new()
函数原型
struct event *event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)
所需头文件
#include <event.h>
返回值
成功:返回该fd所监听事件的结构体
失败:NULL
参数
base:即event_init()所创建好的框架
fd:描述符或信号
events:事件
cb :发现fd有某种事件时所调用的函数,由自己封装
arg:给自己所封装的函数传递的参数
注意:
void *cb(evutil_socket_t fd, short ev, void *arg)
在自己所封装的函数中
fd:描述符或信号
ev:所满足的事件
arg:传入的参数
当发生你所关注的事件的时候,便会调用你所封装的函数,封装的函数中前两个参数是由libevent的函数内部传入的,你只需要关注最后一个参数由event_new中最后一个参数传入即可
3、event_add()
函数原型
int event_add(struct event *ev, const struct timeval *tv)
所需头文件
#include <event>
参数
ev:为event_new的返回值
tv:超时时间
4、event_base_dispatch()
函数原型
int event_base_dispatch(struct event_base *event_base)
所需头文件
#include <event.h>
参数
event_base:所需要开始所关注事件的事件循环
5、event_free()
函数原型
void event_free(struct event *ev)
所需头文件
#include <event.h>
参数:
ev:需要关闭fd的事件结构体
6、event_base_free()
函数原型
void event_base_free(struct event_base *base)
所需头文件
#include <event.h>
参数:
base:需要被删除的框架,即event_init()的返回值
代码示例
例如:我们同过收到某种特定的信号来打印该信号所代表的数
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <event.h>
#include <signal.h>
void sig_cb(int fd,short ev,void *arg)
{
if(ev & EV_SIGNAL)
{
printf("\nsignal = %d\n",SIGINT);
}
}
void time_cb(int fd,short ev,void *arg)
{
if(ev & EV_TIMEOUT)
{
printf("time out\n");
}
}
int main()
{
struct event_base *base = event_init();
assert(base!=NULL);
struct event * sig_ev = event_new(base,SIGINT,EV_SIGNAL|EV_PERSIST,sig_cb,NULL);
assert(sig_ev!=NULL);
event_add(sig_ev,NULL);
struct event *time_ev = event_new(base,-1,EV_TIMEOUT|EV_PERSIST,time_cb,NULL);
assert(time_ev!=NULL);
struct timeval time = {3,0};
event_add(time_ev,&time);
event_base_dispatch(base);
event_free(sig_ev);
event_free(time_ev);
event_base_free(base);
return 0;
}
运行结果为如下图
结果描述:当我在linux的终端上没有按下SIGINT信号(ctrl + c)时,程序不断打印time out 超时,当我发送该信号,则打印该信号所代表的数字
实现io多路
服务器
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <event.h>
#include <stdlib.h>
#define MAX 10
int Creat_Socket();
struct event *arr[1000];
void con_cb(int fd,short ev,void *arg)
{
if(ev & EV_READ)
{
char buf[128] = {0};
int size = recv(fd,buf,127,0);
if(size<=0)
{
event_free(arr[fd]);
arr[fd] = NULL;
close(fd);
printf("one client quit\n");
}
else
{
printf("client send message:\n%s",buf);
send(fd,"success",7,0);
}
}
if(ev & EV_TIMEOUT)
{
printf("time out\n");
}
}
void accept_cb(int fd,short ev,void *arg)
{
if(ev & EV_READ)
{
struct event_base *base = (struct event_base*)arg;
struct sockaddr_in cliaddr;
memset(&cliaddr,0,sizeof(cliaddr));
socklen_t len = sizeof(cliaddr);
int c = accept(fd,(struct sockaddr*)&cliaddr,&len);
assert(c!=-1);
printf("one client connect......fd = %d\n",c);
struct event *con_ev = event_new(base,c,EV_READ|EV_PERSIST|EV_TIMEOUT,con_cb,NULL);
assert(con_ev!=NULL);
struct timeval time = {5,0};
event_add(con_ev,&time);
arr[c] = con_ev;
}
}
int main()
{
struct event_base *base = event_init();
assert(base!=NULL);
int sockfd = Creat_Socket();
assert(sockfd!=-1);
struct event *sock_ev = event_new(base,sockfd,EV_READ|EV_PERSIST|EV_TIMEOUT,accept_cb,(void*)base);
assert(sock_ev!=NULL);
event_add(sock_ev,NULL);
arr[sockfd] = sock_ev;
event_base_dispatch(base);
event_free(sock_ev);
event_base_free(base);
return 0;
}
int Creat_Socket()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
assert(sockfd!=-1);
struct sockaddr_in seraddr;
memset(&seraddr,0,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(5000);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
int bind_ret = bind(sockfd,(struct sockaddr*)&seraddr,sizeof(seraddr));
assert(bind_ret!=-1);
int listen_ret = listen(sockfd,MAX);
assert(listen_ret!=-1);
return sockfd;
}
客户端
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
printf("creat socket fail\n");
return -1;
}
else
{
printf("creat socket success , sockfd = %d\n",sockfd);
}
struct sockaddr_in cliaddr;
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(5000);
cliaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(cliaddr.sin_zero,0,8);
int confd = connect(sockfd,(struct sockaddr *)&cliaddr,sizeof(struct sockaddr));
if(confd < 0)
{
printf("connect fail\n");
close(sockfd);
return -2;
}
else
{
printf("----------connect success-----------\nconfd = %d\n",confd);
}
char buf[128];
while(1)
{
memset(buf,0,128);
printf("send message to server\n");
fgets(buf,128,stdin);
send(sockfd,buf,strlen(buf),0);
if(strncmp(buf,"end",3) == 0)
{
break;
}
}
close(sockfd);
return 0;
}