libevent事件上的处理,定时事件、信号事件

回调函数参数说明

libevent的回调函数的格式就是三个参数
typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
前两个参数不用我们传(目前我学的浅,只传递过第三个参数),第三个参数是一个通用的指针,可以选择传也可以选择不传

事件状态

EV_READ 可读
EV_WRITE 可写
EV_SIGNAL 信号事件
EV_PERSIST 永久性(很多事件默认触发只触发一次,加上这个就可以永久性的触发了)
EV_ET 边沿触发(用于epoll模型)

事件创建函数

struct event *event_new(struct event_base *base, 
evutil_socket_t fd, short what, event_callback_fn cb, void *arg);

参数 what 意思就是上面所讲的事件状态,cb是回调函数,arg想给回调函数传递的参数,如果想传刚创建的event本身,可使用event_self_cbarg()函数
event_callback_fn函数指针的类型

typedef void (*event_callback_fn)(evutil_socket_t sock fd,
				 short what, void *arg);

通用的事件创立的函数,信号事件的话也有再次进行了封装的函数,下面信号事件代码也是展示了两种不同事件创立信号事件。
信号事件对event_new再次封装,默认加了这两种状态 EV_SIGNAL|EV_PERSIST(信号事件和可持久事件)。

#define evsignal_new(b, x, cb, arg)                             \
      event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))

事件创建完成之后,要把事件添加进去
int event_add(struct event *ev, const struct timeval *timeout);参数1、事件对象,参数2、定时参数,返回值0 是成功

创建事件->添加事件->放主循环当中去(event_base_dispatch(base))

事件的详解

1.信号事件

这里主要讲解两个信号,SIGINT(ctrl + c)和SIGTERM(kill、pkill)
如果采用evsignal_new函数来创建一个信号事件,那么该信号事件就是永久性的了,可以看上面evsignal_new宏函数定义。如果使用通用的事件函数创建,想要永久激活,有两种做法,一是 EV_SIGNAL | EV_PERSIST另一种做法是在回调函数再次把事件重新打开

if(!evsignal_pending(ev,NULL)){
		event_del(ev);
		//重新添加
		event_add(ev,NULL);
	}

完整代码如下:

#include <iostream>
#include <event2/listener.h>
#include <arpa/inet.h>
#include <event2/event.h>
#include <sys/types.h>
#include <signal.h>

#include <sys/socket.h>
using namespace std;
//第二个which是属性
static void Ctrl_C(int sock,short which,void *arg){
	cout<<"Ctrl_C"<<endl;
}

static void kill(int sock,short which,void *arg){
        cout<<"kill"<<endl;
	event* ev = (event*)arg;
	//如果处于非待决状态
	if(!evsignal_pending(ev,NULL)){
		event_del(ev);
		//重新添加
		event_add(ev,NULL);
	}
}
int main(int argc,char** argv){
	int port = 9996;
	if(argc > 1){
		port = atoi(argv[1]);
	}
	//忽略管道信号
	if(signal(SIGPIPE,SIG_IGN) == SIG_ERR){
		return 1;
	}
	//创建event的上下文
	event_base* base = event_base_new();
	if(base){
                cout<<"event_base_new successful!"<<endl;
     }
	//添加一个信号ctrl+c,处于no pending状态
	event* event_sig = evsignal_new(base,SIGINT,Ctrl_C,base); 
	if(!event_sig){//失败的话
		cerr<<"event_signal error"<<endl;
		return -1;
	}
	//signal添加到事件,第二个参数是超时时间
	if(event_add(event_sig,0)!=0){//==0是成功
		cerr<<"add failed"<<endl;
		return -1;
	}
	//添加kill信号,非持久只进入一次,event_self_cbarg()传递当前的event
	event* ksig = event_new(base,SIGTERM,EV_SIGNAL,
			kill,event_self_cbarg());
	if(!ksig){//失败的话
                cerr<<"ksignal failed"<<endl;
                return -1;
        }

        //signal添加到事件,第二个参数是超时时间
     if(event_add(ksig,0)!=0){//==0是成功
               cerr<<"ksig failed"<<endl;
               return -1;
     }
	//事件分发处理,事件主循环
	if(base)
       		 event_base_dispatch(base);
	if(base)
		event_base_free(base);
	return 0;	
}

2.定时事件

也是在通用的事件创建函数进行了封装创建函数。 evtimer_new和event_new两种创建。添加事件event_add也是两种,那种都可以,已经测试了。定时事件默认使用二叉堆插入删除都是O(logN),如果超时时间相同的话可以使用如下操作。改为使用双向队列,插入删除O(1)

		
        static timeval tv_in={3,0};
        event* evtime3 = event_new(base,-1,EV_PERSIST,timer3,0);
        const timeval *t3 = event_base_init_common_timeout(
                        base,&tv_in);
        event_add(evtime3,t3);

#include <iostream>
#include <event2/listener.h>
#include <arpa/inet.h>
#include <event2/event.h>
#include <sys/types.h>
#include <signal.h>

#include <sys/socket.h>
using namespace std;
static timeval t1= {1,0};
void timer1(int,short,void *arg){
	auto ev = (event*)arg;
	//只会触发一次,可以重新添加
	if(!evtimer_pending(ev,&t1)){
                event_del(ev);
                event_add(ev,&t1);
        }

	
	cout<<"timer1"<<endl<<flush;
}
void timer2(int,short,void*){
	cout<<"timer2"<<endl;
}

int main(int argc,char** argv){
	int port = 9996;
	if(argc > 1){
		port = atoi(argv[1]);
	}
	//忽略管道信号
	if(signal(SIGPIPE,SIG_IGN) == SIG_ERR){
		return 1;
	}
	//创建event的上下文
	event_base* base = event_base_new();
	if(base){
                cout<<"event_base_new successful!"<<endl;
        }

	//定时器处理
	event* evtime = evtimer_new(base,timer1
			,event_self_cbarg());
	if(!evtime){
		cout<<"evtimer failed "<<endl;
		return -1;
	}
	event_add(evtime,&t1);
	
	event* evtime2 = event_new(base,-1,EV_PERSIST,timer2,0);
	evtimer_add(evtime2,&t1);


	if(base)
       		 event_base_dispatch(base);

	if(base)
		event_base_free(base);
	if(evtime)
		event_free(evtime);
	if(evtime2)
		event_free(evtime2);
	return 0;	
}

监控文件事件

还是一样的事件,只不过课程视频监测的是root登录日志,而我再普通用户下没有办法监测登录日志,天真的我以为随便监听个事件然后手打内容就ok了,可惜还是太年轻了。那样并不能真的监听到文件,我又猜想我的代码是否有问题?拿到root权限下试一下监测登录日志,发现ok可以监测到。我又开始猜想是不是要系统把内容拷贝到文件而不是手打进去的?有了这个想法我又开始写了一个read函数,发现OK,可以监测到了。
监测文件代码如下:

#include <iostream>
#include <event2/listener.h>
#include <arpa/inet.h>
#include <event2/event.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <thread>

using namespace std;

void read_file(int fd,short,void* ){
	char buf[1024] = { 0 };
	int len = read(fd,buf,sizeof(buf));
	if(len > 0){
		cout<<buf<<endl;
	}
	else{
	//	cout<<"."<<endl;
		this_thread::sleep_for(2500ms);
	}
}
int main(int argc,char** argv){
	int port = 9996;
	if(argc > 1){
		port = atoi(argv[1]);
	}
	//忽略管道信号
	if(signal(SIGPIPE,SIG_IGN) == SIG_ERR){
		return 1;
	}
	event_config* conf = event_config_new();

	//设置支持文件描述符,这样才可以监听文件
	event_config_require_features(conf,
	EV_FEATURE_FDS);
	
	//根据特征来生成base
	event_base* base = event_base_new_with_config(conf);
	if(!base){
		cerr<<"base failed"<<endl;
		return -1;
	}
	
	int fd = open("listened.log",O_RDONLY | O_NONBLOCK);
	if(fd <= 0){
		cerr<<"open failed"<<endl;
		return -1;
	}

	//文件指针移到结尾处
//	lseek(fd,0,SEEK_END);

	event* fv = event_new(base,fd,EV_READ | EV_PERSIST
			,read_file,0);
	event_add(fv,NULL);

	 //要进入主循环
	 if(base)
                 event_base_dispatch(base);





	if(conf){
		event_config_free(conf);
	}
	if(base){
                cout<<"event_base_new successful!"<<endl;
        }


	if(base)
		event_base_free(base);
	return 0;	
}

read函数文件

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <thread>
#include <iostream>
#include <string.h>
using namespace std;
int main(){
	int fd = open("listened.log",O_WRONLY,0);
	if(fd <= 0){
		cout<<"open failed"<<endl;
		return -1;
	}
	lseek(fd,0,SEEK_END);
	const char* buff = "dxgzg";
	for(int i = 0;i < 5;++i){
		int flag = write(fd,buff,strlen(buff));
		this_thread::sleep_for(2000ms);
	}
	return 0;
}

终于可以监测到文件了
在这里插入图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值