1.libevent介绍
libevent是一个轻量级的基于事件驱动的高性能的开源网络库,支持多种系统,对不同系统的函数进行再次封装统一接口,编译的时候,选择自己的系统就行了
因为libevent的出色的轻量级、高性能的表现,很多其他的开源库基于此库,开发出了适应更多场景的开源库
例如memcached,在libevent上面增加了多线程的支持,主要利用了主线程+多个工作线程,实现线程池管理线程
2.libevent实现原理
libevent主要是用来处理大量连接的,处理多个连接的时候,一般都会用到IO多路复用+多线程/多进程
- IO多路复用:监测多个连接,当有某个就绪,就进行相应的处理,整个处理过程是一个单线程、同步的方式,这样虽然减少了线程切换,节约了系统资源,但是很有可能浪费时间在等待过程中,这个等待时间有可能是在等某个连接数据到来(这个等待无关紧要),也有可能在处理的过程中发生了阻塞(编程上尽量使用非阻塞)
- 多线程/多进程:每当有一个连接请求,就建立一个线程去处理,就算某个处理线程出现了阻塞,其他线程也会利用cpu资源,多线程是有弊端的,每个线程都需要占用栈空间,当连接过多(DDoS攻击),栈空间消耗很大,线程创建是需要消耗时间的,针对这些问题,使用线程池和keepalive机制,杜绝一些站着茅坑不拉屎的连接
libevent使用的是多路IO复用(select、poll、epoll),利用事件,事件的回调函数
lbevent的事件支持三种,分别是网络IO、定时器和信号。定时器的数据结构使用最小堆(Min Heap),以提高效率。网络IO和信号的数据结构采用了双向链表(TAILQ)。
3.libevent的安装
1.首先去官网或者git上获取源码安装包
2.进入libevent目录,执行./autogen.sh,这个过程可能需要一些第3方工具,一个什么tool,生成.lo文件的工具
3. ./configure && make; make install
4.libevent的使用
1.event_init:创建一个事件队列
2.event_set:事件初始化,创建事件、事件回调函数,传入的event指针参数存在整个事件生命周期,因为是简单的赋值,并不是内存拷贝
3.event_base_set:把事件和事件队列关联起来
4.event_add:添加事件到事件队列,可以设置超时时间,有添加就有删除event_del
5.event_base_dispatch:循环等待事件就绪
上面的这些函数是把多路IO复用函数再次封装得到的,在上层增加了事件管理
5.libevent使用实例
下面这段代码有一点问题,有一段malloc的内存在close(sock_fd)没有free这段内存
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 6666
#define LISTEN_MAX 5
#define BUF_SIZE 512
struct event_base *base;
static int init_socket_server(const char* ip, int port)
{
int on = 1;
int ret = -1;
int sock_fd = -1;
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sock_fd)
{
printf("socket failed\n");
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr(ip);
ret = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (-1 == ret)
{
printf("setsockopt failed\n");
exit(1);
}
ret = bind(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (-1 == ret)
{
printf("bind failed\n");
exit(1);
}
ret = listen(sock_fd, LISTEN_MAX);
if (-1 == ret)
{
printf("listen failed\n");
exit(1);
}
return sock_fd;
}
static void read_write_process(int sock_fd, short event, void* arg)
{
printf("write\n");
int nbyte = 0;
char buffer[BUF_SIZE];
memset(buffer, 0, sizeof(buffer));
nbyte = recv(sock_fd, buffer, BUF_SIZE, 0);
if (0 == nbyte)
{
close(sock_fd);
return;
}
nbyte = send(sock_fd, buffer, strlen(buffer)+1, 0);
printf("nbyte = %d\n", nbyte);
}
static void accept_process(int sock_fd, short event, void* arg)
{
printf("accept\n");
int new_fd = -1;
socklen_t sin_size;
struct event *event_list;
struct sockaddr_in client_addr;
event_list = (struct event*)malloc(sizeof(struct event));
sin_size = sizeof(struct sockaddr_in);
memset(&client_addr, 0, sizeof(client_addr));
new_fd = accept(sock_fd, (struct sockaddr*)&client_addr, &sin_size);
if (-1 == new_fd)
{
printf("accept failed\n");
exit(1);
}
event_set(event_list, new_fd, EV_READ|EV_PERSIST, read_write_process, NULL);
event_base_set(base, event_list);
event_add(event_list, NULL);
}
int main(int argc, char** argv)
{
int sock_fd = -1;
struct event event_list;
sock_fd = init_socket_server(SERVER_IP, SERVER_PORT);
base = event_init();//epoll_create
event_set(&event_list, sock_fd, EV_READ|EV_PERSIST, accept_process, NULL);
//设置好事件以及事件的回调函数, EV_PERSIST持续性事件,激活之后不需要重新event_add
event_base_set(base, &event_list);//base和event_list关联起来
event_add(&event_list, NULL);//epoll_ctl
event_base_dispatch(base);//epoll_wait
return 0;
}