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-2.1.11-stable.tar.gz
Ubuntu安装:apt-get install libevent-dev
原型:struct event_base *event_init(void)
struct event_base *event_init(void)
{
struct event_base *base = event_base_new_with_config(NULL);
if (base == NULL) {
event_errx(1, "%s: Unable to construct event_base", __func__);
return NULL;
}
current_base = base;
return (base);
}
初始化事件集合,其实就是调用了event_base_new_with_config( )
函数,创建event_base
对象,并且赋值给了全局变量struct evdns_base *current_base
。
原型:void event_set(struct event *ev, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg);
参数:
1)事件
2)关联的文件描述符
3)事件类型
4)回调函数
5)回调函数的参数
void event_set(struct event *ev, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
{
int r;
r = event_assign(ev, current_base, fd, events, callback, arg);
EVUTIL_ASSERT(r == 0);
}
初始化event事件(其实就是给结构体ev的成员赋值)
fd
表示事件对应的文件描述符,events
表示事件的类型,callback
是回调函数(即当fd满足条件时调用该函数),arg
表示给回调函数传递的参数。原型:int event_add(struct event *ev, const struct timeval *tv);
int event_add(struct event *ev, const struct timeval *tv)
{
int res;
if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {
event_warnx("%s: event has no event_base set.", __func__);
return -1;
}
EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);
res = event_add_nolock_(ev, tv, 0);
EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);
return (res);
}
这个函数会让程序陷入死循环, 如果集合中没有事件可以监听,则返回
#include
#include
#include
#include
#include
#include
#include
/*
当监听的事件满足条件的时候,会触发回调函数,通过回调函数读取数据
*/
void fifo_read(evutil_socket_t fd, short events, void *arg) {
char buf[32] = {0};
int ret = read(fd, buf, sizeof(buf));
if (-1 == ret) {
perror("read");
exit(1);
}
printf("从管道读取: %s\n", buf);
}
int main() {
int ret = mkfifo("fifo.tmp", 00700);
if (-1 == ret) {
perror("mkfifo");
exit(1);
}
int fd = open("fifo.tmp", O_RDONLY);
if (-1 == fd) {
perror("open");
exit(1);
}
// 创建事件
struct event ev;
// 初始化事件集合
event_init();
// 初始化事件(把fd和事件ev绑定)
// 参数:事件、关联的文件描述符、事件类型、回调函数、回调函数参数
event_set(&ev, fd, EV_READ | EV_PERSIST, fifo_read, NULL);
//把事件添加到集合
event_add(&ev, NULL);
//开始监吿
event_dispatch(); //死循玿 如果集合中没有事件可以监听,则返囿
exit(0);
}
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd = open("fifo.tmp", O_WRONLY);
if (-1 == fd)
{
perror("open");
exit(2);
}
char buf[128] = {0};
while (1)
{
scanf("%s", buf);
if (write(fd, buf, strlen(buf)) == -1)
{
perror("write");
break;
}
if (!strcmp(buf, "bye"))
break;
memset(buf, 0, 128);
}
close(fd);
return 0;
}
原型:struct event_base *event_base_new(void);
struct event_base *event_base_new(void)
{
struct event_base *base = NULL;
struct event_config *cfg = event_config_new();
if (cfg) {
base = event_base_new_with_config(cfg);
event_config_free(cfg);
}
return base;
}
创建event_base对象
注意:采用event_base_new( )创建出来的事件集合,最后要用 event_base_free(base)
释放掉,因为event_base_new( )是在堆空间上进行创建的。
用于释放event_base_new( )
函数创建的集合
原型:int event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
1)事件
2)事件集合
3)关联的文件描述符
4)事件类型
5)回调函数
6)回调函数的参数
将指定事件集合中的事件与某一文件描述符进行关联
可监听事件集合当中的事件,和event_dispatch( )的作用相同
原型:event_del(struct event *ev);
#include
#include
#include
#include
int signal_count = 0;
void signal_handler(evutil_socket_t fd, short events, void *arg) {
struct event *ev = (struct event *)arg;
printf("收到信号 %d\n", fd);
signal_count++;
if (signal_count >= 5) {
// 把事件从集合中删除
event_del(ev);
}
}
int main() {
// 创建事件集合
struct event_base *base = event_base_new();
// 创建事件
struct event ev;
// 把事件和信号进行绑定
event_assign(&ev, base, SIGINT, EV_SIGNAL | EV_PERSIST, signal_handler, &ev);
// 把事件添加到集合中
event_add(&ev, NULL);
// 监听集合
event_base_dispatch(base);
// 释放集合
event_base_free(base);
exit(0);
}
运行结果(收到一次Ctrl+C就打印信息,收到五次之后退出):
常见步骤:
struct evconnlistener *evconnlistener_new_bind(struct event_base *base,
evconnlistener_cb cb,
void *ptr,
unsigned flags,
int backlog,
const struct sockaddr *sa,
int socklen);
void evconnlistener_free(struct evconnlistener *listener);
参数 listener 是指向要释放的 TCP 监听器的指针
使用 evconnlistener_free() 函数释放监听器时,libevent 库将自动关闭监听器的文件描述符,并释放监听器所占用的内存。在释放监听器之后,应该将指向监听器的指针设置为 NULL,以避免出现悬空指针的问题。
需要注意的是,如果在释放监听器之前仍然有活动的连接,则这些连接将被关闭,并且可能会因为连接未正常关闭而导致数据丢失。因此,在释放监听器之前,应该确保已经停止了所有连接的事件循环,并且数据已经被正确地处理和发送。
struct bufferevent *bufferevent_socket_new(
struct event_base *base,
evutil_socket_t fd,
enum bufferevent_options options
);
bufferevent_setcb() 函数是 libevent 库中用于设置套接字缓冲区事件处理器回调函数的函数。
void bufferevent_setcb(
struct bufferevent *bev,
bufferevent_data_cb readcb,
bufferevent_data_cb writecb,
bufferevent_event_cb eventcb,
void *cbarg
);
int bufferevent_read(struct bufferevent *bev, void *data, size_t size);
void bufferevent_free(struct bufferevent *bev);
服务端
#include
#include
#include
#include
#include
#include
#include
#include
//读取数据
void read_cb(struct bufferevent *bev, void *ctx) {
char buf[128] = {0};
size_t ret = bufferevent_read(bev, buf, sizeof(buf));
if (ret < 0) {
printf("bufferevent_read error!\n");
} else {
printf("read %s\n", buf);
}
}
void event_cb(struct bufferevent *bev, short what, void *ctx) {
if (what & BEV_EVENT_EOF) {
printf("客户端下线\n");
bufferevent_free(bev); //释放bufferevent对象
} else {
printf("未知错误\n");
}
}
void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg) {
printf("接受%d的连接\n", fd);
struct event_base *base = arg;
//针对已经存在的socket创建bufferevent对象
//事件集合(从主函数传递来)、fd(代表TCP连接)、BEV_OPT_CLOSE_ON_FREE(如果释放bufferevent对象则关闭连接)
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (NULL == bev) {
printf("bufferevent_socket_new error!\n");
exit(1);
}
//给bufferevent设置回调函数
//bufferevent对象、读事件回调函数、写事件回调函数、其他事件回调函数、参数
bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
//使能bufferevent对象
bufferevent_enable(bev, EV_READ);
}
// 常见步骤:
/*
socket
bind
listen
accept
*/
int main() {
//创建一个事件集合
struct event_base *base = event_base_new();
if (NULL == base) {
printf("event_base_new error\n");
exit(1);
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//创建socket、绑定、监听、接受连接
//创建监听对象,在指定的地址上监听接下来的TCP连接
//事件集合、当有连接时调用的函数、回调函数参数、释放监听对象关闭socket|端口重复使用、监听队列长度、绑定信息
//返回值:监听对象
struct evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 10, (struct sockaddr *)&server_addr,sizeof(server_addr));
if (NULL == listener) {
printf("evconnlistener_new_bind error\n");
exit(1);
}
//监听集合中的事件
event_base_dispatch(base);
//释放两个对象
evconnlistener_free(listener);
event_base_free(base);
exit(0);
}
客户端:
#include
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
int main()
{
//创建socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
perror("socket");
exit(1);
}
//发起连接请求
struct sockaddr_in server_info; //保存服务器的信息
bzero(&server_info, sizeof(server_info));
server_info.sin_family = AF_INET;
server_info.sin_port = htons(8000);
server_info.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(sockfd, (struct sockaddr *)&server_info, sizeof(server_info)) == -1)
{
perror("connect");
exit(2);
}
char buf[1024] = {0};
while (1)
{
scanf("%s", buf);
if (send(sockfd, buf, strlen(buf), 0) == -1)
{
perror("send");
break;
}
if (!strcmp(buf, "bye"))
break;
bzero(buf, 1024);
}
close(sockfd);
return 0;
}
运行结果: