libevent

一、libevent库的安装(ubuntu)

  1. root用户运行以下命令:
apt-get install libevent-dev
  1. 非root用户:
sudo apt-get install libevent-dev
  1. 编译命令
gcc 文件名 -o 文件名 -levent

二、libevent-IO事件(使用的是libevent库中的event集合)

步骤:

  1. 创建事件
  2. 初始化事件集合
  3. 初始化事件:把fd和事件ev绑定
  4. 把事件添加到集合中
  5. 开始监听

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. 创建事件集合
  2. 创建事件
  3. 把事件和信号绑定
  4. 把事件添加刀集合中
  5. 监听集合
  6. 释放集合

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-高并发服务器

步骤:

  1. 创建一个事件集合
  2. 创建socket,绑定、监听和接受连接并创建监听对象,在指定地址监听TCP连接
    ① 调用回调函数
    ② 针对已经存在的socket创建bufferevent
    ③ 设置bufferevent的回调函数
    有2个回调函数,一个正常执行调用,一个异常调用
    ④ 使能bufferevent对象
  3. 监听集合事件
  4. 释放对象

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;

}

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值