我是靠谱客的博主 幽默铃铛,最近开发中收集的这篇文章主要介绍libevent笔记-事件/事件循环,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

//libevent笔记-事件/事件循环
//转载请注明出处: yuliying的csdn博客.

//===事件循环控制主要是开始事件循环和终止事件循环===
//1.使用默认配置开始事件循环: event_base_dispatch()
//2.开始事件循环,可以设置一些参数: event_base_loop()
//3.指定时间后退出事件循环,处理完回调函数后退出 : event_base_loopexit()
//4.立即退出事件循环: event_base_loopbreak()

//===libevent的基本操作单元是事件(event),每个(event)都是下列情况发生的集合===
//1.文件描述符【已经】可读/可写.
//2.文件描述符【变为】可读/可写.(边缘触发模式下)
//3.超时事件时间已到
//4.进程收到信号
//5.用户自己触发事件

//===事件有几个状态===
//1.已初始化状态(initialized). 设置好一个事件,并且将其关联到一个event_base.
//2.等待状态(pending). 将上述已初始化状态的事件添加到event_base,事件变为待触发状态.
//3.激活状态(active). 待触发事件的触发条件满足,事件变为触发状态.

//===初始化事件(event_new)时,libevent可关注的事件有下列几种,可以是下列事件的组合===
//1.EV_TIMEOUT : 超时事件.当在添加事件到循环(event_add)的时候设置一个超时时间.这个标记超时的时候在回调函数中的事件集合中被标记.
//2.EV_READ : 文件描述符可读事件
//3.EV_WRITE: 文件描述符可写事件
//4.EV_SIGNAL: 进程信号事件
//5.EV_PERSIST : 将当前事件标记为永久事件,非永久事件只能激活一次,回调函数执行后变为已始化状态.永久事件激活处理后后变为等待状态.
//6.EV_ET : 该文件描述符事件使用边缘触发方式.

//===文件描述符事件(通用事件)操作函数===
//1.初始化事件: event_new()
//2.添加事件到事件循环 : event_add()
//3.从事件循环删除事件,不再监听:event_del()
//4.销毁事件: event_free()
//5.判断事件是否处于pending或者active状态,返回关注的事件或者激活的事件. event_pending()
//6.设置事件的优先级: event_priority_set()
//7.获取事件对应的文件描述符: event_get_fd()
//8.从事件获取event_base : event_get_base()
//9.获取关注的事件: event_get_events()
//10.获取事件的回调函数: event_get_callback()
//11.获取事件回调函数的参数: event_get_callback_arg()
//12.获取事件的优先级: event_get_priority()
//13.手动激活事件: event_active()

//===单纯的定时器事件操作函数(通用函数的包装的宏,方便使用)===
//1.初始化事件: evtimer_new()
//2.添加事件到事件循环 : evtimer_add()
//3.从事件循环删除事件,不再监听: evtimer_del()
//4.销毁事件: evtimer_del()
//5.判断事件是否处于pending或者active状态,返回关注的事件或者激活的事件. evtimer_pending()

//===单纯的信号事件操作函数(通用函数的包装的宏,方便使用)===
//1.初始化事件: evsignal_new()
//2.添加事件到事件循环 : evsignal_add()
//3.从事件循环删除事件,不再监听: evsignal_del()
//4.销毁事件: event_free()
//5.判断事件是否处于pending或者active状态,返回关注的事件或者激活的事件. evsignal_pending()


//下面代码分别演示:文件描述符事件/超时事件/信号事件.

#include <event2/event.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <signal.h>

//保存所有fd对应的event结构体指针.方便销毁事件.
//一些事件的销毁这里就不做处理了.实际程序中注意内存泄露.当事件已经无用后记得销毁掉.
struct event * fdevent_array[10000];

void setnonblock(int fd)
{
    int flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

void reuseAddr(int fd) {
    int yes = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
}

void read_cb(evutil_socket_t fd, short events, void *arg)
{
	struct event * myevent;
	
	if ( events & EV_READ ){
		//真正的应用中这里需要两个用户的buffer来保存该连接上的数据.
		char buffer[1024];
		while(1){
			memset( buffer , 0 , 1024);
			int nbytes = read( fd , buffer , 1023);
			if ( nbytes < 0){
				if (errno == EINTR) continue;
				if (errno == EAGAIN) break;
				printf("client error , fd [%d]n" , fd);
				goto fdclose;
				break;
			}
			if ( nbytes == 0){
				printf("client close , fd [%d]n" , fd);
				goto fdclose;
				break;
			}
			printf("data from client: %s" , buffer);
			write( fd , buffer , nbytes);
		}
	}
	return;
	
fdclose:
	//
	myevent = fdevent_array[fd];
	event_del(myevent);
	event_free(myevent);
	close(fd);
	fdevent_array[fd] = NULL;
}

void listen_cb(evutil_socket_t listener, short events , void *arg)
{
	if (events & EV_TIMEOUT ){
		printf("timer eventn");
	}
	if (events & EV_READ ){
		struct event_base *base = arg;
		struct sockaddr_storage ss;
		socklen_t slen = sizeof(ss);
		int fd = accept(listener, (struct sockaddr*)&ss, &slen);
		//接受了一个新客户连接,我们为这个新连接设置一个事件
		if(fd > 0){
			printf("accept new client fd [%d]n" , fd);
			setnonblock( fd );
			//我们现在只关注读事件,将客户端发来的信息回送给客户端.
			struct event * client_event = event_new( base , fd , EV_READ|EV_PERSIST , read_cb , (void*)base  );
			event_add( client_event , NULL);
			fdevent_array[fd] = client_event;
		}
	}
}

void sigint_cb(evutil_socket_t sig, short events , void *arg){
	struct event_base *base = arg;
	if ( events & EV_SIGNAL ){
		printf("capture signal [%d]n" , sig);
		event_base_loopbreak(base);
	}
}

int main(){
	//
	struct event_base * base = event_base_new();
	
	//以下代码开启server监听一个本地端口.可以将ip改为自己的服务器以方便测试.
	int port = 9999;
	struct sockaddr_in my_addr;
	memset( &my_addr , 0 , sizeof(struct sockaddr_in));
	my_addr.sin_family=AF_INET;
	my_addr.sin_port=htons(port);
	my_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
	int listen_socket = socket(AF_INET , SOCK_STREAM , 0);
	setnonblock(listen_socket);
	reuseAddr(listen_socket);
	bind(listen_socket , (struct sockaddr*)&my_addr , sizeof(struct sockaddr));
	listen(listen_socket , 10);
	
	//给监听的socket添加一个事件,用来accept新连接,关注读事件,并将该event设置为永久事件.设置好回调函数和参数.
	struct event *listener_event = event_new( base , listen_socket , EV_READ|EV_PERSIST , listen_cb , (void*)base);
	//将监听事件加入事件循环.同时我们在监听事件上加一个定时器事件.在回调函数中判断应该接受连接还是定时器触发.
	struct timeval expire = { 5 , 0 };
	event_add( listener_event , &expire);
	fdevent_array[listen_socket] = listener_event;
	
	//设置一个信号事件,我们监听(ctrl+c的中断信号),收到信号后打印一条消息,退出事件循环.
	struct event *signal_event = evsignal_new( base , SIGINT , sigint_cb , (void*)base);
	evsignal_add( signal_event , NULL );
	
	//开始事件循环
	event_base_dispatch( base );
	
	//
	event_base_free(base);
}

最后

以上就是幽默铃铛为你收集整理的libevent笔记-事件/事件循环的全部内容,希望文章能够帮你解决libevent笔记-事件/事件循环所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部