libevent学习笔记

libevent学习笔记

1.什么是libevent?

Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大;源代码相当精炼、易读;跨平台,支持 Windows、 Linux、 *BSD 和 Mac Os;支持多种 I/O 多路复用技术, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定时器和信号等事件;注册事件优先级。

2.基本数据类型

1.event_base

一个监听对象,一般libevent为了监听多个文件描述符只需要将多个事件绑定到event_base对象即可

1
2
3
4
5
6
7
8
9
struct event_base *event_base_new();										//创建一个event_base
void event_base_free(struct event_base *base); //释放一个event_base
int event_base_priority_init(struct event_base *base, int n_priorities); //为事件设置优先级,以后事件优先级可以从0到n_priorities,(0最高)
int event_reinit(struct event_base *base); //调用fork函数之后要重新初始化base,因此要使用此函数重新初始化
int event_base_loop(struct event_base *base, int flags); //开始运行循环
int event_base_dispatch(struct event_base *base); //同event_base_loop,只是flag没有设置
int event_base_loopexit(struct event_base *base,const struct timeval *tv); //让event_base在指定的时间之后停止循环
int event_base_loopbreak(struct event_base *base); //让event_base立即停止循环
void event_base_dump_events(struct event_base *base, FILE *f); //将event_base的状态转存到文件中
2.event

一个事件,libevent监听的最小单位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define EV_TIMEOUT      0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10
#define EV_ET 0x20
typedef void (*event_callback_fn)(evutil_socket_t, short, void *); //触发事件调用的回调函数类型

struct event *event_new(struct event_base *base, evutil_socket_t fd,
short what, event_callback_fn cb,
void *arg); //创建一个新的event

void event_free(struct event *event); //释放一个新的event
int event_priority_set(struct event *event, int priority);
3.bufferevent

缓冲区监听事件,libevent常用bufferevent管理socket缓冲区的读写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct bufferevent *bufferevent_socket_new(
struct event_base *base,
evutil_socket_t fd,
enum bufferevent_options options);//创建一个bufferevent
int bufferevent_socket_connect(struct bufferevent *bev,
struct sockaddr *address, int addrlen);
void bufferevent_free(struct bufferevent *bev); //释放一个bufferevent
void bufferevent_enable(struct bufferevent *bufev, short events);
void bufferevent_disable(struct bufferevent *bufev, short events); //屏蔽bufferevent对应的触发事件
void bufferevent_setwatermark(struct bufferevent *bufev, short events,
size_t lowmark, size_t highmark); //设置bufferevent 读写操作水位
struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);
struct evbuffer *bufferevent_get_output(struct bufferevent *bufev); //获取bufferevent对象的evbuffer结构体
int bufferevent_write(struct bufferevent *bufev,
const void *data, size_t size);
int bufferevent_write_buffer(struct bufferevent *bufev,
struct evbuffer *buf); //向bufferevent中读取或者写入数据
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
int bufferevent_read_buffer(struct bufferevent *bufev,
struct evbuffer *buf);
4.evBuffer

evBuffer是一个数据缓冲区,常用于创建一块数据缓冲区用于数据的发送和接收

1
2
3
4
5
6
7
8
9
10
struct evbuffer *evbuffer_new(void);
void evbuffer_free(struct evbuffer *buf);//创建或者释放一个evbuffer
size_t evbuffer_get_length(const struct evbuffer *buf);//得到evbuffer当前的数据长度
int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen);//将数据data加入到buf的末尾
int evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src);//将 src 中的所有数据移动到 dst 末尾,成功时返回0,失败时返回-1。

int evbuffer_remove_buffer(struct evbuffer *src,
struct evbuffer *dst,
size_t datlen);//函数从 src 中移动 datlen 字节到 dst 末尾,尽量少进行复制。如果字节数小于 datlen,所有字节被移动。函数返回移动的字节数。

5.evconnlistener

evconnlistener是一个连接对象,一般用于接收来自客户端的连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct evconnlistener *
evconnlistener_new(struct event_base *base,
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
evutil_socket_t fd);

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 *lev);//创建或者销毁一个evconnlistener
typedef void (*evconnlistener_cb)(struct evconnlistener *listener,
evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr);//evconnlistener的回调函数
evutil_socket_t evconnlistener_get_fd(struct evconnlistener *lev);
struct event_base *evconnlistener_get_base(struct evconnlistener *lev);//获取evconnlistener中获取fd或者event_base。

3.基本使用步骤

  • 1.创建监听对象

  • 2.创建监听事件

  • 3.将监听事件加入监听集合

  • 4.开始监听

4.实例

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
//服务器
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <event.h>
#include <arpa/inet.h>
#include <event2/listener.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <strings.h>
//转换ip地址类型
int convertip_to_str(int af, struct sockaddr *addr, char *str_addr, unsigned short *port) {
struct sockaddr_in *addr_tmp = (struct sockaddr_in *) addr;
if (inet_ntop(af, &(addr_tmp->sin_addr), str_addr, SOCKLEN) == NULL) {
return -1;
}
*port = ntohs(addr_tmp->sin_port);
return 0;
}

//转换ip地址类型
int convertip_to_net(int af, struct sockaddr *addr, char *str_addr, unsigned short port) {
struct sockaddr_in *addr_tmp = (struct sockaddr_in *) addr;
addr_tmp->sin_port = htons(port);
if (inet_pton(af, str_addr, &(addr_tmp->sin_addr)) != 1) {
printf("convert ip failed!\n");
return -1;
}
return 0;
}

//帮助信息
void Usage(char *str) {
printf("Usage:\n");
printf("%s SERVERADDR SERVERPORT\n", str);
}

//创建新的socket
int NewSocket(char *addr, char *port, int backlog) {
struct sockaddr ADDR;
convertip_to_net(AF_INET, &ADDR, addr, atoi(port));
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (bind(fd, (struct sockaddr *) &ADDR, SOCKLEN) != 0) {
perror("bind");
return -1;
}
if (listen(fd, backlog) != 0) {
perror("listen");
return -1;
}
return fd;
}
//处理客户端请求
void handler_cb(int fd, short event, void *arg) {
struct sockaddr client;
int client_fd = accept(fd, (struct sockaddr *) &client, &SOCKLEN);
char client_addr[15];
unsigned short client_port;
convertip_to_str(AF_INET, &client, client_addr, &client_port);
printf("client:%s:%hu is connected!\n", client_addr, client_port);
}
int main(int argc,char **argv)
{
if (argc != 3) {
Usage(argv[0]);
}
int fd = NewSocket(argv[1], argv[2], 10);
//创建一个监听集合
struct event_base *base = event_base_new();
//创建一个事件
struct event *new_event = event_new(base, fd, EV_READ | EV_PERSIST, handler_cb, NULL);
struct timeval timeout= {450,30};//设置超时时间
//注册事件
event_add(new_event, &timeout);
//等待事件的发生
event_base_dispatch(base);
}

3.连接监听器和数据缓冲区(evconnlistener,bufferevent)

1.基本的使用步骤

  • 1.创建一个事件监听器
  • 2.设置事件触发的回调函数
  • 3.开始监听事件

2.基本的API

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

//连接监听器

//创建一个新的socket并且绑定监听,返回一个struct evconnlistener类型
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开始监听
evconnlistener_new(struct event_base *base,
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
evutil_socket_t fd);

evconnlistener_disable(struct evconnlistener *lev);
evconnlistener_enable(struct evconnlistener *lev);
//重新设置回调函数
evconnlistener_set_cb(struct evconnlistener *lev,
evconnlistener_cb cb, void *arg);


//创建缓冲区
struct bufferevent *bufferevent_socket_new(struct event_base *base,int fd,int option);

//设置回调函数
struct bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb, bufferevent_data_cb writecb,
bufferevent_event_cb eventcb, void *cbarg)

struct bufferevent *bufferevent_connect(struct bufferevent *bev,
const struct sockaddr *sa, int socklen);
//从libevent自行维护的缓冲区中读取数据
size_t
bufferevent_read(struct bufferevent *bufev, void *data, size_t size);

//向libevent自行维护的缓冲区中写入数据
int
bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);

3.实例

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
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <event.h>
#include <arpa/inet.h>
#include <event2/listener.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <strings.h>
//读回调函数
bufferevent_data_cb read_cb(struct bufferevent *bev, void *arg) {
char buffer[1024];
bufferevent_read(bev, buffer, 10);//从接收缓冲区读取数据
printf("read buffer :%s\n", buffer);
return NULL;
}
//写回调函数
bufferevent_data_cb write_cb(struct bufferevent *bev, void *arg) {
char *str = "hello,world";
bufferevent_write(bev, str, strlen(str) + 1);//向客户端发送消息
return NULL;
}
//异常处理函数
bufferevent_event_cb error_cb(struct bufferevent *bev, short event, void *arg) {
return NULL;
}

evconnlistener_cb
listen_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen,
void *arg) {
//这个回调函数传进来的fd为与客户端的通信的fd不是监听的fd
struct event_base *base = (struct event_base *) arg;
struct bufferevent *buffevent = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);//创建缓冲区
bufferevent_setcb(buffevent, read_cb, write_cb, error_cb, NULL);//设置回调函数
bufferevent_enable(bufferevent,EV_READ|EV_PRESIST|EV_WRITE);//注册事件
}
int main(int argc,char **argv)
{
socklen_t SOCKLEN = (socklen_t) (sizeof(struct sockaddr));
struct event_base *base = event_base_new();
struct sockaddr bind_addr;
convertip_to_net(AF_INET, &bind_addr, "127.0.0.1", 10000);
struct evconnlistener *listener = evconnlistener_new_bind(base, listen_cb, (void *) base,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE_PORT, -1,
&bind_addr, SOCKLEN);
event_base_dispatch(base);//循环监听事件
evconnlistener_free(listener);//释放连接监听器
event_base_free(base);//释放监听集合;
}

4.小结

reactor是一个事件驱动模型,常用于解决需要同时监听多个文件描述符的场景,通常使用一个对象去监听多个文件描述符,而不是简单的轮询实现多个文件描述符的监听,大大的提高的程序的效率,而libevent是一个具体的reactor的解决方案,提供了一系列的接口用于实现reactor模型。因此在实际的开发中,可以直接刁洪libevent库,而不用自己去实现一个reactor模型。

参考https://aceld.gitbooks.io/libevent/content/chapter1.html


libevent学习笔记
https://dreamaccount.github.io/2022/04/17/libevent学习笔记/
作者
404NotFound
发布于
2022年4月17日
许可协议