官方示例
// libev需要的头文件
#include <ev.h>
#include <stdio.h>
// 建立需要监听的事件,这些事件类型是libev提供的
//ev_io为监听控制台输入,ev_timer为时间事件
ev_io stdin_watcher;
ev_timer timeout_watcher;
// 以下为自定义的回调函数,当触发监听事件时,调用执行对应的函数
// ev_io事件的回调函数,当有输入流stdin时,调用函数
static void stdin_cb (EV_P_ ev_io *w, int revents)
{
puts ("stdin ready");
//对ev_io事件的监控不会自动停止,需要手动在需要的时候停止
ev_io_stop (EV_A_ w);
//整体的loop事件在所有监控停止时停止,也可以手动关闭全部的ev_run
ev_break (EV_A_ EVBREAK_ALL);
}
// 时间事件的自定义回调函数,可定时触发
static void timeout_cb (EV_P_ ev_timer *w, intrevents)
{
puts ("timeout");
//关闭还在运行的ev_run
ev_break (EV_A_ EVBREAK_ONE);
}
int main (void)
{
//定义默认的 event loop,它就像一个大容器,可以装载着很多事件不停运行
struct ev_loop *loop = EV_DEFAULT;
// 初始化ev_io事件监控,设置它的回调函数,,和stdin
ev_io_init (&stdin_watcher, stdin_cb,0, EV_READ);
//将ev_io事件放到event loop里面运行
ev_io_start (loop, &stdin_watcher);
// 初始化ev_timer事件监控,设置它的回调函数,间隔时间,是否重复
ev_timer_init (&timeout_watcher, timeout_cb, 5.5,0.);
//将ev_timer事件放到event loop里面运行
ev_timer_start (loop, &timeout_watcher);
// 大容器event loop整体运行起来
ev_run (loop, 0);
// ev_run运行结束之后,才会运行到这里
return 0;
}
监听了 控制台输入事件和定时器事件
如果5.5s内没输入,打印timeout
控制台输入了,打印stdin ready
使用的思路:
-
创建
ev_xxx
监控器类型 - ev_xxx_init注册该监控器感兴趣的事件,设置它的回调函数和其他参数
- ev_xxx_start把该监控器放入事件驱动容器
- ev_run事件驱动容器运行
向libev注册感兴趣的events,比如Socket事件,libev会对所注册的事件进行管理,并在事件发生时触发相应的程序
libev实现简单服务端
监听到客服端连接事件 会初始化通信socket,打印someone connected,然后注册数据接收事件
监听到数据接收事件,会转发该数据给客服端
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>
#include <netinet/in.h>
#include <strings.h>
#include <ev.h>
#define PORT 8333
#define BUFFER_SIZE 1024
//与客服端数据交换
void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
char buffer[BUFFER_SIZE];
ssize_t read;
if(EV_ERROR & revents)
{
printf("error event in read");
return;
}
read = recv(watcher->fd, buffer, BUFFER_SIZE, 0);
if(read < 0)
{
printf("read error,errno:%d\n", errno);
return;
}
else if(0 == read)
{
printf("someone disconnected.errno:%d\n", errno);
ev_io_stop(loop,watcher);//停止
free(watcher);
return;
}
else
{
printf("get the message:%s\n", buffer);
}
send(watcher->fd, buffer, read, 0);
bzero(buffer, read);
}
//初始化通信socket,成功就打印"someone connected"
void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sd;
struct ev_io *w_client = (struct ev_io*) malloc (sizeof(struct ev_io));
if(EV_ERROR & revents)
{
printf("error event in accept\n");
return;
}
client_sd = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len);
if (client_sd < 0)
{
printf("accept error\n");
return;
}
printf("someone connected \n");
ev_io_init(w_client, read_cb, client_sd, EV_READ);//socket事件监控器注册了数据读取事件
ev_io_start(loop, w_client);
}
int main()
{
int sd = -1;
struct sockaddr_in addr;
if((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
printf("socket error. errno:%d\n", errno);
return -1;
}
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);//#define PORT 8333
addr.sin_addr.s_addr = INADDR_ANY;
if(bind(sd, (struct sockaddr*) &addr, sizeof(addr)) != 0)
{
printf("bind error.errno:%d\n",errno);
return -1;
}
if(listen(sd, 5) < 0)
{
printf("listen error\n");
return -1;
}
//上面是监听socket初始化
struct ev_loop *loop = ev_default_loop(0);//初始化事件驱动池
struct ev_io socket_watcher;//socket事件监控器
ev_io_init(&socket_watcher, accept_cb, sd, EV_READ);//EV_READ是监听的事件,连接事件发生,回调函数accept_cb处理
ev_io_start(loop, &socket_watcher);//放入事件驱动池
while (1)
{
printf("ev_loop working\n") ;
ev_loop(loop, 0);//一次ev_loop执行一轮事件处理 , while (1)实现事件循环处理
}
return 0;
}
服务端接收了数据并转发给客户端
客户端接收的
使用原理
libev通过各种事件监听器watcher对各种类型的事件进行监听
watcher是libev定义的结构体
事件是libev定义的宏
ev_loop的创建,运行,停止
//默认使用EV_DEFAULT类型的loop
struct ev_loop *loop = EV_DEFAULT;
返回一个最基础的ev_loop,并自动完成它的初始化
如果程序中已经执行过该创建,将直接返回之前的创建
也可以用ev_default_loop(EVBACKEND_POLL | EVBACKEND_SELECT | EVFLAG_NOENV);
struct ev_loop *loop = ev_default_loop(0);
ev_run(loop, int flags);
flags的作用,用于设置ev_loop的运行方式
通常设置为0,表示该ev_loop在所有watcher结束后停止,也可以手动break
ev_break (loop,how)
how代表停止的方式:
EVBREAK_ONE:停止最久远的那个ev_run
EVBREAK_ALL:停止所有的ev_run
watcher结构体和共有的标准化函数
用面向对象的思想去理解,以下是"基类"
typedef struct ev_watcher
{
int active; //是否注册到容器中
int pending; //已经触发事件但是没有处理
int priority;
void *data;
void (*cb)(struct ev_loop *loop, struct ev_watcher *w, int revents);
} ev_watcher;
举例:"派生类"ev_io
typedef struct ev_io
{
int active;
int pending;
int priority;
void *data;
void (*cb)(struct ev_loop *loop, struct ev_io *w, int revents);
struct ev_watcher_list *next;//装监控器的List,与基类配套
int fd; //这里的fd,events就是派生类的私有成员,分别表示监听的文件fd和触发的事件(可读还是可写)
int events;
} ev_io;
为了对成员变量使用的语句进行简化,就又写了一个"ev_wrap.c" 也可以这样
typedef structev_io
{
EV_WATCHER_LIST (ev_io)
int fd;
int events;
} ev_io;
//共有的标准化函数
typedef void (*)(struct ev_loop *loop, ev_TYPE*watcher, int revents) callback; // 回调函数都是这种类型
ev_init (ev_TYPE *watcher, callback); // 初始化与watcher具体类型无关的部分
ev_TYPE_set (ev_TYPE *watcher, [args]); // 初始化watcher的与类型相关的部分
ev_TYPE_init (ev_TYPE *watcher, callback, [args]); //实现上面两个
ev_TYPE_start (loop, ev_TYPE *watcher); // 在ev_loop中注册watcher
ev_TYPE_stop (loop, ev_TYPE *watcher); // 在ev_loop中注销watcher
ev_set_priority (ev_TYPE *watcher, int priority); // 设置watcher优先级,值域为[-2,2],大的优先
ev_feed_event (loop, ev_TYPE *watcher, int revents);// 跨线程通知,相当于触发了某个事件。
bool ev_is_active (ev_TYPE *watcher); // watcher是否active.
bool ev_is_pending (ev_TYPE *watcher); // watcher是否pending.
int ev_clear_pending (loop, ev_TYPE *watcher); // 清除watcher pending状态并且返回事件