level 5
西安恩仪联教育
楼主
Libevent库编程步骤:
一、libevent介绍:
Libevent是开源社区的一款高性能的I/0框架库,使用Libevent的著名案例有:高性能的分布式内存对象缓存软件 memcached9,Googlo浏览器Chromium的Linux版本。
作为一个I/0框架库,Libevent具有如下特点:
跨平台支持 Libevent支持Linux、Unix和Windows
统一事件源 Libevent对I/O事件、信号和定时事件提供统一的处理,线程安全 Libevent使用libevent pthread库来提供线程安全支持
基于Reactor模式的实现
二、libevent使用模型:

libevent主框架提供注册方法,通过事件循环去检测事件就绪并通知libevent框架去调用回调函数.
三、总体编程流程
1、 创建事件:调用 event_new() 分配并初始化事件对象,指定监控条件和回调函数。
2、添加事件:使用 event_add() 将事件添加到 event_base 的事件循环中。若未调用 event_add(),事件不会被激活。
3、启动事件循环:调用 event_base_dispatch() 启动事件循环,等待事件触发并执行回调。
4、清理资源:事件处理完毕后,需通过 event_del() 删除事件,并调用 event_free() 释放事件内存;最后用 event_base_free() 释放 event_base。
四、各个步骤主要函数说明
1、创建event_base对象(定义变量并初始化)
struct event_base *event_init();
struct event_base *event_base_new();
例如:
struct event_base * base = event_init();
struct event_base *base = event_base_new();
实际上这一步相当于初始化一个 Reactor实例。
注意:event_init 和 event_base_new 都是用于初始化 libevent 库的事件基础结构,但它们在功能和使用上存在关键区别。
线程安全性:event_base_new 是线程安全的,而 event_init 不是线程安全的。这意味着在多线程环境中,应优先使用 event_base_new 以避免潜在的竞争条件。
官方状态:event_init 在其官方文档中已被标记为过时(deprecated),并建议用 event_base_new 替代。event_base_new 是当前推荐的初始化方式。
功能封装:event_init 实际上是对 event_base_new 的简单封装;它内部调用 event_base_new 来创建 event_base 对象,并额外设置一个全局变量 current_base。因此,event_base_new 提供了更底层和直接的控制。
在实际使用中,由于 event_init 已过时,新项目应直接使用 event_base_new
2、初始化事件 event,设置回调函数和关注的事件
event_set 的函数原型是:
void event_set(struct event *ev, int fd, short event,
void (*cb)(int,short, void *),
void *arg)
ev:执行要初始化的 event 对象;
fd:该 event 绑定的“句柄”,对于信号事件,它就是关注的信号;
event:在该 fd 上关注的事件类型,它可以是 EV_READ, EV_WRITE, EV_SIGNAL;
cb:这是一个函数指针,当 fd 上的事件 event 发生时,调用该函数执行处理,它有三个参数,
调用时由 event_base 负责传入,按顺序,实际上就是 event_set 时的 fd, event 和 arg;
arg:传递给 cb 函数指针的参数;
这一步相当于初始化一个 event handler,在 libevent 中事件类型保存在 event 结构体中。
注意:libevent 并不会管理 event 事件集合,这需要应用程序自行管理;
3、设置 event 从属的 event_base
int event_base_set(struct event_base *base, struct event *ev);
例如:event_base_set(base, &ev);
这一步相当于指明 event 要注册到哪个event_base 实例上;
4、创建一个新事件对象,event_new()
这个事件对象用于描述需要被监控的特定条件(如文件描述符的读写就绪、信号到达或超时),以及当这些条件满足时应执行的回调函数
struct event *event_new(struct event_base *base, evutil_socket_t fd, short what, event_callback_fn cb, void *arg);
base:指向事件循环基础结构 event_base 的指针,新创建的事件将归属于该事件库进行管理。
fd:文件描述符或信号编号。对于 I/O 事件(如读、写),它是一个有效的文件描述符(如 socket);对于信号事件,它是对应的信号编号(如 SIGINT);若仅创建超时事件,则可设为 -1。
what:事件类型标志位,指定需要监控的条件。常见选项包括:
EV_READ:监控文件描述符可读。
EV_WRITE:监控文件描述符可写。
EV_SIGNAL:监控指定信号的到达。
EV_TIMEOUT:创建超时事件(无需关联文件描述符)。
EV_PERSIST:标记事件为持久化,表示事件触发后自动重新添加到事件循环中,实现周期性触发
这些标志可通过按位或(|)组合,但互斥类型(如读写与信号)不能同时设置。
cb:事件触发时调用的回调函数指针。函数签名通常为 void (*cb)(evutil_socket_t fd, short what, void *arg),其中 fd 是触发事件的文件描述符,what 是触发的事件类型,arg 是用户自定义参数。
arg:传递给回调函数的用户数据指针,可在回调中使用。
注意:
event_new()相当于malloc+event_set()+event_base_set()
4、添加事件event_add()
int event_add(struct event *ev, const struct timeval *tv);
ev: 指向要添加的事件对象(struct event)。该事件必须通过 event_new() 或 event_assign() 进行初始化
tv: 指定超时时间(struct timeval 类型)。如果为 NULL,则事件不会超时,仅等待 I/O 或信号触发;否则,事件在指定时间后触发超时回调
event_add(&ev, timeout);
基本信息都已设置完成,调用 event_add()函数即可完成,其中 timeout 是定时值;这一步相当于函数注册事件。
6、程序进入无限循环,等待就绪事件并执行事件处理
event_base_dispatch();
event_base_loop();
event_base_dispatch() 和 event_base_loop() 都是 libevent 库中用于启动事件循环的核心函数,但它们在灵活性和默认行为上存在关键区别。
基本关系:event_base_dispatch() 实际上是 event_base_loop() 的一个特例。具体来说,event_base_dispatch() 等价于调用 event_base_loop(base, 0),即不设置任何标志位。
灵活性:event_base_loop() 提供了 flags 参数,允许通过组合标志(如 EVLOOP_ONCE、EVLOOP_NONBLOCK 等)来改变事件循环的行为,例如让循环仅处理一次事件或非阻塞地运行。而 event_base_dispatch() 没有 flags 参数,其行为是固定的:它会一直运行事件循环,直到没有更多事件注册,或者显式调用了 event_base_loopbreak() 或 event_base_loopexit()。
使用场景:当需要更精细的控制(如一次性处理事件或非阻塞模式)时,应使用 event_base_loop();而如果只需要标准的事件循环行为,event_base_dispatch() 作为简化接口更为便捷。
注意:
在你使用任何有意思的Libevent函数之前,你需要分配一个或多个event_base结构.每一个event_base结构含有一组events,并且可以告知你哪一些events是就绪的.就是一个event_base可以有一个或一组event。
7、清理资源
event_free()和event_base_free()
void event_free(struct event *ev);
void event_base_free(struct event_base *base)
释放事件(包括资源释放)
五、编程案例:基于libevent实现一个简易的tcp服务器,客户端给服务器发送字符串,服务器收到后显示。
服务器端的实现:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <event.h>
void set_nonblocking(int sock)
{
int opts;
opts = fcntl(sock, F_GETFL);
if (opts < 0)
{
perror("fcntl");
return;
}
opts = opts | O_NONBLOCK;
if (fcntl(sock, F_SETFL, opts) < 0)
{
perror("fcntl");
}
}
struct event *connev = NULL;
void readcb(evutil_socket_t fd,short events,void *arg)
{
int n = 0;
char buf[1024];
memset(buf,0,sizeof(buf));
n = read(fd,buf,sizeof(buf) - 1);
if(n <= 0)
{
close(fd);
event_del(connev);
}
printf("%s\n",buf);
}
void conncb(evutil_socket_t fd,short events,void *arg)
{
struct event_base *base = (struct event_base *)arg;
int cfd = accept(fd,NULL,NULL);
if(cfd > 0)
{
connev = event_new(base,cfd,EV_READ|EV_PERSIST,readcb,NULL);
if(connev == NULL)
{
event_base_loopexit(base,NULL);
}
event_add(connev,NULL);
}
}
int main()
{
struct sockaddr_in addr;
int listenfd = 0;
int sockfd = 0;
int ret = 0;
char buf[100] = {0};
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 6060;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
listenfd = socket(AF_INET,SOCK_STREAM,0);
if(listenfd < 0)
{
perror("socket");
exit(-1);
}
ret = bind(listenfd,(struct sockaddr *)&addr,sizeof(struct sockaddr));
if(ret < 0)
{
perror("bind");
exit(-1);
}
ret = listen(listenfd,5);
if(ret < 0)
{
perror("listen");
exit(-1);
}
struct event_base *base = event_base_new();
if(base == NULL)
{
printf("event_base_new error\n");
return -1;
}
struct event *ev = event_new(base,listenfd,EV_READ|EV_PERSIST,conncb,base);
if(ev == NULL)
{
printf("event_new error\n");
return -1;
}
event_add(ev,NULL);
event_base_dispatch(base);
event_base_free(base);
event_free(ev);
close(listenfd);
return 0;
}
客户端实现:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
struct sockaddr_in addr;
int sockfd = 0;
int ret = 0;
char buf[100] = {0};
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 6060;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
ret = connect(sockfd,(struct sockaddr *)&addr,sizeof(addr));
if(ret < 0)
{
perror("connect");
exit(-1);
}
while(1)
{
printf("please input information:\n");
scanf("%s",buf);
ret = send(sockfd,buf,strlen(buf),0);
if(ret < 0)
{
perror("send");
exit(-1);
}
memset(buf,0,sizeof(buf));
}
close(sockfd);
return 0;
}
2025年12月23日 07点12分
1
一、libevent介绍:
Libevent是开源社区的一款高性能的I/0框架库,使用Libevent的著名案例有:高性能的分布式内存对象缓存软件 memcached9,Googlo浏览器Chromium的Linux版本。
作为一个I/0框架库,Libevent具有如下特点:
跨平台支持 Libevent支持Linux、Unix和Windows
统一事件源 Libevent对I/O事件、信号和定时事件提供统一的处理,线程安全 Libevent使用libevent pthread库来提供线程安全支持
基于Reactor模式的实现
二、libevent使用模型:

libevent主框架提供注册方法,通过事件循环去检测事件就绪并通知libevent框架去调用回调函数.三、总体编程流程
1、 创建事件:调用 event_new() 分配并初始化事件对象,指定监控条件和回调函数。
2、添加事件:使用 event_add() 将事件添加到 event_base 的事件循环中。若未调用 event_add(),事件不会被激活。
3、启动事件循环:调用 event_base_dispatch() 启动事件循环,等待事件触发并执行回调。
4、清理资源:事件处理完毕后,需通过 event_del() 删除事件,并调用 event_free() 释放事件内存;最后用 event_base_free() 释放 event_base。
四、各个步骤主要函数说明
1、创建event_base对象(定义变量并初始化)
struct event_base *event_init();
struct event_base *event_base_new();
例如:
struct event_base * base = event_init();
struct event_base *base = event_base_new();
实际上这一步相当于初始化一个 Reactor实例。
注意:event_init 和 event_base_new 都是用于初始化 libevent 库的事件基础结构,但它们在功能和使用上存在关键区别。
线程安全性:event_base_new 是线程安全的,而 event_init 不是线程安全的。这意味着在多线程环境中,应优先使用 event_base_new 以避免潜在的竞争条件。
官方状态:event_init 在其官方文档中已被标记为过时(deprecated),并建议用 event_base_new 替代。event_base_new 是当前推荐的初始化方式。
功能封装:event_init 实际上是对 event_base_new 的简单封装;它内部调用 event_base_new 来创建 event_base 对象,并额外设置一个全局变量 current_base。因此,event_base_new 提供了更底层和直接的控制。
在实际使用中,由于 event_init 已过时,新项目应直接使用 event_base_new
2、初始化事件 event,设置回调函数和关注的事件
event_set 的函数原型是:
void event_set(struct event *ev, int fd, short event,
void (*cb)(int,short, void *),
void *arg)
ev:执行要初始化的 event 对象;
fd:该 event 绑定的“句柄”,对于信号事件,它就是关注的信号;
event:在该 fd 上关注的事件类型,它可以是 EV_READ, EV_WRITE, EV_SIGNAL;
cb:这是一个函数指针,当 fd 上的事件 event 发生时,调用该函数执行处理,它有三个参数,
调用时由 event_base 负责传入,按顺序,实际上就是 event_set 时的 fd, event 和 arg;
arg:传递给 cb 函数指针的参数;
这一步相当于初始化一个 event handler,在 libevent 中事件类型保存在 event 结构体中。
注意:libevent 并不会管理 event 事件集合,这需要应用程序自行管理;
3、设置 event 从属的 event_base
int event_base_set(struct event_base *base, struct event *ev);
例如:event_base_set(base, &ev);
这一步相当于指明 event 要注册到哪个event_base 实例上;
4、创建一个新事件对象,event_new()
这个事件对象用于描述需要被监控的特定条件(如文件描述符的读写就绪、信号到达或超时),以及当这些条件满足时应执行的回调函数
struct event *event_new(struct event_base *base, evutil_socket_t fd, short what, event_callback_fn cb, void *arg);
base:指向事件循环基础结构 event_base 的指针,新创建的事件将归属于该事件库进行管理。
fd:文件描述符或信号编号。对于 I/O 事件(如读、写),它是一个有效的文件描述符(如 socket);对于信号事件,它是对应的信号编号(如 SIGINT);若仅创建超时事件,则可设为 -1。
what:事件类型标志位,指定需要监控的条件。常见选项包括:
EV_READ:监控文件描述符可读。
EV_WRITE:监控文件描述符可写。
EV_SIGNAL:监控指定信号的到达。
EV_TIMEOUT:创建超时事件(无需关联文件描述符)。
EV_PERSIST:标记事件为持久化,表示事件触发后自动重新添加到事件循环中,实现周期性触发
这些标志可通过按位或(|)组合,但互斥类型(如读写与信号)不能同时设置。
cb:事件触发时调用的回调函数指针。函数签名通常为 void (*cb)(evutil_socket_t fd, short what, void *arg),其中 fd 是触发事件的文件描述符,what 是触发的事件类型,arg 是用户自定义参数。
arg:传递给回调函数的用户数据指针,可在回调中使用。
注意:
event_new()相当于malloc+event_set()+event_base_set()
4、添加事件event_add()
int event_add(struct event *ev, const struct timeval *tv);
ev: 指向要添加的事件对象(struct event)。该事件必须通过 event_new() 或 event_assign() 进行初始化
tv: 指定超时时间(struct timeval 类型)。如果为 NULL,则事件不会超时,仅等待 I/O 或信号触发;否则,事件在指定时间后触发超时回调
event_add(&ev, timeout);
基本信息都已设置完成,调用 event_add()函数即可完成,其中 timeout 是定时值;这一步相当于函数注册事件。
6、程序进入无限循环,等待就绪事件并执行事件处理
event_base_dispatch();
event_base_loop();
event_base_dispatch() 和 event_base_loop() 都是 libevent 库中用于启动事件循环的核心函数,但它们在灵活性和默认行为上存在关键区别。
基本关系:event_base_dispatch() 实际上是 event_base_loop() 的一个特例。具体来说,event_base_dispatch() 等价于调用 event_base_loop(base, 0),即不设置任何标志位。
灵活性:event_base_loop() 提供了 flags 参数,允许通过组合标志(如 EVLOOP_ONCE、EVLOOP_NONBLOCK 等)来改变事件循环的行为,例如让循环仅处理一次事件或非阻塞地运行。而 event_base_dispatch() 没有 flags 参数,其行为是固定的:它会一直运行事件循环,直到没有更多事件注册,或者显式调用了 event_base_loopbreak() 或 event_base_loopexit()。
使用场景:当需要更精细的控制(如一次性处理事件或非阻塞模式)时,应使用 event_base_loop();而如果只需要标准的事件循环行为,event_base_dispatch() 作为简化接口更为便捷。
注意:
在你使用任何有意思的Libevent函数之前,你需要分配一个或多个event_base结构.每一个event_base结构含有一组events,并且可以告知你哪一些events是就绪的.就是一个event_base可以有一个或一组event。
7、清理资源
event_free()和event_base_free()
void event_free(struct event *ev);
void event_base_free(struct event_base *base)
释放事件(包括资源释放)
五、编程案例:基于libevent实现一个简易的tcp服务器,客户端给服务器发送字符串,服务器收到后显示。
服务器端的实现:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <event.h>
void set_nonblocking(int sock)
{
int opts;
opts = fcntl(sock, F_GETFL);
if (opts < 0)
{
perror("fcntl");
return;
}
opts = opts | O_NONBLOCK;
if (fcntl(sock, F_SETFL, opts) < 0)
{
perror("fcntl");
}
}
struct event *connev = NULL;
void readcb(evutil_socket_t fd,short events,void *arg)
{
int n = 0;
char buf[1024];
memset(buf,0,sizeof(buf));
n = read(fd,buf,sizeof(buf) - 1);
if(n <= 0)
{
close(fd);
event_del(connev);
}
printf("%s\n",buf);
}
void conncb(evutil_socket_t fd,short events,void *arg)
{
struct event_base *base = (struct event_base *)arg;
int cfd = accept(fd,NULL,NULL);
if(cfd > 0)
{
connev = event_new(base,cfd,EV_READ|EV_PERSIST,readcb,NULL);
if(connev == NULL)
{
event_base_loopexit(base,NULL);
}
event_add(connev,NULL);
}
}
int main()
{
struct sockaddr_in addr;
int listenfd = 0;
int sockfd = 0;
int ret = 0;
char buf[100] = {0};
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 6060;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
listenfd = socket(AF_INET,SOCK_STREAM,0);
if(listenfd < 0)
{
perror("socket");
exit(-1);
}
ret = bind(listenfd,(struct sockaddr *)&addr,sizeof(struct sockaddr));
if(ret < 0)
{
perror("bind");
exit(-1);
}
ret = listen(listenfd,5);
if(ret < 0)
{
perror("listen");
exit(-1);
}
struct event_base *base = event_base_new();
if(base == NULL)
{
printf("event_base_new error\n");
return -1;
}
struct event *ev = event_new(base,listenfd,EV_READ|EV_PERSIST,conncb,base);
if(ev == NULL)
{
printf("event_new error\n");
return -1;
}
event_add(ev,NULL);
event_base_dispatch(base);
event_base_free(base);
event_free(ev);
close(listenfd);
return 0;
}
客户端实现:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
struct sockaddr_in addr;
int sockfd = 0;
int ret = 0;
char buf[100] = {0};
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 6060;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
ret = connect(sockfd,(struct sockaddr *)&addr,sizeof(addr));
if(ret < 0)
{
perror("connect");
exit(-1);
}
while(1)
{
printf("please input information:\n");
scanf("%s",buf);
ret = send(sockfd,buf,strlen(buf),0);
if(ret < 0)
{
perror("send");
exit(-1);
}
memset(buf,0,sizeof(buf));
}
close(sockfd);
return 0;
}