一,event事件的学习
//1,创建根
struct event_base *event_base_new(void);
//2,创建节点
struct event event_new(struct event_base ,
evutil_socket_t,
short,
event_callback_fn,
void *);
//3,添加节点
int event_add(struct event *ev,
const struct timeval *timeout);
//4,添加循环等待client处理
int event_base_dispatch(struct event_base *);
//5,删除节点
int event_del(struct event *);
//6,释放节点
void event_free(struct event *);
#include <event.h>
#include <string.h>
#include <unistd.h>
struct event *readEv = NULL;
void readcb(evutil_socket_t fd, short events, void *arg)
{
char buf[256] = {0};
int ret = recv(fd,buf,sizeof(buf),0);
if(ret > 0){
send(fd,buf,ret,0);
}
else if(ret == 0){
//客户端关闭
printf("client closed\n");
close(fd);
event_del(readEv);
event_free(readEv);
readEv = NULL;
}
else {
perror("recv err");
close(fd);
event_del(readEv);
event_free(readEv);
readEv = NULL;
}
}
void conncb(evutil_socket_t fd, short events, void *arg)
{
struct event_base *base = arg;
struct sockaddr_in client;
socklen_t len = sizeof(client);
int cfd = accept(fd,(struct sockaddr*)&client,&len);
if(cfd > 0){
//得到新连接,上树
readEv = event_new(base,cfd,EV_READ|EV_PERSIST,readcb,NULL);
event_add(readEv,NULL);
}
}
int main()
{
//1. 创建socket
int lfd = socket(AF_INET,SOCK_STREAM,0);
//2. 绑定bind
struct sockaddr_in serv;
bzero(&serv,sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(8888);
serv.sin_addr.s_addr = htonl(INADDR_ANY);
int opt = 1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置端口复用
if(bind(lfd,(struct sockaddr*)&serv,sizeof(serv)) < 0){
perror("bind err");
return -1;
}
//3. 监听listen
listen(lfd,128);
//libevent 创建根节点
struct event_base *base = event_base_new();
//创建event事件,设置回调函数,事件类型,文件描述符
//struct event *event_new(struct event_base *, evutil_socket_t, short, event_callback_fn, void *);
struct event *connEv = event_new(base,lfd,EV_READ|EV_PERSIST,conncb,base);
//设置监听event_add
// event_add(struct event *ev, const struct timeval *timeout);
event_add(connEv,NULL);//开始监听新连接事件
//事件循环 event_base_dispatch
event_base_dispatch(base);
//各种释放
event_base_free(base);//释放根
event_free(connEv);
if(readEv ){
event_free(readEv);
}
close(lfd);
return 0;
}
二,bufferevent
1, server初始化
struct bufferevent *bufferevent_socket_new(struct event_base *base,
evutil_socket_t fd,
int options);
bufferevent_socket_new 对已经存在socket创建bufferevent事件,
可用于后面讲到的链接监听器的回调函数中,参数说明
//base – 对应根节点
//fd – 文件描述符
//第三个参数: BEV_OPT_CLOSE_ON_FREE = (1<<0), –释放bufferevent自动关闭底层接口
BEV_OPT_THREADSAFE = (1<<1), – 使bufferevent能够在多线程下是安全的
BEV_OPT_DEFER_CALLBACKS = (1<<2),
BEV_OPT_UNLOCK_CALLBACKS = (1<<3)
2, client 链接server函数
int bufferevent_socket_connect(struct bufferevent *bev,
struct sockaddr *serv ,
int socklen );
参数:
bev – 需要提前初始化的bufferevent事件
serv – 对端的ip地址,端口,协议的结构指针
socklen – 描述serv的长度
3, 释放bufferevent
void bufferevent_free(struct bufferevent *bufev);
4,bufferevent_setcb用于设置bufferevent的回调函数,
readcb,writecb,eventcb分别对应了读回调,
写回调,事件回调,cbarg代表回调函数的参数。
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb, bufferevent_data_cb writecb,
bufferevent_event_cb eventcb, void *cbarg);
5,read和write的回调函数
typedef void (*bufferevent_data_cb)(struct bufferevent *bev,
void *ctx);
6, 事件的回调函数的处理
typedef void (*bufferevent_event_cb)(struct bufferevent *bev,
short what,
void *ctx);
What 代表 对应的事件BEV_EVENT_EOF,
BEV_EVENT_ERROR,
BEV_EVENT_TIMEOUT,
BEV_EVENT_CONNECTED
7,写信息函数
①int bufferevent_write(struct bufferevent *bufev,
const void *data, size_t size);
bufferevent_write是将data的数据写到bufferevent的写缓冲区
②int bufferevent_write_buffer(struct bufferevent *bufev,
struct evbuffer *buf);
bufferevent_write_buffer 是将数据写到写缓冲区另外一个写法,
实际上bufferevent的内部的两个缓冲区结构就是struct evbuffer。
9, 读取数据的函数
①size_t bufferevent_read(struct bufferevent *bufev,
void *data,
size_t size);
bufferevent_read 是将bufferevent的读缓冲区数据读到data中,
同时将读到的数据从bufferevent的读缓冲清除。
②int bufferevent_read_buffer(struct bufferevent *bufev,
struct evbuffer *buf);
bufferevent_read_buffer 将bufferevent读缓冲数据读到buf中,接口的另外一种。
10, 设置事件是否生效
①int bufferevent_enable(struct bufferevent *bufev,
short event);
②int bufferevent_disable(struct bufferevent *bufev,
short event);
bufferevent_enable与bufferevent_disable是设置事件是否生效,
如果设置为disable,事件回调将不会被触发。
三, 监听listenevent 事件
1, 链接监听器封装了底层的socket通信相关函数,
比如socket,bind,listen,accept这几个函数。
链接监听器创建后实际上相当于调用了socket,bind,listen,
此时等待新的客户端连接到来, 如果有新的客户端连接,
那么内部先进行accept处理,然后调用用户指定的回
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:值的参考
#define LEV_OPT_LEAVE_SOCKETS_BLOCKING (1u<<0) 文件描述符为阻塞的
#define LEV_OPT_CLOSE_ON_FREE (1u<<1) 关闭时自动释放
#define LEV_OPT_CLOSE_ON_EXEC (1u<<2)
#define LEV_OPT_REUSEABLE (1u<<3) 端口复用
#define LEV_OPT_THREADSAFE (1u<<4) 分配锁,线程安
2, 函数与前一个函数不同的地方在与后2个参数,使用本函数时,认为socket已经初始化好,
并且bind完成,甚至也可以做完listen,所以大多数时候,我们都可以使用第一个函数。
struct evconnlistener *evconnlistener_new(struct event_base *base,
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
evutil_socket_t fd);
3, 回调函数
typedef void (evconnlistener_cb)(struct evconnlistener ,
evutil_socket_t fd,
struct sockaddr * addr,
int socklen,
void * arg);
主要回调函数fd参数会与客户端通信的描述符,并非是等待连接的监听的那个描述符,
所以cliaddr对应的也是新连接的对端地址信息,已经是accept处理好的。
4, 使链接监听器生效
int evconnlistener_enable(struct evconnlistener *lev);
5, 使链接监听器失效
int evconnlistener_disable(struct evconnlistener *lev);
6, 释放链接监听器
void evconnlistener_free(struct evconnlistener *lev);
实现代码
//基于bufferevent的回射服务器
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#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_readcb(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;//定义根节点
struct evconnlistener *listener;//定义连接监听器
struct event *signal_event;//定义一个事件 信号
struct sockaddr_in sin;
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_cb
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;
}
//定义了 SIGINT信号事件,设置回调位signal_cb
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;
}
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;//定义bufferevent
//创建了一个bufferevent
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);
return;
}
//设置回调 -- 读回调为空,写回调 conn_writecb , 事件回调 -conn_eventcb
bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, NULL);
//使 写生效
bufferevent_enable(bev, EV_WRITE | EV_READ);
//使读 失效
//bufferevent_disable(bev, EV_READ);
//将MESSAGE 写到bufferevent的写缓冲区中
//bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}
static void
conn_readcb(struct bufferevent *bev, void *user_data)
{
//读数据 要从 bufferevent缓冲区读 数据
char buf[256] = {0};
//读数据
int ret = bufferevent_read(bev,buf,sizeof(buf));
if(ret > 0){
bufferevent_write(bev,buf,ret);//写到写缓冲区
}
}
static void
conn_writecb(struct bufferevent *bev, void *user_data)
{
// struct evbuffer 是bufferevent 的缓冲区类型
struct evbuffer *output = bufferevent_get_output(bev);
if (evbuffer_get_length(output) == 0) {
// 如果 bufferevent的写缓冲区内容长度为0,代表已经将写缓冲区内容刷新到底层缓冲区
printf("flushed answer\n");
// bufferevent_free(bev);//释放bufferevent 关闭底层接口
}
}
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 };//设置延迟2s
printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
//如果有事件,处理完当前事件退出
event_base_loopexit(base, &delay);
}