在正式进入libevent源码时,我们先来用一下它。会用然后再问为什么。
libevent提供了一个框架,我们只需要往里面添加细节就可以了。机械人士,就拿汽车来做比喻吧,我就往里面装好发动机就行了:
- 拿到差发动机的框架(创建框架)
- 拿出发动机(创建事件)
- 将发动机装入(添加事件)
- 造汽车(事件循环)
我们就根据这个逻辑来使用一下libevent。
一、框架——event_base()
第一步就是框架的创建。
struct event_base* event_base_new(void);
有借有还,再借不难,用了人家的用完了记得还回去。
event_base_free(struct event_base* base);
二、事件创建——event_new()
创建我们要做的事情。
struct event* event_new(
struct event_base* base,//框架
evutil_socket_t fd,//文件描述符
short what,//事情
event_callback_fn cb,//回调函数
void* arg//参数
);
typedef void (*event_callback_fn)(evutil_socket_t,short,void*);//对应上面的成员
// short what
#define EV_TIMEOUT 0x01 // 已淘汰(忽略)
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08 //libevent封装了信号相关的操作 SIGNAL
#define EV_PERSIST 0x10 // 持续触发
#define EV_ET 0x20 // 边沿模式
用完记得还
void event_free(struct event *event);
事件添加——event_add()
创建了事件,你还要将他注册,也就是添加到框架上去。
int event_add(struct event* ev,const struct timeval* tv);//tv为NULL,事件被触发,回调调用,tv为{0,n},时间内触发则调用,否则等时间后调用。 返回0成功,返回-1失败。
事件循环——event_base_dispatch()
最后添加事件循环就可以了。
int event_base_dispatch(struct event_base* base);
这是libevent的基础使用框架,能完成简单的功能。下面继续介绍一下一些重要的函数。
数据缓冲
在读写操作时,libevent提供一个IO通信,bufferevent,是一个实现的端口,有两部分,读缓冲和写缓冲。使用流程:
创建:
bufferevent_socket_new(),由函数就可以看出是一个类似套接字的东西。
struct bufferevent * bufferevent_socket_new(
struct event_base *base,
evutil_socket_t fd,
enum bufferevent_options options
);
函数原型中可以看参数:首先这也是一个event,结构体内有框架,有描述符,option则是相关的操作。
同上面一样,记得释放。
void bufferevent_free(struct bufferevent *buff);
回调函数
bufferevent_setcb(),设置相应的回调函数。
void bufferevent_setcb(
struct bufferevent *bufev,
bufferevent_data_cb readcb,//使用 bufferevent_read()读取buff中数据信息
bufferevent_data_cb writecb,//写回调只是提示你发生出去数据,没有实质作用
bufferevent_event_cb eventcb,
void *cbarg
);
输入输出
int bufferevent_write(
struct bufferevent *bufev,
const void *data,
size_t size
);
size_t bufferevent_read(
struct bufferevent *bufev,
void *data,
size_t size
);
连接
客户端自然要有connect操作。
int bufferevent_socket_connect(
struct bufferevent *bev,
struct sockaddr *address,
int addrlen
);
bev为前面的结构体,后面则是对应地址和地址长度。
服务端则要进行bind和listen操作。
struct evconnlistener *evconnlistener_new_bind(
struct event_base *base,
evconnlistener_cb cb,
void *ptr,
unsigned flags,
int backlog,
const struct sockaddr *sa,
int socklen
);
里面的参数很容易懂,之哟有一个listen回调函数,定义如下。
typedef void (*evconnlistener_cb)(
struct evconnlistener *listener,
evutil_socket_t sock,
struct sockaddr *addr,
int len,
void *ptr
);
之后free
void evconnlistener_free(struct evconnlistener *lev);
例子说话
说了只是一些基础的框架,具体的细节还是要用到是慢慢查,写一个例子就能顺起来了。
我们完成一个客户端和服务端的通讯操作。来看下libevent的作用。
//server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.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};//init a buf
bufferevent_read(bev,buf,sizeof(buf));//buffer read
printf("recv from client:%s\n",buf);
}
void write_cb(struct bufferevent* bev,void *arg)
{
printf("server send!");
}
void event_cb(struct bufferevent* bev,short events,void* arg)
{
if (events & BEV_EVENT_EOF)
{
printf("connection closed\n");
}
else if(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
bufferevent_free(bev);
printf("buffevent is free!\n");
}
void send_cb(evutil_socket_t fd,short what,void* arg);
void cb_listener(struct evconnlistener *listener,
evutil_socket_t fd,
struct sockaddr *addr,
int len, void *ptr){
printf("new client connect!\n");
struct event_base *base=(struct event_base*)ptr;
struct bufferevent* bev;
bev=bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE);
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,
send_cb, bev);
event_add(ev,NULL);
}
void send_cb(evutil_socket_t fd,short what,void* arg)
{
char buf[1024]={0};
struct bufferevent* bev=(struct bufferevent*)arg;
printf("input:");
read(fd,buf,sizeof(buf));
bufferevent_write(bev,buf,strlen(buf)+1);
}
//调用框架就可以了
int main(int argc,const char* argv[])
{
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(9876);
ser.sin_addr.s_addr=htonl(INADDR_ANY);
struct event_base *base;
base=eventbase_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;
}
//client.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
void read_cb(struct bufferevent *bev, void *arg)
{
char buf[1024] = {0};
bufferevent_read(bev, buf, sizeof(buf));
printf("recv from server: %s\n", buf);
}
void write_cb(struct bufferevent *bev, void *arg)
{
printf("client send!\n");
}
void event_cb(struct bufferevent *bev, short events, void *arg)
{
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("connect ok!\n");
return;
}
bufferevent_free(bev);
printf("bufferevent free!\n");
}
void send_cb(evutil_socket_t fd, short what, void *arg)
{
char buf[1024] = {0};
struct bufferevent* bev = (struct bufferevent*)arg;
printf("input: \n");
read(fd, buf, sizeof(buf));
bufferevent_write(bev, buf, strlen(buf)+1);
}
int main(int argc, const char* argv[])
{
struct event_base* base;
base = event_base_new();
struct bufferevent* bev;
bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
// 连接服务器
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(9876);
evutil_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 | EV_PERSIST);
// 创建一个事件
struct event* ev = event_new(base, STDIN_FILENO,
EV_READ | EV_PERSIST,
send_cb, bev);
event_add(ev, NULL);
event_base_dispatch(base);
event_base_free(base);
return 0;
}
也算是个很大的工程了,顺便回忆了socket的一些操作。可以看到,使用libevent时,只需要自己设置好自己想要的操作,然后套用框架就可以了。
之后,继续分析libevent的源码,看看这些框架为什么可以实现这些操纵。
文中很多参数没有介绍,可以在使用时自行查阅libevent相关参数就可以了。