一、libevent库的安装(ubuntu)
- root用户运行以下命令:
apt-get install libevent-dev
- 非root用户:
sudo apt-get install libevent-dev
- 编译命令
gcc 文件名 -o 文件名 -levent
二、libevent-IO事件(使用的是libevent库中的event集合)
步骤:
- 创建事件
- 初始化事件集合
- 初始化事件:把fd和事件ev绑定
- 把事件添加到集合中
- 开始监听
1.event
event 结构体是 libevent 库中的一个核心结构,用于表示一个事件。它包含了事件的各种属性和状态信息,以及与事件相关的回调函数和超时设置。
2.event_init
在 libevent 中,event_init 函数仍然是用于初始化事件的正确函数。
以下是 event_init 函数的用法示例:
void event_init(void);
这个函数用于初始化 libevent 库。它在使用 libevent 之前必须被调用一次。event_init 函数会执行一些必要的初始化操作,以确保 libevent 正常工作。
在调用 event_init 函数后,你可以使用其他 libevent 函数来创建事件、设置事件回调函数、添加事件到事件循环等。
3.event_set
在 libevent 中,event_set 函数用于设置事件的属性和回调函数。它可以用于指定事件的文件描述符、事件类型、回调函数等。
以下是 event_set 函数的用法示例:
int event_set(struct event *ev, int fd, short events, void (*callback)(int, short, void *), void *arg);
参数:
- ev:指向要设置属性的事件结构体的指针。
- fd:事件的文件描述符。
- events:事件的类型
在 libevent 中,事件类型由以下宏定义表示:- EV_TIMEOUT:超时事件,即在指定的时间间隔之后触发。
- EV_READ:读事件,当文件描述符可读时触发。
- EV_WRITE:写事件,当文件描述符可写时触发。
- EV_SIGNAL:信号事件,当指定的信号发生时触发。
- EV_PERSIST:持久事件,即事件在触发后仍然保持激活状态,直到显式删除。
- EV_ET:边缘触发事件,仅在文件描述符状态发生变化时触发一次。
这些宏可以单独使用,也可以通过按位或运算符(|)进行组合,以便同时指定多个事件类型。
- callback:事件发生时要调用的回调函数的指针。
- arg:传递给回调函数的参数。
返回值:
该函数返回一个整数值,表示函数调用的结果。如果成功设置事件属性,则返回 0;否则返回 -1。
4.event_add
在 libevent 中,event_add 函数用于将事件添加到事件循环中,以便在事件发生时触发相应的回调函数。
以下是 event_add 函数的用法示例:
int event_add(struct event *ev, const struct timeval *tv);
参数:
- ev:指向要添加到事件循环的事件结构体的指针。
- tv:指向 struct timeval 结构体的指针,用于指定事件的超时时间。如果为 NULL,事件将无限期地等待触发。
返回值:
该函数返回一个整数值,表示函数调用的结果。如果成功将事件添加到事件循环中,则返回 0;否则返回 -1。
5.event_dispatch
在 libevent 中,event_dispatch 函数用于开始事件循环,等待事件的发生并触发相应的回调函数。
以下是 event_dispatch 函数的用法示例:
int event_dispatch(void);
返回值:
该函数返回一个整数值,表示函数调用的结果。如果事件循环正常结束,则返回 0;否则返回 -1。
6.代码实例
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <event.h>
#include <unistd.h>
#include <stdlib.h>
//执行event_set函数时,会把event_set中的fd和ev参数传给回调函数(在这即fifo_read)
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(3);
}
printf("从管道读取的内容:%s\n", buf);
}
int main()
{
//创建管道
int ret = mkfifo("fifo.tmp", 00700);
if(-1 == ret)
{
perror("mkfifo0");
exit(1);
}
int fd = open("fifo.tmp", O_RDONLY);
if(-1 == fd)
{
perror("open");
exit(2);
}
//创建事件
struct event ev;
//初始化事件集合
event_init();
//初始化事件(把fd和事件ev绑定)
//参数:事件,关联的文件描述符,事件类型,回调函数,回调函数参数
event_set(&ev, fd, EV_READ | EV_PERSIST, fifo_read, NULL);
//把事件添加到集合中
event_add(&ev, NULL);
//开始监听
event_dispatch(); //死循环,如果集合中没有事件可以监听,则返回
return 0;
}
三、libevent-信号事件(自定义事件)
步骤:
- 创建事件集合
- 创建事件
- 把事件和信号绑定
- 把事件添加刀集合中
- 监听集合
- 释放集合
1.event_base_new
在 libevent 中,event_base_new 函数用于创建事件基础(event base)对象,即事件的管理和调度器()即集合。
以下是 event_base_new 函数的用法示例:
struct event_base *event_base_new(void);
返回值:
该函数返回一个指向 struct event_base 结构体的指针,表示创建的事件基础对象。如果函数调用失败,则返回 NULL。
2.event_assign
在 libevent 中,event_assign 函数用于将一个已经存在的事件对象与指定的事件基础对象相关联。
以下是 event_assign 函数的用法示例:
int event_assign(struct event *event, struct event_base *base, evutil_socket_t fd, short events, event_callback_fn callback, void *arg);
参数:
- event:指向已经存在的 struct event 结构体的指针,表示要关联的事件对象。
- base:指向 struct event_base 结构体的指针,表示要关联到的事件基础对象。
- fd:要关联的文件描述符。
- events:表示关注的事件类型
在 libevent 中,事件类型由以下宏定义表示:- EV_TIMEOUT:超时事件,即在指定的时间间隔之后触发。
- EV_READ:读事件,当文件描述符可读时触发。
- EV_WRITE:写事件,当文件描述符可写时触发。
- EV_SIGNAL:信号事件,当指定的信号发生时触发。
- EV_PERSIST:持久事件,即事件在触发后仍然保持激活状态,直到显式删除。
- EV_ET:边缘触发事件,仅在文件描述符状态发生变化时触发一次。
这些宏可以单独使用,也可以通过按位或运算符(|)进行组合,以便同时指定多个事件类型。
- callback:事件发生时要调用的回调函数。
- arg:传递给回调函数的参数。
返回值:
该函数返回一个整数值,表示函数调用的结果。如果函数调用成功,返回值为 0;如果函数调用失败,返回值为 -1。
3.event_base_dispatch
event_base_dispatch 是 libevent 库中用于事件循环的函数之一。它用于启动事件循环,监听注册的事件并调用相应的回调函数。
以下是 event_base_dispatch 函数的基本用法:
#include <event2/event.h>
int event_base_dispatch(struct event_base *base);
参数:
- base:指向 event_base 结构的指针,表示事件的基础结构。event_base 是事件驱动编程模型中的核心结构,用于管理事件、事件处理器、定时器等。
返回值:
返回值是一个整数,表示函数的执行状态。如果函数成功启动事件循环,返回值为 0;如果出现错误,返回值为 -1。
4.event_base_free
event_base_free 是 Libevent 库中的一个函数,用于释放事件基础结构所占用的资源。
函数原型:
void event_base_free(struct event_base *base);
参数:
- base:指向 event_base 结构的指针,表示要释放的事件基础结构。
5.代码实例
#include <stdio.h>
#include <event.h>
#include <signal.h>
int signal_count = 0;
//fd这里是SIGINT 的数值,events这里是EV_SIGNAL | EV_PERSIST
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 >= 2)
{
//把事件从集合中删除
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);
return 0;
}
三、libevent-高并发服务器
步骤:
- 创建一个事件集合
- 创建socket,绑定、监听和接受连接并创建监听对象,在指定地址监听TCP连接
① 调用回调函数
② 针对已经存在的socket创建bufferevent
③ 设置bufferevent的回调函数
有2个回调函数,一个正常执行调用,一个异常调用
④ 使能bufferevent对象 - 监听集合事件
- 释放对象
1.evconnlistener_new_bind
libevent 是一个高性能的网络通信库,它提供了异步事件通知等功能。evconnlistener_new_bind 函数是 libevent 中用于创建一个新的监听器(listener)来接受TCP连接的函数。这个函数非常重要,因为它允许程序异步地接受新的连接,而不必自己管理socket监听和接受连接的复杂性。
函数原型:
struct evconnlistener *evconnlistener_new_bind(
struct event_base *base,
evconnlistener_cb cb,
void *ptr,
unsigned flags,
int backlog,
const struct sockaddr *sa,
int socklen
);
参数:
- base:一个指向 event_base 结构的指针,这个结构代表了libevent的事件处理循环。所有的事件和监听器都需要绑定到一个 event_base 上。
- cb:一个回调函数,当有新的连接到来时,这个函数会被调用。它的类型是 evconnlistener_cb,原型如下:
typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);
//定义回调函数:
void evconnlisterer(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *)
{
...
}
其中,evutil_socket_t 是接受到的新连接的socket描述符,sockaddr 是指向结构体的指针,包含了新连接的地址信息,socklen 是地址信息的长度,void * 是用户提供的参数,通常用于传递给回调函数的上下文或状态信息。
- ptr:传递给回调函数的用户定义的数据,可以是指向任何用户定义数据的指针。
- flags:控制监听器行为的标志位,可以是以下值的位或(bitwise OR)组合:
- LEV_OPT_CLOSE_ON_FREE:当监听器被释放时关闭底层的socket。
- LEV_OPT_REUSEABLE:设置socket为可重用,允许立即重新绑定地址和端口。
- LEV_OPT_THREADSAFE(在libevent 2.1及以后的版本中可用):使得监听器在多线程环境中是线程安全的。
- backlog:决定了内核中等待接受的连接队列的最大长度。这个值通常由 SOMAXCONN 定义,它提供了系统级的默认值。
- sa:指向 sockaddr 结构的指针,包含了要绑定的地址和端口信息。
- socklen:sockaddr 结构的长度。
返回值:
- 成功时,返回一个指向新创建的 evconnlistener 结构的指针,这个结构代表了一个监听器实例。
- 失败时,返回 NULL。
2.bufferevent_socket_new
在libevent中,bufferevent_socket_new函数用于创建一个基于套接字的bufferevent对象。它提供了一个方便的方式来处理网络I/O。
函数原型:
struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options);
参数:
- base:指向event_base对象的指针,用于处理事件。
- fd:套接字文件描述符。
- options:选项标志,用于设置bufferevent的行为。可以使用以下标志的按位或运算来指定多个选项:
- BEV_OPT_CLOSE_ON_FREE:在释放bufferevent时自动关闭底层套接字。
- BEV_OPT_THREADSAFE:启用线程安全的bufferevent操作。
返回值:
- 成功:指向新创建的bufferevent对象的指针。
- 失败:返回NULL。
3.bufferevent_setcb
bufferevent_setcb函数用于设置bufferevent对象的回调函数,以便在相应的事件发生时进行处理。它允许您指定读取、写入和事件回调函数,以及传递上下文参数。
函数原型:
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb,
bufferevent_data_cb writecb,
bufferevent_event_cb eventcb,
void *cbarg);
参数:
- bufev:指向bufferevent对象的指针。
- readcb:读取数据时调用的回调函数。
- writecb:写入数据时调用的回调函数。
- eventcb:处理网络事件时调用的回调函数。
- cbarg:回调函数的上下文参数,可以是任意类型的指针。
回调函数的原型如下:
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);
回调函数的定义:
void read_cb(struct bufferevent *bev, void *ctx) {
// 处理读取数据的回调函数
}
void write_cb(struct bufferevent *bev, void *ctx) {
// 处理写入数据的回调函数
}
void event_cb(struct bufferevent *bev, short events, void *ctx) {
// 处理网络事件的回调函数
}
void event_cb(struct bufferevent *bev, short events, void *ctx)
参数解释:
- bev:指向触发事件的bufferevent对象的指针。
- events:一个表示触发的事件类型的位掩码。可以使用以下标志进行按位与运算来检查具体的事件类型:
- BEV_EVENT_READING:读取事件被触发。
- BEV_EVENT_WRITING:写入事件被触发。
- BEV_EVENT_EOF:连接已经关闭,即达到了文件结束位置。
- BEV_EVENT_ERROR:发生了错误。
- BEV_EVENT_TIMEOUT:超时事件被触发。
- BEV_EVENT_CONNECTED:连接已经建立。
- ctx:用户上下文指针,可以在注册回调函数时传递,并在回调函数中使用。
4.bufferevent_enable
bufferevent_enable函数用于启用或禁用bufferevent对象的读取和写入事件监听。它允许您指定要启用或禁用的事件类型。
函数原型:
void bufferevent_enable(struct bufferevent *bufev, short event);
参数:
- bufev:指向bufferevent对象的指针。
- event:要启用事件类型,可以使用以下标志进行按位或运算:
- EV_READ:启用读取事件。
- EV_WRITE:启用写入事件。
5.bufferevent_write
bufferevent_write 是 libevent 中用于向缓冲事件(bufferevent)写入数据的函数。
它的原型如下:
int bufferevent_write(struct bufferevent *bev, const void *data, size_t size);
参数:
- bev:指向要写入数据的缓冲事件。
- data:要写入的数据的指针。
- size:要写入的数据的大小(字节数)。
返回值:
- 返回值为 0,表示写入操作成功,所有的数据都已成功写入到缓冲事件的输出缓冲区中。
- 返回值为 -1,表示写入操作失败,数据未能完全写入到缓冲事件的输出缓冲区中。
6.bufferevent_read
bufferevent_read 是 libevent 中用于从缓冲事件(bufferevent)读取数据的函数。
它的原型如下:
size_t bufferevent_read(struct bufferevent *bev, void *data, size_t size);
参数:
- bev:指向要读取数据的缓冲事件。
- data:用于存储读取数据的缓冲区的指针。
- size:要读取的数据的最大大小(字节数)。
返回值:
- 返回值大于 0,表示成功从缓冲事件的输入缓冲区中读取了指定大小的数据,并将其存储到指定的缓冲区中。(返回的是实际读取的数据大小(字节数))
- 返回值为 0,表示输入缓冲区中没有可读取的数据。
- 返回值为 -1,表示读取操作失败。
7.代码实例
#include <stdio.h>
#include <event.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <event2/listener.h>
void event_cb(struct bufferevent *bev, short what, void *ctx)
{
//如果what的值与BEV_EVENT_EOF进行按位与运算结果为非零,表示连接已经关闭,即客户端下线。在这种情况下,打印"客户端下线"的消息,并释放bufferevent对象,以确保资源的正确释放。
if(what & BEV_EVENT_EOF)
{
printf("客户端下线\n");
bufferevent_free(bev);
}
else
{
printf("未知错误\n");
}
}
void read_cb(struct bufferevent *bev, void *ctx)
{
char buf[128] = {0};
size_t ret = bufferevent_read(bev, buf, sizeof(buf));
if(ret < 0)
{
perror("bufferevent\n");
}
else
{
printf("read %s\n", buf);
}
}
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
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if(NULL == bev)
{
perror("bufferevent");
exit(3);
}
//给bufferevent设置回调函数
bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
//使能bufferevent对象
bufferevent_enable(bev, EV_READ);
}
int main()
{
//创建一个事件集合
struct event_base *base = event_base_new();
if(NULL == base)
{
perror("event_base_new");
exit(1);
}
struct sockaddr_in server_info;
bzero(&server_info, 0);
server_info.sin_family = AF_INET;
server_info.sin_port = htons(7000);
server_info.sin_addr.s_addr = inet_addr("127.0.0.1");
//创建socket,绑定,监听和接受连接
//创建监听对象,在指定的地址上监听接下来的TCP连接
//
struct evconnlistener *listerner = evconnlistener_new_bind(base, listener_cb, base, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 10,
(struct sockaddr *)&server_info, sizeof(server_info));
if(NULL == listerner)
{
perror("evconnlisterner");
exit(2);
}
//监听事件集合中的事件
event_base_dispatch(base);
//释放两个对象
evconnlistener_free(listerner);
event_base_free(base);
return 0;
}