libevent库
开源。精简。跨平台(Windows,Linux,maxos)。专注于网络通信。
源码包安装:参考README
1、./configure 检查安装环境 生成makefile
2、make 生成.o和执行文件
3、sudo make install 将必要的资源拷贝至系统指定目录
4、进入sample目录进行验证,运行demo验证库安装使用情况
5、编译使用库的.c时,需要加 -levent
/usr/local/lib中可以查看到
特性
基于“事件”异步通信模型。----回调机制。
libevent框架
1、创建event_base(底座)
struct event_base *event_base_new(void);
struct event_base *base = event_base_new();
2、创建事件event
常规事件event
struct event *ev;
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(持续触发,结合循环生效使用)
cb:一旦事件满足监听条件,回调函数
typedef void(*event_callback_fn)(evutil_socket_t fd , short , void*)
arg:回调函数参数
返回值:成功创建的事件
event_new();
bufferevent
bufferevent_socket_new();
3、将事件添加到base上
int event_add(struct event *ev , const struct timeval *tv);
ev:event_new()函数返回值
tv:NULL不超时 回调函数一定被调用
非0,等待时间
返回值成功0失败-1
从base上摘下事件(了解)
int event_del(struct event *ev)
4、循环监听事件满足
int event_base_dispatch(struct event_base *base);
5、释放event_base
int event_base_free(base);
6、不常用一些函数
const char ** event_get_supported_methods(); // 查看支持的多路IO方法
char *buf[10];
buf = event_get_supported_methods();
char buf[1024];
buf = event_base_get_method(base); // 查看目前的多路IO方法
事件的未决和非未决
未决:有资格被处理,但是还没有被处理
非未决:没有资格被处理
解释:事件被创建出来时是处于非未决状态,因为现在没有在base上,所以没有资格被处理,当调用event_add函数的时候,事件被插在base上,有资格被处理,故为未决态,当对应事件触发并且进入循环,事件会变为激活态,等回调函数被调用,事件被处理之后,事件又重新变为非未决态,如果设置了EV_PERSIST,事件会持续触发,变为未决态。
带缓冲区的事件bufferevent
#include<event2/bufferevent.h>
bufferevent有两个缓冲区:也是队列实现的,只能读一次,先进先出(FIFO)。
创建销毁
struct bufferevent *ev
struct bufferevent *bufferevent_socket_new(struct event_base*base , evutil_socket_t fd ,
enum bufferevent_options options);
base:event_base
fd:封装到bufferevent内的fd
options:BEV_OPT_CLOSE_ON_FREE
返回:成功创建的bufferevent对象
void bufferevent_socket_free(struct bufferevent *ev)
给读写缓冲区设置回调
对比event:event_new(base , fd , cb , void*arg); event_add----挂到base
bufferevent_socket_new(fd) bufferevent_setcb(call_back);
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:设置bufferevent读缓冲,对应回调
read_cb(){ bufferevent_read()读数据 }
writecb:设置bufferevent写缓冲,对应回调
write_cb(){ } --给调用者发送写成功通知
可以直接传NULL
eventcb:设置事件回调,也可以传NULL
cbarg:上述回调函数的参数
read回调函数
typedef void(*bufferevent_data_cb)(struct bufferevent* bufev , void*ctx);
eg. void read_cb(struct bufferevent* bufev , void*arg)
{
bufferevent_read() //读数据
}
size_t bufferevent_read(struct bufferevent* bufev , void*data , size_t size);
在read_cb中代替read函数!!!
event回调函数
typedef void(*bufferevent_data_cb)(struct bufferevent* bufev , shot events , void*ctx);
void event_cb(struct bufferevent* bufev , shot events , void*arg)
{
.......
}
events:BEV_EVENT_CONNECTED;
错误事件回调函数
void event_cb(struct bufferevent *bev, short events, void *ctx) {
if (events & BEV_EVENT_CONNECTED) {
printf("✅ Connected to server.\n");
} else if (events & BEV_EVENT_ERROR) {
perror("❌ Error from bufferevent");
} else if (events & BEV_EVENT_EOF) {
printf("⚠️ Connection closed by server.\n");
}
}
禁用、启用bufferevent缓冲区
默认新建的bufferevent写缓冲是enable,而读缓冲是disable。
void bufferevent_enable(struct bufferevent* bufev , short events);
events:EV_READ , EV_WRITE , EV_READ | EV_WRITE.
void bufferevent_disable(struct bufferevent* bufev , short events);
网络通信
连接客户端
socket()、connect()
int bufferevent_socket_connect(struct bufferevent* bev ,
struct sockaddr*address , int addrlen);
服务器创建监听器服务器
socket()、bind()、listen()、accept()
下面这一个函数代替了上述四个函数的作用。
#include<event2/listener.h>
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)
flags:可识别的标志 |
LEV_OPT_CLOSE_ON_FREE: 释放bufferevent时关闭底层传输端口(关闭套接字、释放底层)
LEV_OPT_REUSEABLE : 端口复用
backlog:-1默认使用最大值 listen参2
返回值:成功创建的监听器。
回调函数:一旦被回调,说明在其内部应该与客户端建立完成,数据读写操作,进行通信
typedef void(*evconnlistener_cb)(struct evconnlistener* listener ,
evutil_socket_t sock , struct sockaddr*addr ,
int len , void* ptr);
sock:用于通信的文件描述符
addr:客户端的地址结构
回调函数调用成功表示:一旦被回调,说明在其内部应该与客户端建立完成,数据读写操作,进行通信。
服务器端libevent创建TCP连接
1.创建event_base
2.创建一个bufferevent事件对象 bufferevent_socket_new();
3.使用bufferevent_setcb()给bufferevent的read,write,event设置回调函数
4.当监听的事件满足时,read_cb会被调用,在其内部bufferevent_read()读操作
5.使用evconnlistener_new_bind创建一个监听服务器,设置其回调函数,当客户端成功连接时,回调函数会被调用
6.封装listener_cb回调函数。在函数内部完成和客户端通信
7.设置读缓冲和写缓冲的使能状态
8.启动循环监听event_base_dispatch();
9.释放连接
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<event2/bufferevent.h>
#include<ctype.h>
#include<event2/listener.h>
#define SER_PORT 9004
void read_cb(struct bufferevent* bufev , void*arg)
{
char buf[BUFSIZ];
int n , i;
n = bufferevent_read(bufev , buf , sizeof(buf));
for(i = 0 ; i < n ; i++)
buf[i] = toupper(buf[i]);
bufferevent_write(bufev , buf , n);
return;
}
void write_cb(struct bufferevent* bufev , void*arg)
{
printf("recived form client\n");
return;
}
void event_cb(struct bufferevent *bev, short events, void *ctx) {
if (events & BEV_EVENT_CONNECTED) {
printf("✅ Connected to server.\n");
} else if (events & BEV_EVENT_ERROR) {
perror("❌ Error from bufferevent");
} else if (events & BEV_EVENT_EOF) {
printf("⚠️ Connection closed by server.\n");
}
}
void listen_cb(struct evconnlistener* listener ,
evutil_socket_t sock , struct sockaddr*addr , int len , void* ptr)
{
struct event_base*base = (struct event_base*)ptr;
struct bufferevent*bev = bufferevent_socket_new(base , sock , BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev , read_cb , write_cb , event_cb , NULL);
bufferevent_enable(bev , EV_READ);//默认读缓冲是不可的,所以要设置成enable
return;
}
int main(int argc , char *argv[])
{
struct event_base*base = event_base_new();
struct sockaddr_in serv_addr;
bzero(&serv_addr , sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SER_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
struct evconnlistener*listener = evconnlistener_new_bind(base , listen_cb , base ,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE , -1 , (struct sockaddr*)&serv_addr,
sizeof(serv_addr));
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0 ;
}
实现TCP客户端流程
1.创建event_base
2.创建一个bufferevent事件对象 bufferevent_socket_new();
3.使用bufferevent_setcb()给bufferevent的read,write,event设置回调函数
4.当监听的事件满足时,read_cb会被调用,在其内部bufferevent_read()读操作
6.使用bufferevent_socket_connect回调函数。建立连接
7.可以添加一个事件,用来用用户和终端交互,监听终端的写事件,就可以不用使用dup2
8.将监听事件挂在base上 event_add
8.启动循环监听event_base_dispatch();
9.释放连接
注意:先建立bufferevent_socket_new,创建出来缓冲区----bufferevent_setcb()----设置读缓冲使能bufferevent_enable()----建立连接bufferevent_socket_connect。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<event2/bufferevent.h>
#include<event2/event.h>
#include<fcntl.h>
#include<arpa/inet.h>
#define SER_PORT 9004
void read_cb(struct bufferevent* bufev , void*arg)
{
char buf[BUFSIZ];
int n = bufferevent_read(bufev , buf , sizeof(buf)-1);
if(n > 0){
buf[n] = '\0';
printf("recived from server:%s\n" , buf);
}else{
printf("no data");
}
//bufferevent_write(bufev , buf , strlen(buf)+1);
sleep(1);
}
void write_cb(struct bufferevent* bufev , void*arg)
{
printf("I am client , writing over\n");
}
void event_cb(struct bufferevent *bev, short events, void *ctx) {
if (events & BEV_EVENT_CONNECTED) {
printf("✅ Connected to server.\n");
} else if (events & BEV_EVENT_ERROR) {
perror("❌ Error from bufferevent");
} else if (events & BEV_EVENT_EOF) {
printf("⚠️ Connection closed by server.\n");
}
}
void read_terminal(evutil_socket_t fd , short what , void*arg)
{
struct bufferevent*bufev = (struct bufferevent*)arg;
char buf[1024] = {0};
int len = read(fd , buf , sizeof(buf));
bufferevent_write(bufev , buf , len);
}
int main(int argc , char *argv[])
{
int fd = socket(AF_INET , SOCK_STREAM , 0);
struct sockaddr_in serv_addr;
bzero(&serv_addr , sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SER_PORT);
inet_pton(AF_INET , "10.11.1.211" , &serv_addr.sin_addr);
struct event_base*base = event_base_new();
struct bufferevent*bufev;
bufev = bufferevent_socket_new(base , fd , BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bufev , read_cb , write_cb , event_cb , NULL);
bufferevent_enable(bufev , EV_READ | EV_WRITE);
bufferevent_socket_connect(bufev , (struct sockaddr*)&serv_addr , sizeof(serv_addr));
struct event*ev = event_new(base , STDIN_FILENO , EV_READ | EV_PERSIST ,
read_terminal , bufev);
event_add(ev , NULL);
event_base_dispatch(base);
event_base_free(base);
return 0 ;
}