epoll的使用方法及代码

一、epoll函数组解析

1、epoll_create函数

函数原型

int epoll_create(int size)

参数解释

(1)size:此参数在现在已经没有是什么意义了

(2)返回值:返回值为一个文件描述符,作为后面两个函数的参数

函数作用

此函数可以在内核中创建一个内核事件表,通过返回的内核事件表来管理

2、epoll_ctl函数

函数原型

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

参数解释

(1)epfd:操作内核时间表的文件描述符,即epoll_create函数的返回值

(2)op:操作内核时间表的方式

        有以下操作方式:

      (1)  EPOLL_CTL_ADD(向内核时间表添加文件描述符,即注册) 
      (2)  EPOLL_CTL_MOD(修改内核事件表事件)
      (3)  EPOLL_CTL_DEL (删除内核事件表中的事件)

(3)fd:操作的文件描述符

(4)event:指向struct epoll_event的指针

event结构

struct epoll_event
{
    uint32_t events;
    epoll_data_t data;
};

typedef union epoll_data
{
    void *ptr;
    int fd;
    _uint32_t u32;
    _uint64_t u64;
}epoll_data_t;

结构体变量解释

(1)events:储存用户感兴趣的事情和就绪事件

(2)data:为一个联合体,联合体最重要的就是fd,即要操作的文件描述符

events取值

(1)EPOLLOUT:表示对应的文件描述符可以写;
(2)EPOLLPRI:表示对应的文件描述符有紧急的数据可读
(3)EPOLLERR:表示对应的文件描述符发生错误;
(4)EPOLLHUP:表示对应的文件描述符被挂断;
(5)EPOLLET:表示对应的文件描述符设定为edge模式;

函数作用

此函数可以增,删,改内核事件表中的事件

3、epoll_wait函数

函数原型

int epoll_wait(int epfd, struct epoll_event events, int maxevents, int timeout)

参数解释

(1)epfd:同上面函数

(2)events:用于接收内核返回的就绪事件的数组

(3)maxevents:用户最多能处理的事件个数

(4)等待I/O的超时值(后面的编程设为-1,表示永不超时),单位为ms

(5)返回值,指的是就绪事件的个数

函数作用

此函数在内核中实现,若有就绪事件,内核就会向用户返回已就绪的事件(注意:这里与poll不同,pol只返回已就绪事件的个数)

二、epoll编程代码及注释

#define _GNU_SOURCE
#define MAXFD 100

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
int create_socket()//创建一个socket连接
{
	int listenfd = socket(AF_INET, SOCK_STREAM, 0);
	assert(-1 != listenfd);

	struct sockaddr_in ser;
	ser.sin_family = AF_INET;
	ser.sin_port = htons(6000);
	ser.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = bind(listenfd, (struct sockaddr *)&ser, sizeof(ser));
	assert(-1 != res);

	listen(listenfd, 5);
	return listenfd;
}

void get_client_link(int fd, int epfd)//获取一个客户端连接
{
	struct sockaddr_in cli;
	int len = sizeof(cli);

	int c = accept(fd,(struct sockaddr_in *)&cli, &len);
	
	struct epoll_event ev;
	ev.events = EPOLLIN;
	ev.data.fd = c;

	epoll_ctl(epfd, EPOLL_CTL_ADD, c, &ev);

	printf("one client link\n");
}

void unlink_client(int fd, int epfd)//断开连接
{
	close(fd);
	epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
	printf("one client unlink\n");
}

void deal_client_data(int fd, int epfd)//处理客户端数据
{
	int buff[128] = {0};
	int res = recv(fd, buff, 127, 0);

	if(res == 0)
	{
		unlink_client(fd, epfd);
		return;
	}
	printf("%s\n",buff);
	send(fd, "ok", 2, 0);
}

void deal_finish_fd(int n, struct epoll_event *events, int epfd, int listenfd)//处理就绪的文件描述符
{	
	int i = 0;
	for(i = 0;i <= n;i++)
	{
		int fd = events[i].data.fd;
		if(fd == listenfd)
		{
			get_client_link(fd, epfd);
		}
		else if(events[i].events & EPOLLHUP)
		{
			unlink_client(fd, epfd);
		}
		else if(events[i].events & EPOLLIN)
		{
			deal_client_data(fd, epfd);//在后面,如果n = 0,需要断开连接,所以得把epfd传进去
		}
		else
		{
			printf("fd error\n");
		}
	}
}

int main()
{
	int listenfd = create_socket();
	
	int epfd = epoll_create(5);

	struct epoll_event ev;
	ev.data.fd = listenfd;
	ev.events = EPOLLIN;

	epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);//在内核事件中注册listenfd

	while(1)
	{
		struct epoll_event events[MAXFD];

		int n = epoll_wait(epfd, events, MAXFD, -1);
		
		if(n < 0)
		{
			printf("epoll error\n");
			break;
		}

		deal_finish_fd(n, events, epfd, listenfd);
	}

	exit(0);
}

当然这是份儿服务器代码,如果要检测的话得需要一分儿客户端代码,这这里就不过多的赘述了,

有兴趣的读者可以自己编写测试一下,一下是我的测试结果:

epoll的使用方法及代码_第1张图片

epoll的使用方法及代码_第2张图片

三、epoll的优缺点

1、支持打开大数目的socket描述符(相对于select)

2、I\O效率不随fd数目的增加而线性下降(因为每次内核返回的是已就绪的文件描述符)

以上就是epoll的使用以及代码,欢迎读者纠错

 

你可能感兴趣的:(epoll的使用方法及代码)