Libevent库使用指南

Libevent库使用指南

Libevent库概述

简介

  • Libevent:是一个用C语言编写的、轻量级的开源高性能事件通知库,是一个基于事件的异步通信模型
  • Libevent是基于reactor实现的
  • Libevent广泛地应用作为底层的网络库,比如memcached、 Vomit、 Nylon、 Netchat等
  • 下载Libevent库,看到stable表示稳定版本,现在稳定版有1.4、2.0以及2.1系列,使用的话使用最新的稳定版即可,但是初学源码从1.4学比较好(源码简单)

优点

  1. 开源
  2. 精简,轻量级,专注于网络通信
  3. 跨平台,支持windows、Linux、Mac Os等
  4. 事件驱动,高性能
  5. 支持多种I/O多路复用技术,epoll、poll、dev/poll、select和kqueue等
  6. 支持I/O,定时器和信号等事件

安装

  1. Libevent中下载最新的稳定版2.1.12-stable

  2. 解压刚刚下载的压缩包,得到一个目录

  3. 进入到目录,进行源码包安装(如果有README.md文件也可以进行查看)

    1
    2
    3
    4
    5
    6
    7
    8
    #检查安装环境,生成makefile
    ./configure

    #生产.o和可执行文件
    make

    #将必要的资源拷贝到系统指定目录
    sudo make install
  4. 进入sample目录,运行demo验证库安装使用情况,比方说编译hello-world.c文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #需要加库选项 -levent
    gcc hello-world.c -o hello-world -levent

    #运行
    ./hello-world

    #另开终端测试,因为这是一个服务器
    nc 127.0.0.1 9995

    #返回Hello,World!
  5. 可以到/usr/lib或者/usr/local/lib下输入以下命令查看是否存在libevent库也就是libevent.so

    1
    2
    3
    4
    5
    6
    cd /usr/lib

    #或者
    cd /usr/local/lib

    ll libevent*

cmake链接Libevent库

1
2
3
4
5
6
7
8
9
find_package(Libevent REQUIRED)


include_directories(${LIBEVENT_INCLUDE_DIR})

add_executable(main main.cpp)

#main是我要链接的可执行文件名
target_link_libraries(main ${LIBEVENT_LIBRARIES})

Libevent基本框架

  1. 创建event_base:event_base_new

  2. 创建事件

    事件分为:

    • 常规事件event:event_new
    • 带缓冲区事件bufferevent:bufferevent_socket_new
  3. 将事件添加到base上

    • 常规事件event:event_add
    • 带缓冲区事件bufferevent
  4. 循环监听事件满足:event_base_dispatch

  5. 释放event_base:event_base_free

event_base

创建event_base

struct event_base *event_base_new(void):用于创建event_base

1
2
3
4
5
6
7
8
9
10
11
12
#include <event2/event.h>

struct event_base *event_base_new(void);


//使用
struct event_base *base=NULL;
base=event_base_new();


//返回值
返回:event_base变量;

释放event_base

void event_base_free(struct event_base *base):用于释放event_base

1
2
3
4
5
6
#include <event2/event.h>

void event_base_free(struct event_base *base);

//参数
base:event_base变量;

事件循环

启动循环

int event_base_dispatch(struct event_base *base)阻塞监听event_base

1
2
3
4
5
6
7
8
9
10
11
#include <event2/event.h>

int event_base_dispatch(struct event_base *base);

//参数
base:event_base变量;

//返回值
成功:0;

失败:-1;

注意:

  • 只有event_new中指定了EV_PERSIST才能持续触发,否则只触发一次,就跳出循环
  • 通常设定为:EV_WRITE|EV_PERSIST、EV_READ|EV_PERSIST

退出循环

  • int event_base_loopbreak(struct event_base *base,const struct timeval *tv)立即停止循环

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <event2/event.h>

    int event_base_loopbreak(struct event_base *base);

    //参数
    base:要停止循环的event_base变量;

    //返回值
    成功:0;

    失败:-1;
  • int event_base_loopexit(struct event_base *base,const struct timeval *tv)在指定时间后停止循环

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <event2/event.h>

    int event_base_loopexit(struct event_base *base,const struct timeval *tv);


    //参数
    base:要退出循环的event_base变量;

    tv:定时时间;

    //返回值
    成功:0;

    失败:-1;

常规事件event

创建事件

strutc event *event_new(struct event_base *base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg)用于创建一个常规事件,里面已经调用了初始化函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <event2/event.h>

struct event *event_new(struct event_base *base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg);

//event_callback_fn定义的回调函数
typedef void (*event_callback_fn)(evutil_socket_t, short, void *);


//函数实际原型
event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg);



#define EV_TIMEOUT 0x01 //废弃
#define EV_READ 0x02 //读事件
#define EV_WRITE 0x04 //写事件
#define EV_SIGNAL 0x08 //等待信号发生事件
#define EV_PERSIST 0x10 //持续触发
#define EV_ET 0x20 //边缘触发模式
#define EV_FINALIZE 0x40
/*删除事件时就不会阻塞了,不会等到回调函数执行完毕;为了在多线程中安全使用,需要使用event_finalize()或者event_free_finalize()*/

#define EV_CLOSED 0x80
/*可以自动监测关闭的连接,然后放弃读取未完的数据,但是不是所有后台方法都支持这个选项*/





//参数
base:要将新创建的事件放入的event_base;

fd:event所绑定的文件描述符;

what:监听fd上的事件(读、写、异常);
//列表:
EV_READ:读事件;
EV_WRITE:写事件;
EV_PERSIST:持续触发,没有这个参数事件发生一次就会退出监听阻塞;
...:剩余部分见上面宏定义;


cb:监听事件发生时所调用的回调函数;

arg:回调函数参数;



//返回值
返回:常规事件event对象;

初始化事件

int event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, event_callback_fn callback, void *arg))初始化事件,再创建事件时会调用该函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <event2/event.h>



int event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, event_callback_fn callback, void *arg));

typedef void (*event_callback_fn)(evutil_socket_t,short,void *);

//函数原型
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));


//参数
ev:要初始化的event事件对象;

base:event事件变量要加入的event_base变量;

fd:event所绑定的文件描述符;

callback:监听事件发生时所调用的回调函数;

arg:回调函数参数;


//返回值
成功:0;

失败:-1;

添加事件

int event_add(struct event *ev,const struct timeval *tv)将创建的事件添加到其创建时所填的event_base中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <event2/event.h>

int event_add(struct event *ev,const struct timeval *tv);


//参数
ev:事件event对象,event_new返回值;

tv:定时时间,一般传NULL;
//取值:
NULL:不会超时,阻塞等待,事件触发调用回调函数;
>0:等待时间,监听事件没有被触发,时间一到,回调函数依旧会被调用;


//返回值
成功:0;

失败:-1;

删除事件

int event_del(struct event *ev)从创建事件所绑定的event_base中删除指定事件

1
2
3
4
5
6
7
8
9
10
11
12
#include <event2/event.h>

int event_del(struct event *ev);


//参数
ev:要从event_base中删除的事件对象;

//返回值
成功:0;

失败:-1;

释放事件

void event_free(struct event *ev)释放(销毁)事件

1
2
3
4
5
6
7
#include <event2/event.h>


void event_free(struct event *ev);

//参数
ev:要销毁释放的事件对象;

其他函数

  • const char **event_get_supported_methods(void)查看支持哪些多路IO模型

    1
    2
    3
    4
    5
    6
    #include <event2/event.h>

    const char **event_get_supported_methods(void);

    //返回值
    返回:当前系统支持哪些多路IO模型,字符串数组;
  • const char *event_base_get_method(const struct event_base *base)查看当前用的多路IO模型

    1
    2
    3
    4
    5
    6
    #include <event2/event.h>

    const char *event_base_get_method(const struct event_base *base);

    //返回值
    返回:当前用的多路IO模型;
  • int event_reinit(struct event_base *base)重新初始化fork后子进程的event_base变量,使用该函数后,父进程创建的base才能在子进程中生效,相当于子进程中的event_base_new函数,不是所有事件后端都在调用 fork之后可以正确工作,所以需要重新初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <event2/event.h>

    int event_reinit(struct event_base *base);

    //参数
    base:需要重新初始化的event_base对象;

    //返回值
    成功:0;

    失败:-1;

未决和非未决

未决有资格被处理,但还没被处理,将事件添加到event_base中(event_add)则非未决态变成未决态

非未决没有资格处理,新创建的事件(event_new)都处于非未决态

激活态事件在阻塞监听时,当事件被触发后,则事件从未决态变为激活态

被处理态事件的回调函数调用后,激活态变为被处理态,然后自动进入非未决态

带缓冲区事件bufferevent

bufferevnt

  • 头文件

    1
    #include <event2/bufferevent.h>
  • 原理bufferevent有两个缓冲区(读和写),也是队列实现的,读走即没,先进先出

  • :有数据则调用读回调函数,使用bufferevent_read读数据

  • :使用bufferevent_write向写缓冲区写数据,写缓冲区中一旦有数据就自动发送给对端,数据写完后,调用写回调函数(通知写数据完成)

创建带缓冲区事件

struct bufferevent *bufferevent_socket_new(struct event_base *base,evutil_socket_t fd,enum bufferevent_options options):创建一个带缓冲区事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <event2/bufferevent.h>

struct bufferevent *bufferevent_socket_new(struct event_base *base,evutil_socket_t fd,enum bufferevent_options options);


//参数
base:要将新创建的带缓冲区事件放入的event_base;

fd:bufferevent所绑定的文件描述符;

options:函数行为控制参数,一般为BEV_OPT_CLOSE_ON_FREE;
//参数列表
BEV_OPT_CLOSE_ON_FREE:释放bufferevent时关闭底层传输端口,将关闭底层套接字,释放底层bufferevent等;

BEV_OPT_THREADSAFE:自动为bufferevent分配锁,这样可以安全地在多个线程中使用bufferevent;

BEV_OPT_DEFER_CALLBACKS:设置这个标志时,bufferevent延迟所有回调;

BEV_OPT_UNLOCK_CALLBACKS:默认情况下,如果设置bufferevent为线程安全的,则bufferevent会在调用用户提供的回调时进行锁定,设置这个选项会让libevent在执行回调的时候不进行锁定;

//返回值
返回:带缓冲区事件bufferevent对象;

释放带缓冲区事件

void bufferevent_free(struct bufferevent *bev)释放(销毁)带缓冲区事件

1
2
3
4
5
6
7
#include <event2/bufferevent.h>

void bufferevent_free(struct bufferevent *bev);


//参数
bev:要释放的带缓冲区事件,bufferevent_socket_new返回值;

设置缓冲区回调函数

void bufferevent_setcb(struct bufferevent *bufev,bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void *cbarg)给带缓冲区事件对象bufev的缓冲区设置回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <event2/bufferevent.h>


void bufferevent_setcb(struct bufferevent *bufev,bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void *cbarg);


//参数
bufev:要设置缓冲区回调函数的带缓冲区事件对象;

readcb:给读缓冲区设置的回调函数,不需要可以传NULL;

writecb:给写缓冲区设置的回调函数,不需要可以传NULL;

eventcb:事件满足时的回调函数,用来处理异常或者其他情况,不需要可以传NULL;

cbarg:回调函数的参数;

注意:writecb回调函数是在写操作完成后调用的

readcb和writecb回调函数原型

1
2
3
4
5
6
typedef void (*bufferevent_data_cb)(struct bufferevent *bev,void *ctx);

//参数
bev:要绑定的bufferevent对象;

ctx:事件回调传递的参数;

eventcb回调函数原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef void (*bufferevent_event_cb)(struct bufferevent *bev,short events,void *ctx);


//参数
bev:要绑定的bufferevent对象;

events:事件参数,不同标志位,代表不同的事件;
//列表
EV_EVENT_READING:读取操作时发生某事件,具体是哪种事件,看其他标志;
EV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件,看其他标志;
BEV_EVENT_ERROR:操作时发生错误,可以用EVUTIL_SOCKET_ERROR()查看错误信息;
BEV_EVENT_TIMEOUT:发生超时;
BEV_EVENT_EOF:遇到文件结束提示符;
[*]BEV_EVENT_CONNECTED:请求的连接过程已经完成,实现客户端时可用;

ctx:事件回调传递的参数;

启用/禁用缓冲区

引言:在套接字中的两个缓冲区可以支持半关闭,因此在bufferevent的缓冲区中也可以通过启用/禁用支持全双工/半关闭

函数原型

  • void bufferevent_enable(struct bufferevent *bufev,short events)启用缓冲区,通常用来启用的read缓冲区

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <event2/bufferevent.h>

    void bufferevent_enable(struct bufferevent *bufev,short events);

    //参数
    bufev:要启用缓冲区的bufferevent对象;

    events:要启用的缓冲区类型(读、写和读写);
    //取值列表
    EV_READ:读缓冲区;
    EV_WRITE:写缓冲区;
    EV_READ|EV_WRITE:读写缓冲区;
  • void bufferevent_disable(struct bufferevent *bufev,short events)禁用缓冲区

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <event2/bufferevent.h>

    void bufferevent_disable(struct bufferevent *bufev,short events);

    //参数
    bufev:要启用缓冲区的bufferevent对象;

    events:要启用的缓冲区类型(读、写和读写);
    //取值列表
    EV_READ:读缓冲区;
    EV_WRITE:写缓冲区;
    EV_READ|EV_WRITE:读写缓冲区;
  • short bufferevnt_get_enabled(struct bufferevent *bufev)获取缓冲区的禁用状态,需要借助&来得到

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <event2/bufferevent.h>

    short bufferevnt_get_enabled(struct bufferevent *bufev);

    //参数
    bufev:要查看缓冲区状态的bufferevent对象;

    //返回值
    返回:缓冲区状态;

    //得到的返回值与你想查看是否禁用的缓冲区类型进行&操作
    bufferevnt_get_enabled(bufev)&EV_READ:当结果为true则表示读缓冲区禁用,否则就是启用

注意:新建的bufferevent写缓冲是启用状态的,读缓冲是禁用状态的

设置/调整缓冲区水位

概念

  • 缓冲区"水位":也就是缓冲区的界限,对于低水位就是缓冲区中数据最小数,高水位就是缓冲区最大存储数据数

  • 对于每个bufferevent对象,都有4个水位分别为

    1. 读取低水位读缓冲区数据量高于此水位后,读取回调被调用。默认值0,导致一旦缓冲区有数据,立即调用回调
    2. 读取高水位读缓冲区数据量高于此水位后,bufferevent不再将数据读取到缓冲区。默认值无限
    3. 写入低水位写缓冲区数据量低于此水位后,写入回调被调用。默认值0,导致仅当缓冲区全部写入到底层传输接口后,才调用写入回调
    4. 写入高水位没有直接使用。当一个bufferevent用作另外一个bufferevent的底层传输接口时有用

函数原型

void bufferevent_setwatermark(struct bufferevent *bufev.short events,size_t lowmark,size_t highmark)设置bufferevent对象的缓冲区水位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <event2/bufferevent.h>

void bufferevent_setwatermark(struct bufferevent *bufev.short events,size_t lowmark,size_t highmark);

//参数
bufev:要设置/调整水位的bufferevent对象;

events:要设置/调整水位的缓冲区类型;
//取值列表
EV_READ:读缓冲区;
EV_WRITE:写缓冲区;

lowmark:低水位;

highmark:高水位;

建立连接函数

int bufferevent_socket_connect(struct bufferevent *bev,const struct sockaddr *sa,int socklen)客户端与服务端建立连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <event2/bufferevent.h>

int bufferevent_socket_connect(struct bufferevent *bev,const struct sockaddr *sa,int socklen);

//参数
bev:用于建立连接的bufferevent对象,因为bufferevent封装了fd;

sa:服务器IP地址结构;

socklen:服务器IP地址结构大小;


//返回值
成功:0;

失败:-1;

缓冲区IO操作函数

读缓冲区数据

  • size_t bufferevent_read(struct bufferevent *bufev,void *data,size_t size)从读缓冲区中读指定字节数数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <event2/bufferevent.h>


    size_t bufferevent_read(struct bufferevent *bufev,void *data,size_t size);


    //参数
    bufev:要读取读缓冲区数据的带缓冲区事件对象;

    data:传出参数,用于指向存储从读缓冲区中读出来的数据的缓冲区,该存储大小大于size值;

    size:表示要读取的数据的最大字节数;


    //返回值
    >0:实际读取到数据的字节数;
    =0:没有数据可读;
    -1,errno:读取时发生错误,并设置errno;
  • int bufferevent_read_buffer(struct bufferevent *bufev,struct evbuffer *buf)将读缓冲区的所有数据读出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <event2/bufferevent.h>

    int bufferevent_read_buffer(struct bufferevent *bufev,struct evbuffer *buf);

    //参数
    bufev:要读取读缓冲区数据的带缓冲区事件对象;

    buf:传出参数,用于指向存储从读缓冲区中读出来的数据的缓冲区;

    //返回值
    成功:0;

    失败:-1;

注意:

  • 使用 bufferevent_read() 函数只会从套接字缓冲区中读取数据,并不会阻塞等待数据到达。如果没有数据可读,函数将立即返回,因此需要在事件处理器的读回调函数中多次调用该函数,以确保读取所有可用的数据
  • 以上两个函数,所读出数据则读缓冲区对应数据移除,也就是读走即没

写缓冲区数据

  • int bufferevent_write(struct bufferevent *bufev,const void *data,size_t size)向写缓冲区末尾写入data缓冲区开头到size字节大小的数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <event2/bufferevent.h>


    int bufferevent_write(struct bufferevent *bufev,const void *data,size_t size);


    //参数
    bufev:要写入数据的写缓冲区的带缓冲区事件对象;

    data:指向数据源缓冲区;

    size:写入数据大小;

    //返回值
    成功:0;

    失败:-1;
  • int bufferevent_write_buffer(struct bufferevent *bufev,struct evbuffer *buf)将data中的所有数据写入到bufev中的写缓冲区末尾

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <event2/bufferevent.h>

    int bufferevent_write_buffer(struct bufferevent *bufev,struct evbuffer *buf);


    //参数
    bufev:要写入数据的写缓冲区的带缓冲区事件对象;

    data:指向数据源缓冲区;


    //返回值
    成功:0;

    失败:-1;

evconnlistener监听器

监听连接/创建

函数原型

  • struct evconnlistener_new(struct event_base *base,evconnlistener_cb cb,void *ptr,unsigned flags,int backlog,evutil_socket_t fd)创建一个事件监听者对象(监听器),用于监听客户端的建立连接请求,一般直接使用evconnlistener_new_bind函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #include <event2/listener.h>

    struct evconnlistener_new(struct event_base *base,evconnlistener_cb cb,void *ptr,unsigned flags,int backlog,evutil_socket_t fd);


    //参数
    base:用于监听连接的event_base对象;

    cb:当新连接请求时,要调用的回调函数,一般用于与客户端进行通信;

    ptr:回调函数参数;

    flags:控制函数行为参数,多个参数用"|";
    //取值列表
    LEV_OPT_CLOSE_ON_FREE:释放bufferevent时,将底层套接字关闭并且释放底层bufferevent等;
    LEV_OPT_REUSEABLE:端口复用;
    LEV_OPT_LEAVE_SOCKETS_BLOCKING:默认情况下,监听器是非阻塞模式的用于Libevent库所需,该参数可以设置成阻塞;
    LEV_OPT_THREADSAFE:为监听器分配锁,用于多线程安全;
    LEV_OPT_CLOSE_ON_EXEC:在监听的socket上设置 FD_CLOEXEC 选项;


    backlog:建立连接的上限值,传-1表示使用默认最大值128;

    fd:监听文件描述符,socket创建出来的文件描述符;

    //返回值
    返回:事件监听器对象;

    注意这个函数实质是完成了listen()和accept()函数的工作,并没有完成socket()和bind(),需要自行先行完成

  • struct evconnlistener *evconnlistener_new_bind(struct event_base *base,evconnlistener_cb cb,void *ptr,unsigned flags,int backlog,const struct sockaddr *sa,int socklen)创建一个事件监听者对象(监听器),用于监听客户端的建立连接请求,完成了socket、bind、listen、accept函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #include <event2/listener.h>

    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对象;

    cb:当新连接请求时,要调用的回调函数,一般用于与客户端进行通信;

    ptr:回调函数参数;

    flags:控制函数行为参数,多个参数用"|";
    //取值列表
    LEV_OPT_CLOSE_ON_FREE:释放bufferevent时,将底层套接字关闭并且释放底层bufferevent等;
    LEV_OPT_REUSEABLE:端口复用;
    LEV_OPT_LEAVE_SOCKETS_BLOCKING:默认情况下,监听器是非阻塞模式的用于Libevent库所需,该参数可以设置成阻塞;
    LEV_OPT_THREADSAFE:为监听器分配锁,用于多线程安全;
    LEV_OPT_CLOSE_ON_EXEC:在监听的socket上设置 FD_CLOEXEC 选项;

    backlog:建立连接的上限值,传-1表示使用默认最大值128;

    sa:传入参数,服务器IP地址结构;

    socklen:服务器IP地址结构大小;


    //返回值
    返回:事件监听器对象;

监听器回调函数

typedef void (*evconnlistener_cb)(struct evconnlistener *listener,evutil_socket_t sock,struct sockaddr *addr,int len,void *ptr)创建监听器时的回调函数参数原型,用于与客户端通信

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef void (*evconnlistener_cb)(struct evconnlistener *listener,evutil_socket_t sock,struct sockaddr *addr,int len,void *ptr);


//参数
listener:监听器对象,evconnlistener_new_bind函数的返回值;

sock:用于通信的文件描述符;

addr:客户端的IP地址结构;

len:客户端的IP地址结构大小;

ptr:外部ptr传递的值,也就是创建监听器中的回调函数参数;

设置/调整回调

void evconnlistener_set_cb(struct evconnlistener *lev,evconnlistener_cb cb,void *arg)可以重新调整修改监听器的回调函数以及回调函数参数

1
2
3
4
5
6
7
8
9
10
#include <event2/listener.h>

void evconnlistener_set_cb(struct evconnlistener *lev,evconnlistener_cb cb,void *arg);

//参数
lev:要重新调整/设置回调函数以及回调函数参数的监听器对象;

cb:新的回调函数;

arg:也就是新的ptr,替换原来的ptr;

销毁/释放

void evconnlistener_free(struct evconnlistener *lev)释放监监听器对象

1
2
3
4
5
6
#include <event2/listener.h>

void evconnlistener_free(struct evconnlistener *lev);

//参数
lev:要释放的监听器对象;

启用/禁用

  • int evconnlistener_enable(struct evconnlistener *lev)重新允许监听新连接

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <event2/listener.h>


    int evconnlistener_enable(struct evconnlistener *lev);

    //参数
    lev:要允许监听新连接的监听器对象;

    //返回值:
    成功:0;

    失败:-1;
  • int evconnlistener_disable(struct evconnlistener *lev)暂时禁止监听器监听新连接

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <event2/listener.h>


    int evconnlistener_disable(struct evconnlistener *lev);

    //参数
    lev:要禁止监听新连接的监听器对象;

    //返回值:
    成功:0;

    失败:-1;

异常处理

void evconnlistener_set_error_cb(struct evconnlistener *lev,evconnlistener_errorcb errorcb):可以给监听器设置一个错误捕捉的回调函数,当监听器错误产生时就调用该回调函数

1
2
3
4
5
6
7
8
9
#include <event2/listener.h>

void evconnlistener_set_error_cb(struct evconnlistener *lev,evconnlistener_errorcb errorcb);


//参数
lev:要设置错误回调函数的监听器对象;

errorcb:要设置的错误回调函数;

错误回调函数:

1
2
3
4
5
6
typedef void (*evconnlistener_errorcb)(struct evconnlistener *lis, void *ptr);

//参数
lis:要设置错误回调函数的监听器对象;

ptr:回调函数参数;

TCP服务器

server

具体步骤

  1. 创建event_base,使用event_base_new

  2. 创建一个服务器连接监听器,并设置其回调函数,当有客户端成功连接时,回调函数被调用,使用evconnlistener_new_bind

  3. 封装连接监听器的回调函数,该回调函数完成以下操作

    1. 创建bufferevent事件对象,使用bufferevent_socket_new
    2. 使用bufferevent_setcb给read、write、event事件设置回调函数,并添加到event_base中
    3. 设置bufferevent的读写缓冲区启用/禁用
    4. 当监听的事件满足时,调用对应事件的回调函数
  4. 使用bufferevent_read/bufferevent_write进行接收/发送数据

  5. 启动循环监听,使用event_base_dispatch

  6. 释放连接

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <cstring>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
#include <arpa/inet.h>
#include <event2/util.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <strings.h>
#include <cstdio>
#include <unistd.h>


#define SER_PORT 5005

//读回调函数
void read_cb(struct bufferevent *bev,void *ptr);
//写回调函数
void write_cb(struct bufferevent *bev,void *ptr);
//事件回调函数
void event_cb(struct bufferevent *bev,short events,void *ptr);



void read_cb(struct bufferevent *bev,void *ptr){
char buf[BUFSIZ];
bzero(&buf,sizeof(buf));
bufferevent_read(bev,buf,sizeof(buf));
printf("client reply :%s\n",buf);
char *p="我是服务器,已经成功收到你发送的数据!";
bufferevent_write(bev,p,strlen(p)+1);
sleep(1);
}



void write_cb(struct bufferevent *bev,void *ptr){
printf("我是服务器,成功写数据到客户端,写缓冲区回调函数被回调\n");
}


void event_cb(struct bufferevent *bev,short events,void *ptr){
if(events&BEV_EVENT_EOF){
printf("connection closed\n");
}
else if (events&BEV_EVENT_ERROR) {
printf("some other error\n");
}

bufferevent_free(bev);
printf("bufferevent 资源已经被释放\n");
}



//监听器回调函数
void cb_listener(struct evconnlistener *listener,evutil_socket_t fd,struct sockaddr *addr,int len,void *ptr){
printf("connect new client\n");

struct event_base *base=(struct event_base*)ptr;

//添加新事件
struct bufferevent *bev;
bev=bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE);

//给bufferevent缓冲区设置回调
bufferevent_setcb(bev,read_cb,write_cb,event_cb,NULL);

//启用bufferevent的读缓冲,默认是disable
bufferevent_enable(bev,EV_READ);
}


int main (int argc, char *argv[]) {
//服务端地址结构
struct sockaddr_in ser_addr;
bzero(&ser_addr,sizeof(ser_addr));
ser_addr.sin_family=AF_INET;
ser_addr.sin_port=htons(SER_PORT);
ser_addr.sin_addr.s_addr=htonl(INADDR_ANY);

//创建event_base
struct event_base *base=NULL;
base=event_base_new();

//创建套接字,绑定,监听连接请求
struct evconnlistener *listener; //监听器
listener=evconnlistener_new_bind(base,cb_listener,base,LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,-1,(struct sockaddr *)&ser_addr,sizeof(ser_addr));

//启动循环监听
event_base_dispatch(base);

//释放资源
evconnlistener_free(listener);
event_base_free(base);
return 0;
}

client

具体步骤

  1. 创建event_base,使用event_base_new
  2. 使用bufferevent_socket_new创建一个跟服务器通信的bufferevent事件对象
  3. 使用bufferevent_socket_connect连接服务器
  4. 使用bufferevent_setcb设置read、write、event设置回调
  5. 启用/禁用bufferevent对象的读写缓冲区
  6. 使用bufferevent_read/bufferevent_write进行接收/发送数据
  7. 启动循环监听,使用event_base_dispatch
  8. 释放资源

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/util.h>
#include <strings.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstdio>
#include <string.h>
#include <unistd.h>

#define SER_PORT 5005



//读回调函数
void read_cb(struct bufferevent *bev,void *ptr);
//写回调函数
void write_cb(struct bufferevent *bev,void *ptr);
//事件回调函数
void event_cb(struct bufferevent *bev,short events,void *ptr);
//新建事件的回调函数
void read_ter(evutil_socket_t fd,short what,void *arg);



void read_cb(struct bufferevent *bev,void *ptr){
char buf[BUFSIZ];
bzero(&buf,sizeof(buf));
bufferevent_read(bev,buf,sizeof(buf));
printf("server reply: %s\n",buf);
bufferevent_write(bev,buf,strlen(buf)+1);
sleep(1);
}


void write_cb(struct bufferevent *bev,void *ptr){
printf("我是客户端的写回调函数\n");
}



void event_cb(struct bufferevent *bev,short events,void *ptr){
if(events&BEV_EVENT_EOF){
printf("connection closed\n");
}
else if (events&BEV_EVENT_ERROR) {
printf("some other error\n");
}else if (events&BEV_EVENT_CONNECTED) { //判断连接
printf("已经连接服务器\n");
return;
}

bufferevent_free(bev);
printf("bufferevent 资源已经被释放\n");
}


void read_ter(evutil_socket_t fd,short what,void *arg){
char buf[BUFSIZ];
bzero(&buf,sizeof(buf));
int len=read(fd,buf,sizeof(buf));
struct bufferevent *bev=(struct bufferevent *)arg;
bufferevent_write(bev,buf,len+1);
}


int main (int argc, char *argv[]) {
struct event_base *base=NULL;
base=event_base_new();

int fd=socket(AF_INET,SOCK_STREAM,0);

struct bufferevent *bev=NULL;
bev=bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE);

struct sockaddr_in ser_addr;
bzero(&ser_addr, sizeof(ser_addr));
ser_addr.sin_family=AF_INET;
ser_addr.sin_port=htons(SER_PORT);
ser_addr.sin_addr.s_addr=htonl(INADDR_ANY);

//连接服务器
bufferevent_socket_connect(bev,(struct sockaddr *)&ser_addr,sizeof(ser_addr));

//设置回调
bufferevent_setcb(bev,read_cb, write_cb, event_cb,NULL);

//设置读回调生效
bufferevent_enable(bev, EV_READ);

//创建事件
struct event *ev=event_new(base,STDIN_FILENO, EV_READ|EV_PERSIST,read_ter,bev);

//添加事件
event_add(ev,NULL);

//循环监听
event_base_dispatch(base);

//释放资源
event_free(ev);
event_base_free(base);
return 0;
}

Libevent库使用指南
https://moonfordream.github.io/posts/Libevent库使用指南/
作者
Moon
发布于
2024年6月10日
许可协议