欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux网络编程 - I/O多路复用之epoll

程序员文章站 2022-06-14 10:58:48
...

目录

  • 什么是epoll?
  • 为什么要使用epoll?
  • 如何使用epoll?
  • 查看epoll官方帮助文档
  • epoll版TCP回音客户端
  • epoll版TCP回音服务端


<一> 什么是epoll?

epoll = enhance poll 增强版轮询,是poll/select 的升级版;

<二> 为什么要使用epoll?

使用epoll在对fd进行监视时,如果有满足条件的fd,则该fd会被添加到一个新的数组区域,该区域存放所有满足条件的fd;

获取/判断满足条件的fd时,遍历当前数组即可;

但上述原理和select原理基本一致,那epoll增强了什么?

epoll在轮询(poll)时,提供了两种触发方式:

  1. Level Triggered 水平触发(EPOLLLT)
  2. Edge Triggered 边缘触发(EPOLLET)

水平触发(LT):

对于一文件描述符fd=0,在被epoll监视时,若采用LT则在read函数读入数据,若buffer大小 < read内容大小,则在下一次使用read时,epoll会通知read读取上一次剩余的内容(epoll缺省配置);

边缘触发(ET):

相较于上述LT中示例,边缘触发在下一次使用read0时,epoll不会再通知read0中有数据可读,即再次读取一次新的数据,原有read(0,buffer,sizeof(buffer)) 中大于sizeof(buffer)的内容将被丢弃;

边缘:每次读或写作为一个边缘,遇到边缘只通知一次,随后不再处理边缘文件描述符。

<三> 如何使用epoll?

1> 首先需要创建一个epoll文件描述符,指定epoll可以监视fd的最大值

#define MAX_EVENTS 10

int epfd = epoll_create(MAX_EVENTS);
if(epfd == -1){
    perror("epoll_create");    //stdio.h
    exit(EXIT_FAILURE);        //stdlib.h
}

2> 指定需要监视fd的活动(读/写/...)

struct epoll_event ev, events[MAX_EVENTS];

int fd = 0;
ev.events = EPOLLIN|EPOLLET;
ev.data.fd = fd;

3> 将指定活动添加进入epoll实例

if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd,ev) == -1){
    perror("epoll_ctl:0");
    exit(EXIT_FAILURE);
}

4> 使用循环,开启epoll监视

for(;;){
    int num = epoll_wait(epfd,rev,MAX_EVENTS,-1);
    if(num == -1){
	perror("epoll_wait");
	break;
    }
    //...
    int i=0;
    for(;i<num;i++){
        if(events[i].data.fd == fd){
            size = read(0,buffer,sizeof(buffer));
        }
        //...
}
}

<四> 查看epoll官方帮助文档

man 2 epoll

<五> epoll版TCP回音客户端

Linux网络编程 - I/O多路复用之epoll

客户端处理函数:

Client 边缘触发示例,用于边缘触发本地输入与客户端对socket的读取;

本示例无法解决客户端 "粘包" 问题

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#define MAX_EVENTS 2


/* 服务器对客户端请求的处理 */
void server_process(int sc){
    int size = 0;
    char buffer[1024];
    for(;;){
    	size = read(sc,buffer,sizeof(buffer));
   		if(size == 0){
			return;
    	}
		write(sc,buffer,size);
		write(1,buffer,size);
		memset(buffer,0,sizeof(buffer));
	}
}

/* 客户端处理数据 */
void client_process(int ss){
    int size = 0;
    char send_buffer[1024];    //发送缓冲区1K
    char recv_buffer[5];    //接收缓冲区5B

	int epfd = epoll_create(2);
	int fd = 0;
	struct epoll_event ev1,ev2;
	struct epoll_event events[MAX_EVENTS];
	ev1.data.fd = fd;
	ev1.events = EPOLLIN|EPOLLET;
	ev2.data.fd = ss;
	ev2.events = EPOLLIN|EPOLLET;
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev1)==-1){
		perror("epoll_ctl:0");
		exit(EXIT_FAILURE);
	}
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,ss,&ev2)==-1){
		perror("epoll_ctl:ss");
		exit(EXIT_FAILURE);
	}

	memset(send_buffer,0,sizeof(send_buffer));
	memset(recv_buffer,0,sizeof(recv_buffer));
        for(;;){
		int num = epoll_wait(epfd,events,MAX_EVENTS,-1);
		if(num == -1){
			perror("epoll_wait");
			exit(EXIT_FAILURE);
		}
		int i=0;
		for(;i<num;i++){
			int tfd = events[i].data.fd;
			if(tfd == fd){
				size = read(fd,send_buffer,sizeof(send_buffer));
				write(ss,send_buffer,size);
				memset(send_buffer,0,sizeof(send_buffer));
			}else 
			if(tfd == ss){
				printf("++++\n");
				size = read(ss,recv_buffer,sizeof(recv_buffer));
				write(1,recv_buffer,size);
				memset(recv_buffer,0,size);
			}
		}
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Linux网络编程 - I/O多路复用之epoll