Libevent: 是开源社区的一款高性能的I/O框架库。 其学习和使用者众多,例如:高性能的分布式内存对象缓存软件memcached,Google浏览器Chromium的Linux版本。
Libevent使用特点:
1.跨平台支持: Libevent支持Linux,UINX和Windows。
2.统一事件源: Libevent对I/O事件、信号和定时事件提供统一的处理。
3.线程安全: Libevent使用Libevent_pthreads库来提供线程安全支持。
4.基于Reactor模式实现: 该模式主线程只负责监听文件描述符上是否有事件发生,有的话就立即将该事件通知工作线程,除此之外,主线程不做任何其他实质性工作。读写数据,接受新的连接,以及处理客户请求均在工作线程中完成。
大致使用流程:
1.应用程序进行event事件处理器的定义。
2.将event事件处理器注册到Libevent中。
3.Libevent进行循环监听事件,利用I/O复用方法检测就绪事件。
4.得到就绪事件,在就绪队列中由应用程序进程回调函数调用。
Libevent支持的事件类型:
下面我们利用定时事件与信号事件来看一看Libevent的简单实用(更多详情请参考:Linux高性能服务器编程)。
几个函数的作用:
1.event_init()函数用于创建event_base对象,一个event_base对象相当于一个Reactor实例。
2.evsignal_new()创建信号事件处理器,evtimer_new创建定时事件处理器。
3.event_add()函数将事件处理器添加到注册事件队列中,并将该事件处理器对应的事件添加到事件多路分发器中。
4.event_base_dispatch()函数执行事件循环。
5.事件循环结束后,event_free()释放系统资源。
#include
#include
#include
#include
#include
#include
#include
//信号事件
void signal_cb(int fd, short event, void *arg)
{
printf("SIGINT was call\n");
}
//定时事件
void time_cb(int fd, short event, void *arg)
{
printf("time out\n");
}
int main()
{
struct event_base *base = event_init(); //base为Libevent的一个实例,对base进行初始化
//创建信号事件
struct event *signal_event = evsignal_new(base, SIGINT, signal_cb, NULL);
event_add(signal_event, NULL); //将事件注册到Libevent中
//创建定时事件
struct timeval tm = {5, 0}; //定时时间
struct event *time_event = evtimer_new(base, time_cb, NULL);
event_add(time_event, &tm); //将事件注册到Libevent中
//执行事件循环,检测就绪事件
event_base_dispatch(base);
//事件循环结束,释放资源
event_free(signal_event);
event_free(time_event);
event_base_free(base);
exit(0);
}
测试结果(以下代码均为Linux下测试):使用Libevent时要链入libevent库:
1.定时事件被触发: 若5s内不做任何处理,定时事件便会被触发
2.信号事件被触发: 触发会输出SIGINT was call,SIGINT事件为程序终止(interrupt)信号, 在用户键入Ctrl+C时发出。
使用Libevent库实现TCP服务器时,将监听的socket与连接时的socket分别生成一个Libevent(指定其对应的回调函数),并将其添加到Libevent的另一个base中,执行事件循环检测其发生。
客户端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_CLIENT 100 //最多客户端
#define DATALENGTH 1024 //缓冲区大小
//定义一个全局事件处理器
struct event_base *base = NULL;
typedef struct ClientData
{
int fd;
struct event *listen_event;
}ClientData;
void InitClients(ClientData clients[])
{
int i = 0;
for (; i < MAX_CLIENT; ++i)
{
clients[i].fd = -1;
clients[i].listen_event = NULL;
}
}
void InsertToClients(ClientData clients[], int fd, struct event *listen_event)
{
int i = 0;
for (; i < MAX_CLIENT; ++i)
{
if (clients[i].fd == -1)
{
clients[i].fd = fd;
clients[i].listen_event = listen_event;
break;
}
}
}
struct event *DeleteOfClients(ClientData clients[], int fd)
{
int i = 0;
for (; i < MAX_CLIENT; ++i)
{
if (clients[i].fd == fd)
{
clients[i].fd = -1;
return clients[i].listen_event;
}
}
return NULL;
}
//创建套接字
int CreateSocket()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
return -1;
}
struct sockaddr_in ser_addr;
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(6000);
ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(listenfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
if (res == -1)
{
return -1;
}
res = listen(listenfd, 5);
if (res == -1)
{
return -1;
}
return listenfd;
}
//处理客户端数据
void client_fun(int fd, short event, void *arg)
{
ClientData *clients = (ClientData*)arg;
char buff[DATALENGTH] = {0};
int n = recv(fd, buff, DATALENGTH-1, 0);
if (n <= 0)
{
struct event *listen_event = DeleteOfClients(clients, fd);
event_free(listen_event);
printf("one client unlink!\n");
return;
}
printf("%d : %s\n", fd, buff);
send(fd, "OK", 2, 0);
}
//处理连接事件
void sockfd_fun(int fd, short event, void *arg)
{
ClientData *clients = (ClientData*)arg;
struct sockaddr_in cli_addr;
socklen_t len = sizeof(cli_addr);
int c = accept(fd, (struct sockaddr*)&cli_addr, &len);
if (c < 0)
{
return;
}
struct event *listen_event = event_new(base,c, EV_READ | EV_PERSIST, client_fun, arg);
InsertToClients(clients, c, listen_event);
event_add(listen_event, NULL);
printf("one client link!\n");
}
int main()
{
int sockfd = CreateSocket();
assert(sockfd != -1);
ClientData clients[MAX_CLIENT];
InitClients(clients);
base = event_init();
//针对与listenfd创建事件
struct event *listen_event = event_new(base, sockfd, EV_READ | EV_PERSIST, sockfd_fun, (void*)clients);
event_add(listen_event, NULL);
//执行事件循环,检测就绪事件
event_base_dispatch(base);
//事件循环结束,释放资源
event_free(listen_event);
event_base_free(base);
exit(0);
}
服务器代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
assert(sockfd != -1);
struct sockaddr_in ser_addr;
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(6000);
ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//connect完成与与服务器的连接,所以sockaddr应该给服务器的地址信息
int res = connect(sockfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
assert(res != -1);
while (1)
{
char buff[128] = {0};
printf("input:");
fgets(buff, 127, stdin);
if (strncmp(buff, "end", 3) == 0)
{
break;
}
int num = send(sockfd, buff, strlen(buff)-1, 0);
assert(num != -1);
if (num == 0) //未写入进去数据
{
printf("the length send data is 0\n");
break;
}
char recvbuff[128] = {0};
int n = recv(sockfd, recvbuff, 127, 0);
assert(n != -1);
if (n == 0)
{
printf("recv data error\n");
break;
}
printf("recv data is : %s\n", recvbuff);
}
close(sockfd);
exit(0);
}