浅学libevent

libevent简介

libevent是一个用C语言编写的轻量级的开源高性能事件通知库,支持多种I/O多路复用技术,支持定时器和信号等事件,而且是跨平台的。

libevent特点

  • 事件驱动,高性能
  • 轻量级,专注于网络
  • 跨平台,支持WindowsLinuxMacOs
  • 支持多种I/O多路复用技术,epollpolldev/pollselectkqueue

  • 支持I/O,定时器和信号等事件

libevent下载及安装

下载地址:https://libevent.org/

这里我下载的是libevent-2.1.8-stable.tar.gz,下载之后解压。

1
tar -zxvf libevent-2.1.8-stable.tar.gz

接下来进行源码包安装,这一步的目的是检查安装环境,生成makefile

1
./configure

之后执行make命令生成.o可执行文件,最后执行sudo make install将必要资源放置系统指定目录

验证libevent安装

进入libevent-2.1.8-stable下的sample目录,gcc编译hello-world.c文件

1
gcc hello-world.c -o hello

这个时候会出现报错如下,显示未定义引用

1

出现上述报错是因为我们没有指定库名,因此需要加-l选项

1
gcc hello-world.c -o hello -l event

hello文件是一个简单的服务器程序,端口是9995,我们尝试运行hello,它会等待客户端的连接。并向客户端发送Hello,World字符串,如下所示,证明libevent库安装成功。

2

libevent库的安装位置在/usr/local/lib路径下

3

Hello-world分析

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
This example program provides a trivial server program that listens for TCP
connections on port 9995. When they arrive, it writes a short message to
each client connection, and closes each connection once it is flushed.

Where possible, it exits cleanly in response to a SIGINT (ctrl-c).
*/


#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef _WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
# include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif

#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>

static const char MESSAGE[] = "Hello, World!\n";

//指定端口
static const int PORT = 9995;

//声明静态函数
static void listener_cb(struct evconnlistener *, evutil_socket_t,
struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);

int
main(int argc, char **argv)
{
struct event_base *base; //libevent基础声明。相当于一个底座
struct evconnlistener *listener; //监听,用于建立连接
struct event *signal_event; //信号事件

struct sockaddr_in sin; //地址结构
#ifdef _WIN32
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif

base = event_base_new(); //创建底座
if (!base) {
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}

memset(&sin, 0, sizeof(sin)); //地址结构清零
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);

//相当于socket、bind、listen、accept的融合
listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
(struct sockaddr*)&sin,
sizeof(sin));

if (!listener) {
fprintf(stderr, "Could not create a listener!\n");
return 1;
}

//创建信号事件
signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);

if (!signal_event || event_add(signal_event, NULL)<0) {
fprintf(stderr, "Could not create/add a signal event!\n");
return 1;
}

//相当于while循环+select/poll/epoll
event_base_dispatch(base);

//释放相关事件
evconnlistener_free(listener);
event_free(signal_event);
event_base_free(base);

printf("done\n");
return 0;
}

static void
listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data)
{
struct event_base *base = user_data;
struct bufferevent *bev;

bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);
return;
}
bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
bufferevent_enable(bev, EV_WRITE);
bufferevent_disable(bev, EV_READ);

bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}

static void
conn_writecb(struct bufferevent *bev, void *user_data)
{
struct evbuffer *output = bufferevent_get_output(bev);
if (evbuffer_get_length(output) == 0) {
printf("flushed answer\n");
bufferevent_free(bev);
}
}

static void
conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
if (events & BEV_EVENT_EOF) {
printf("Connection closed.\n");
} else if (events & BEV_EVENT_ERROR) {
printf("Got an error on the connection: %s\n",
strerror(errno));/*XXX win32*/
}
/* None of the other events can happen here, since we haven't enabled
* timeouts */
bufferevent_free(bev);
}

static void
signal_cb(evutil_socket_t sig, short events, void *user_data)
{
struct event_base *base = user_data;
struct timeval delay = { 2, 0 }; //延迟两秒

printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");

event_base_loopexit(base, &delay);
}

libevent框架

创建event_base
1
2
3
4
#include<event2/event.h>
struct event_base *event_base_new(void);
struct event_base *base = NULL;
base = event_base_new();
创建事件
  • 常规事件 event—->event_new()
  • 带缓冲区的事件 bufferevent —->bufferevent_socket_new()
添加事件
1
int event_add(struct event *ev,const struct timeval *tv);
启动循环
1
2
3
int event_base_dispatch(struct event_base *base);
base:event_base_new 函数的返回值
成功:0,失败:-1

只有event_new中指定了EV_PERSIST才持续触发,否则只触发一次,就跳出循环

通常设置为EV_WRITE|EV_PERSISTEV_READ|EV_PERSIST

其他循环

在指定时间后停止循环

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

立即停止循环

1
int event_base_loopbreak(struct event_base *base);
释放event_base
1
event_base_free(base);

查看支持哪些多路I/O

event.c代码如下

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<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<event2/event.h>

void sys_err(const char *str)
{
perror(str);
exit(1);
}

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

int i;
const char **buf;
buf = event_get_supported_methods();

for (i=0;i<10;i++){
printf("buf[%d]=%s\n",i,buf[i]);
}
return 0;
}

gcc编译

1
gcc event.c -o event -l event

执行结果如下,可以发现mac并不支持epoll

1
2
3
4
5
6
7
8
9
10
11
caoyifan@MacBookPro event % ./event 
buf[0]=poll
buf[1]=select
buf[2]=(null)
buf[3]=(null)
buf[4]=(null)
buf[5]=(null)
buf[6]=(null)
buf[7]=(null)
buf[8]=(null)
buf[9]=(null)

创建事件

1
2
3
4
5
6
7
8
9
10
11
12
struct event *event_new(struct event_base *base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg);

base: event_base_new()返回值
fd: 绑定到event上的文件描述符
what: 对应的事件(读、写、异常)
EV_READ 一次读事件
EV_WRITE 一次写事件
EV_PERSIST 持续触发,结合event_base_dispatch函数使用
cb: 一旦事件满足监听条件,回调的函数
typedef void(*event_callback_fn)(evutil_socket_t fd,short,void *)
arg: 回调的函数的参数
返回值: 成功创建的event

添加事件到base

1
2
3
4
int event_add(struct event *ev,const struct timeval *tv);

ev: event_new()函数返回的事件
tv: NULL,即不会超时 一旦等到事件被触发,回调函数会被调用

从base拿下事件

1
2
3
int event_del(struct event *ev)

ev: event_new()的返回值

销毁事件

1
2
3
4
5
int event_free(struct event *ev)

ev: event_new()的返回值
成功: 0
失败: -1

使用libevent读写fifo

read_fifo.c代码如下

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
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<event2/event.h>

void read_cb(evutil_socket_t fd,short what,void *arg)
{
//读管道
char buf[1024] = {0};
int len = read(fd,buf,sizeof(buf));
printf("read event: %s \n",what & EV_READ ?"Yes":"No");
printf("data len = %d,buf = %s\n",len,buf);
sleep(1);
}

//读管道
int main(int argc,const char* argv[])
{
unlink("myfifo");
mkfifo("myfifo",0644);

int fd = open("myfifo",O_RDONLY | O_NONBLOCK);
if(fd == -1){
perror("open error");
exit(1);
}

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

//创建事件
struct event *ev = NULL;
ev = event_new(base,fd,EV_READ|EV_PERSIST,read_cb,NULL);

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

//事件循环
event_base_dispatch(base);

//释放资源
event_free(ev);
event_base_free(base);
close(fd);

return 0;
}

write_fifo.c代码如下

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<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<event2/event.h>

void write_cb(evutil_socket_t fd,short what,void *arg)
{
//写管道
char buf[1024] = {0};
static int num = 0;
sprintf(buf,"hello,world-%d\n",num++);
write(fd,buf,strlen(buf)+1);
sleep(1);
}

//写管道
int main(int argc,const char* argv[])
{
int fd = open("myfifo",O_WRONLY | O_NONBLOCK);
if(fd == -1){
perror("open error");
exit(1);
}

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

//创建事件
struct event *ev = NULL;
ev = event_new(base,fd,EV_WRITE|EV_PERSIST,write_cb,NULL);

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

//事件循环
event_base_dispatch(base);

//释放资源
event_free(ev);
event_base_free(base);
close(fd);

return 0;
}

在mac上编译执行之后发现并没有打印结果,gdb调试一下,发现卡在了dispatch这里,dispatch函数就相当于是while+select/poll/epoll这种形式,对于libevent库默认使用的是epoll,然而epoll对于mac是不支持的。

4

于是我把代码放到了centos服务器上,./read_fifo运行报错如下

1
error while loading shared libraries: libevent-2.1.so.6: cannot open shared object file: No such file or directory

ldd查看依赖发现没有找到libevent-2.1.so.6

1
2
3
4
5
6
7
8
9
10
elssm@VM-20-13-centos ~/event> ldd read_fifo
linux-vdso.so.1 => (0x00007ffcbc5f7000)
/$LIB/libonion.so => /lib64/libonion.so (0x00007fda57d92000)
libevent-2.1.so.6 => not found
libc.so.6 => /lib64/libc.so.6 (0x00007fda57657000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fda57453000)
libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007fda56ff0000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fda56dd4000)
/lib64/ld-linux-x86-64.so.2 (0x00007fda57c79000)
libz.so.1 => /lib64/libz.so.1 (0x00007fda56bbe000)

创建软链接如下

1
sudo ln -s /usr/lib/libevent-2.1.so.6 /usr/lib64/libevent-2.1.so.6

再次查看依赖

1
2
3
4
5
6
7
8
9
10
elssm@VM-20-13-centos ~/event> ldd read_fifo
linux-vdso.so.1 => (0x00007ffcbc5f7000)
/$LIB/libonion.so => /lib64/libonion.so (0x00007fda57d92000)
libevent-2.1.so.6 => /lib64/libevent-2.1.so.6 (0x00007fda57a25000)
libc.so.6 => /lib64/libc.so.6 (0x00007fda57657000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fda57453000)
libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007fda56ff0000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fda56dd4000)
/lib64/ld-linux-x86-64.so.2 (0x00007fda57c79000)
libz.so.1 => /lib64/libz.so.1 (0x00007fda56bbe000)

之后分别执行./read_fifo./write_fifo。发现成功读写

5

事件的未决和非未决

  • 未决:有资格被处理,但尚未被处理
  • 非未决:没有资格被处理

事件的未决和非未决状态转换图如下所示

6

bufferevent

1
#include<event2/bufferevent.h>

bufferevent有两个缓冲区,也是队列实现,先进先出

读:有数据——>读回调函数被调用——->使用bufferevent_read()——>读数据

写:使用bufferevent_write()——->向写缓冲中写数据——->该缓冲区有数据自动写出——>写完,回调函数被调用

bufferevent创建和释放

创建bufferevent

1
2
3
4
5
6
7
8
struct bufferevent *ev
struct bufferevent *bufferevent_socket_new(struct event_base *base,evutil_socket_tfd,enum bufferevent_options options);

base: event_base_new函数的返回值
fd: 跟bufferevent绑定的文件描述符类比event_new()
options: BEV_OPT_CLOSE_ON_FREE只用这一个即可

返回: 成功创建的bufferevent事件对象

释放bufferevent

1
void bufferevent_free(struct bufferevent *bev)

给读写缓冲区设置回调

1
2
3
4
5
6
7
void bufferevent_setcb(struct bufferevent *bufev,bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void *cbarg);

bufev: bufferevent_socket_new()函数的返回值
readcb:读缓冲对应的回调,自己封装,在其内部读数据
writecb:设置bufferevent写缓冲 不用,传NULL
eventcb: 设置事件回调。可传NULL
cbarg: 回调函数用的参数
readcb对应的回调函数
1
typedef void(*bufferevent_data_cb)(struct bufferevent *bev,void *ctx);

例如

1
2
3
4
void read_cb(struct bufferevent *bev,void *arg){
....
bufferevent_read();
}
1
2
size_t bufferevent_read(struct bufferevent *bufev,void *data,size_t size);
//通常用在readcb中,代替read()
writecb对应的回调函数
1
2
3
void write_cb(struct bufferevent *bev,void *arg){
....
}
1
2
int bufferevent_write(struct bufferevent *bufev,const void *data,size_t size);
//常用在bufferevent_read之后,代替write()
eventcb对应的回调函数
1
2
3
4
5
6
7
8
9
typedef void(*bufferevent_event_cb)(struct bufferevent *bev,short events,void *ctx);

events: 不同标志位,代表不同的事件
BEV_EVENT_READING: 读取操作时发生某事件,具体是哪种事件,看其他标志
BEV_EVENT_WRITING: 写入操作时发生某事件,具体是哪种事件,看其他标志
BEV_EVENT_ERROR: 操作时发生错误
BEV_EVENT_TIMEOUT: 发生超时
BEV_EVENT_EOF: 遇到文件结束指示
BEV_EVENT_CONNECTED: 请求的连接过程已经完成,实现客户端时可用

禁用和启用缓冲区

默认:新建的bufferevent写缓冲时enable的,而读缓冲是disable

1
2
void bufferevent_enable(struct bufferevent *bufev,short events);
//通常用来启用bufferevent的read缓冲
1
2
3
void bufferevent_disable(struct bufferevent *bufev,short events); //禁用

events: EV_READ、EV_WRITE、EV_READ|EV_WRITE
1
2
short bufferevent_get_enabled(struct bufferevent *bufev);
//获取缓冲区的禁用状态,需要借助&来得到

客户端连接服务器

1
2
3
4
5
int bufferevent_socket_connect(struct bufferevent *bev,struct sockaddr *address,int addrlen);

bev: bufferevent事件对象(封装了fd)
address: 地址结构
addrlen: 地址长度

服务器创建监听器

1
2
3
4
5
6
7
8
9
#include<event2/listener.h>
struct evconnlistener * evconnlistener_new(
struct event_base *base,
evconnlistener_cb cb,
void *ptr,
unsigned flags,
int backlog,
evutil_socket_t fd
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct evconnlistener *listener
struct evconnlistener *evconnlistener_new_bind(
struct event_base *base,
evconnlistener_cb cb,
void *ptr,
unsigned flags,
int backlog,
const struct sockaddr *sa,
int socklen
);

cb: 监听回调函数,接受连接之后用户要做的操作
ptr: 回调函数的参数
flags: “可识别的标志”
LEV_OPT_CLOSE_ON_FREE: 释放bufferevent时关闭底层传输端口,这将关闭底层套接字,释放底层bufferevent
LEV_OPT_REUSEABLE: 端口复用,可以"|"
backlog: listen()函数的第二个参数,传-1表示使用默认最大值
sa: 服务器自己的地址结构体,IP+Port
socklen: 服务器自己的地址结构体大小

返回值: 成功创建的监听器
回调函数类型
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: addr的len
ptr: 外部ptr传递进来的值

释放监听服务器

1
evconnlistener_free(listener);

服务端bufferevent创建TCP连接流程

  • 创建event_base
  • 创建服务器连接监听器evconnlistener_new_bind()
  • evconnlistener_new_bind()的回调函数中,处理接受连接后的操作
  • 回调函数被调用,说明有一个新的客户端连接,会得到一个新的fd,用于和客户端进行通信
  • 创建bufferevent事件对象,bufferevent_socket_new(),将fd封装到这个事件对象中
  • 使用bufferevent_setcb()函数给buffereventread、write、event设置回调函数
  • 设置读缓冲、写缓冲的使能状态 enable、disable
  • 接受、发送数据bufferevent_read()/bufferevent_write()
  • 启动循环event_base_dispatch()
  • 释放资源

服务端bufferevent实现

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
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<event2/event.h>
#include<event2/listener.h>
#include<event2/bufferevent.h>

//读缓冲区回调
void read_cb(struct bufferevent *bev,void *arg)
{
char buf[1024] = {0};
bufferevent_read(bev,buf,sizeof(buf));
printf("client say : %s\n",buf);
char *p = "我是服务器,已经成功收到你发送的数据!";
bufferevent_write(bev,p,strlen(p)+1);
sleep(1);
}

//写缓冲区回调
void write_cb(struct bufferevent *bev,void *arg)
{
printf("我是服务器的写回调函数...\n");
}

//事件回调
void event_cb(struct bufferevent *bev,short events,void *arg)
{
if (events & BEV_EVENT_EOF)
{
printf("connection close\n");
}
else if(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
bufferevent_free(bev);
printf("buffevent资源已经被释放...\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的读缓冲
bufferevent_enable(bev,EV_READ);
}

int main(int argc,const char *argv[])
{
struct sockaddr_in serv;
memset(&serv,0,sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(9527);
serv.sin_addr.s_addr = htonl(INADDR_ANY);

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

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

event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}

编译

1
gcc ev_server.c -o ev_server -l event

客户端bufferevent创建TCP连接流程

  • 创建event_base
  • 使用bufferevent_socket_new()创建一个用于跟服务器通信的bufferevent事件对象
  • 使用bufferevent_socket_connect()连接服务器
  • 使用bufferevent_setcb()bufferevent对象的read、write、event设置回调
  • 设置bufferevent对象的读写缓冲区enable/disable
  • 接受、发送数据bufferevent_read()/bufferevent_write()
  • 启动循环监听event_base_dispatch()
  • 释放资源

客户端bufferevent实现

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
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<event2/bufferevent.h>
#include<event2/event.h>
#include<arpa/inet.h>

//读缓冲区回调
void read_cb(struct bufferevent *bev,void *arg)
{
char buf[1024] = {0};
bufferevent_read(bev,buf,sizeof(buf));
printf("server say : %s\n",buf);
bufferevent_write(bev,buf,strlen(buf)+1);
sleep(1);
}

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

//事件回调
void event_cb(struct bufferevent *bev,short events,void *arg)
{
if (events & BEV_EVENT_EOF)
{
printf("connection close\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("buffevent资源已经被释放...\n");
}


void read_terminal(evutil_socket_t fd,short what,void *arg)
{

char buf[1024] = {0};
int len = read(fd,buf,sizeof(buf));
struct bufferevent *bev = (struct bufferevent*)arg;
//发送数据
bufferevent_write(bev,buf,len+1);
}

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

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

//通信的fd放在bufferevent中
struct bufferevent *bev = NULL;
bev = bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE);

struct sockaddr_in serv;
memset(&serv,0,sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(9527);
inet_pton(AF_INET,"127.0.0.1",&serv.sin_addr.s_addr);

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

//设置回调
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_terminal,bev);

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

event_base_dispatch(base);
event_free(ev);
event_base_free(base);
return 0;
}

编译

1
gcc ev_client.c -o ev_client -l event

服务端客户端测试

7