Linux:Libevent简单使用

文章目录

      • 一、什么是Libevent
      • 二、Libevent如何使用
      • 三、Libevent简单使用实例
      • 四、Libevent简单实现TCP服务器

一、什么是Libevent

Libevent: 是开源社区的一款高性能的I/O框架库。 其学习和使用者众多,例如:高性能的分布式内存对象缓存软件memcached,Google浏览器Chromium的Linux版本。

Libevent使用特点:
1.跨平台支持: Libevent支持Linux,UINX和Windows。
2.统一事件源: Libevent对I/O事件、信号和定时事件提供统一的处理。
3.线程安全: Libevent使用Libevent_pthreads库来提供线程安全支持。
4.基于Reactor模式实现: 该模式主线程只负责监听文件描述符上是否有事件发生,有的话就立即将该事件通知工作线程,除此之外,主线程不做任何其他实质性工作。读写数据,接受新的连接,以及处理客户请求均在工作线程中完成。
 

二、Libevent如何使用

使用模型:
Linux:Libevent简单使用_第1张图片

大致使用流程:
1.应用程序进行event事件处理器的定义。
2.将event事件处理器注册到Libevent中。
3.Libevent进行循环监听事件,利用I/O复用方法检测就绪事件。
4.得到就绪事件,在就绪队列中由应用程序进程回调函数调用。

Linux:Libevent简单使用_第2张图片
Libevent支持的事件类型:
Linux:Libevent简单使用_第3张图片
 

三、Libevent简单使用实例

下面我们利用定时事件与信号事件来看一看Libevent的简单实用(更多详情请参考:Linux高性能服务器编程)。

几个函数的作用:
1.event_init()函数用于创建event_base对象,一个event_base对象相当于一个Reactor实例。
2.evsignal_new()创建信号事件处理器,evtimer_new创建定时事件处理器。
3.event_add()函数将事件处理器添加到注册事件队列中,并将该事件处理器对应的事件添加到事件多路分发器中。
4.event_base_dispatch()函数执行事件循环。
5.事件循环结束后,event_free()释放系统资源。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

//信号事件
void signal_cb(int fd, short event, void *arg)
{
	printf("SIGINT was call\n");
}

//定时事件
void time_cb(int fd, short event, void *arg)
{
	printf("time out\n");
}

int main()
{
	struct event_base *base = event_init(); //base为Libevent的一个实例,对base进行初始化
	
	//创建信号事件
	struct event *signal_event = evsignal_new(base, SIGINT, signal_cb, NULL);
	event_add(signal_event, NULL); //将事件注册到Libevent中

	//创建定时事件
	struct timeval tm = {5, 0}; //定时时间
	struct event *time_event = evtimer_new(base, time_cb, NULL);
	event_add(time_event, &tm); //将事件注册到Libevent中

	//执行事件循环,检测就绪事件
	event_base_dispatch(base);

	//事件循环结束,释放资源
	event_free(signal_event);
	event_free(time_event);
	event_base_free(base);

	exit(0);
}

测试结果(以下代码均为Linux下测试):使用Libevent时要链入libevent库:
在这里插入图片描述
1.定时事件被触发: 若5s内不做任何处理,定时事件便会被触发
在这里插入图片描述
2.信号事件被触发: 触发会输出SIGINT was call,SIGINT事件为程序终止(interrupt)信号, 在用户键入Ctrl+C时发出。
在这里插入图片描述
 

四、Libevent简单实现TCP服务器

      使用Libevent库实现TCP服务器时,将监听的socket与连接时的socket分别生成一个Libevent(指定其对应的回调函数),并将其添加到Libevent的另一个base中,执行事件循环检测其发生。

客户端代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAX_CLIENT 100 //最多客户端
#define DATALENGTH 1024 //缓冲区大小

//定义一个全局事件处理器
struct event_base *base = NULL;

typedef struct ClientData
{
	int fd;
	struct event *listen_event;
}ClientData;

void InitClients(ClientData clients[])
{
	int i = 0;
	for (; i < MAX_CLIENT; ++i)
	{
		clients[i].fd = -1;
		clients[i].listen_event = NULL;
	}
}

void InsertToClients(ClientData clients[], int fd, struct event *listen_event)
{
	int i = 0;
	for (; i < MAX_CLIENT; ++i)
	{
		if (clients[i].fd == -1)
		{
			clients[i].fd = fd;
			clients[i].listen_event = listen_event;
			break;
		}
	}
}

struct event *DeleteOfClients(ClientData clients[], int fd)
{
	int i = 0;
	for (; i < MAX_CLIENT; ++i)
	{
		if (clients[i].fd == fd)
		{
			clients[i].fd = -1;
			return clients[i].listen_event;
		}
	}
	return NULL;
}

//创建套接字
int CreateSocket()
{
	int listenfd = socket(AF_INET, SOCK_STREAM, 0);
	if (listenfd == -1)
	{
		return -1;
	}

	struct sockaddr_in ser_addr;
	memset(&ser_addr, 0, sizeof(ser_addr));
	ser_addr.sin_family = AF_INET;
	ser_addr.sin_port = htons(6000);
	ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = bind(listenfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
	if (res == -1)
	{
		return -1;
	}

	res = listen(listenfd, 5);
	if (res == -1)
	{
		return -1;
	}

	return listenfd;
}

//处理客户端数据
void client_fun(int fd, short event, void *arg)
{
	ClientData *clients = (ClientData*)arg;

	char buff[DATALENGTH] = {0};
	int n = recv(fd, buff, DATALENGTH-1, 0);
	if (n <= 0)
	{
		struct event *listen_event = DeleteOfClients(clients, fd);
		event_free(listen_event);
		printf("one client unlink!\n");
		return;
	}

	printf("%d : %s\n", fd, buff);
	send(fd, "OK", 2, 0);
}

//处理连接事件
void sockfd_fun(int fd, short event, void *arg)
{
	ClientData *clients = (ClientData*)arg;
	struct sockaddr_in cli_addr;
	socklen_t len = sizeof(cli_addr);
	int c = accept(fd, (struct sockaddr*)&cli_addr, &len);
	if (c < 0)
	{
		return;
	}

	struct event *listen_event = event_new(base,c, EV_READ | EV_PERSIST, client_fun, arg);
	InsertToClients(clients, c, listen_event);
	event_add(listen_event, NULL);
	printf("one client link!\n");
}

int main()
{
	int sockfd = CreateSocket();
	assert(sockfd != -1);

	ClientData clients[MAX_CLIENT];
	InitClients(clients);

	base = event_init();

	//针对与listenfd创建事件
	struct event *listen_event = event_new(base, sockfd, EV_READ | EV_PERSIST, sockfd_fun, (void*)clients);
	event_add(listen_event, NULL);
	

	//执行事件循环,检测就绪事件
	event_base_dispatch(base);

	//事件循环结束,释放资源
	event_free(listen_event);
	event_base_free(base);

	exit(0);
}

服务器代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	assert(sockfd != -1);

	struct sockaddr_in ser_addr;
	memset(&ser_addr, 0, sizeof(ser_addr));
	ser_addr.sin_family = AF_INET;
	ser_addr.sin_port = htons(6000);
	ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	//connect完成与与服务器的连接,所以sockaddr应该给服务器的地址信息
	int res = connect(sockfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
	assert(res != -1);

	while (1)
	{
		char buff[128] = {0};
		printf("input:");
		fgets(buff, 127, stdin);

		if (strncmp(buff, "end", 3) == 0)
		{
			break;
		}

		int num = send(sockfd, buff, strlen(buff)-1, 0);
		assert(num != -1);
		if (num == 0) //未写入进去数据
		{
			printf("the length send data is 0\n");
			break;
		}

		char recvbuff[128] = {0};
		int n = recv(sockfd, recvbuff, 127, 0);
		assert(n != -1);
		if (n == 0)
		{
			printf("recv data error\n");
			break;
		}
		printf("recv data is : %s\n", recvbuff);
	}
	close(sockfd);
	exit(0);
}

测试结果:
一个客户端:
Linux:Libevent简单使用_第4张图片

多个客户端:
Linux:Libevent简单使用_第5张图片

你可能感兴趣的:(Linux)