epoll详解(Linux代码实现)

epoll是一个Linux内核 的系统调用,一个可扩展的I / O事件通知机制。它的功能是监控多个文件描述符,看看I / O有可能在其中任何一个。它是为了取代旧的POSIX select和系统调用,实现了更为苛刻的应用更好的性能,在观看了数文件描述符较大(不同于旧的系统调用,这在操作的时间O(N),工作在O(1)的时间 )。类似的FreeBSD的,因为它操作的配置的内核对象上,暴露于用户空间作为其自身的一个文件描述符。





一个简单的epoll代码如下。该程序和select的例子一样,故不在多介绍,读者可详细看看

<span style="font-size:18px;">#include<stdio.h>
#include<sys/socket.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<sys/time.h>
#include<sys/epoll.h>

static void guse(const char* prac)
{
printf("%s [ip] [port]..\n",prac);
}

static int my_select(const char* _ip, const int _port){    //获得socket套接字
	int sock = socket(AF_INET,SOCK_STREAM,0);
	if(sock < 0){
		perror("create socket error...");
		exit(1);
	}
	
	struct sockaddr_in server_socket;
	bzero(&server_socket,sizeof(server_socket));
	server_socket.sin_family = AF_INET;
	server_socket.sin_addr.s_addr =inet_addr(_ip);
	server_socket.sin_port = htons(_port);
	
	int opt = 1;
	setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
	
	if(bind(sock,(struct sockaddr*)&server_socket,sizeof(server_socket))< 0)
	{
		perror("bind error ...");
		exit(2);
	}
	
	if(listen(sock,5) < 0 ){
		perror("listen errno ...");
		close(sock);
		 exit(3);
	}

	return sock;
}




int main(int argc,char* argv[])
{
   if(argc != 3)
 {
   guse(argv[0]);
     exit(0);
 }
   int listen_sock =  my_select(argv[1], atoi(argv[2]));
   int fdep = epoll_create(256);   //创建epoll
   if(fdep < 0){
	   perror("epoll_creact...\n");
	   exit(4);
   }

    struct epoll_event _ev,revent[20];
	_ev.events = EPOLLIN;
	_ev.data.fd = listen_sock;
	if(epoll_ctl(fdep,EPOLL_CTL_ADD,listen_sock,&_ev)< 0){  //配置注册函数
		perror("epoll _ctl is errno ..\n");
		exit(5);
	}

	int times = -1;
	int fdnum;

   while(1){
	   int revent_size = sizeof(revent)/sizeof(revent);
		switch(fdnum = epoll_wait(fdep,revent,revent_size,times)){  //收集等待事件
			case -1:
				printf("selsect errno..\n");
				exit(6);
				break;
			case 0:
				printf("Time out \n");
				break;
			default:
				{
					int index = 0;
					int new_fd = 0;
					struct sockaddr_in cli;
					socklen_t len = sizeof(cli);
					char buf[1024];

					for(;index < fdnum; index++){
					int fd = revent[index].data.fd;
						if(listen_sock == fd && revent[index].events & EPOLLIN){  
							new_fd =accept(listen_sock,(struct sockaddr*)&cli,&len);	
						if(new_fd >0 ){   //接受了一个新的链接
							printf("%s:%d\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
				 	_ev.data.fd = new_fd;
					_ev.events = EPOLLIN;
					if(epoll_ctl(fdep,EPOLL_CTL_ADD,new_fd,&_ev) <0 ){  //再把这个链接增加到epoll
					perror("accept error...\n");
				    close(fd);
			     	exit(7);
						}
				}
				}else{
				if(revent[index].events & EPOLLIN){   //如果是普通的,便输出
					memset(buf,'\0',sizeof(buf));
				ssize_t _s = recv(fd,buf,sizeof(buf)-1,0);
				if(_s == 0){
					printf("client close ...\n");
					epoll_ctl(fdep,EPOLL_CTL_DEL,fd,NULL);
					close(fd);
				}
				else if(_s < 0)
				{
					printf("errno  read..\n");
				}
				else{
					printf("client:%s",buf);
				}
				}
				}
	}
					}
				}
				break;
		}
   }

return 0;
}</span>

epoll同时提供了边沿触发和电平触发模式。在边沿触发模式,调用epoll_wait将返回只有当一个新的事件排队的epoll对象,而在电平触发模式,epoll_wait将只要条件成立返回。

例如,如果一个管到与登记的epoll,已接收到数据,则调用epoll_wait将返回,信令数据的存在被读取。假设读者仅消耗来自缓冲器的数据的一部分。在电平触发模式下,还呼吁epoll_wait将立即返回,只要管道缓冲区包含要读取的数据。在边沿触发模式,但是,epoll_wait将只一次新数据写入到管道返回。

下面便是给ET模式的代码,只是在上面代码的情况下做出了更改:

<span style="font-size:18px;">#include<stdio.h>
#include<sys/socket.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<sys/time.h>
#include<sys/epoll.h>
#include <fcntl.h>

static void guse(const char* prac)
{
printf("%s [ip] [port]..\n",prac);
}

static int read_data(int fd, char buf[]){   //对读的封装,考虑考ET的特性,故而,要一次将数据读完
    int nread = 0;
    int n = 0;
    while(nread = (recv(fd,buf,128,0)) > 0){
        n = nread;
    }

    return nread;
}

static int set_noblock(int sock){       //使其阻塞的等待
    int fl = fcntl(sock,F_GETFL);
    return fcntl(sock,F_SETFL,fl|O_NONBLOCK);
}


static int my_select(const char* _ip, const int _port){
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock < 0){
        perror("create socket error...");
        exit(1);
    }
    
    struct sockaddr_in server_socket;
    bzero(&server_socket,sizeof(server_socket));
    server_socket.sin_family = AF_INET;
    server_socket.sin_addr.s_addr =inet_addr(_ip);
    server_socket.sin_port = htons(_port);
    
    int opt = 1;
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    
    if(bind(sock,(struct sockaddr*)&server_socket,sizeof(server_socket))< 0)
    {
        perror("bind error ...");
        exit(2);
    }
    
    if(listen(sock,5) < 0 ){
        perror("listen errno ...");
        close(sock);
         exit(3);
    }

    return sock;
}




int main(int argc,char* argv[])
{
   if(argc != 3)
 {
   guse(argv[0]);
     exit(0);
 }
   int listen_sock =  my_select(argv[1], atoi(argv[2]));
   int fdep = epoll_create(256);
   if(fdep < 0){
       perror("epoll_creact...\n");
       exit(4);
   }

    struct epoll_event _ev,revent[128];
    _ev.events = EPOLLIN;
    _ev.data.fd = listen_sock;
    if(epoll_ctl(fdep,EPOLL_CTL_ADD,listen_sock,&_ev)< 0){
        perror("epoll _ctl is errno ..\n");
        exit(5);
    }

       int revent_size = sizeof(revent)/sizeof(revent);
    int times = -1;
    int fdnum;

   while(1){
        switch(fdnum = epoll_wait(fdep,revent,revent_size,times)){
            case -1:
                printf("epoll  errno..\n");
                exit(6);
                break;
            case 0:
                printf("Time out \n");
                break;
            default:
                {
                    int index = 0;
                    struct sockaddr_in cli;
                    socklen_t len = sizeof(cli);
                    char buf[1024];

                    for(;index < fdnum; index++){
                    int fd = revent[index].data.fd;
                        if(listen_sock == fd && revent[index].events & EPOLLIN){
                        int    new_fd =accept(listen_sock,(struct sockaddr*)&cli,&len);    
                        if(new_fd >0 ){
                            printf("%s:%d\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
                     _ev.data.fd = new_fd;
                    _ev.events = EPOLLIN | EPOLLET;

                    set_noblock(new_fd);
                    epoll_ctl(fdep,EPOLL_CTL_ADD,new_fd,&_ev);
                }
                    }else{
                if(revent[index].events & EPOLLIN){
                    memset(buf,'\0',sizeof(buf));
                int ret = read_data(fd,buf);
                if(ret < 0){
                    printf("###############\n");
                    printf("client close ...\n");
                }
                else if(ret == 0)
                {
                    printf("client close ..\n");
                }
                else{
                    printf("recv ...\n");
                }
                }
                _ev.data.fd = fd;
                _ev.events = revent[index].events | EPOLLOUT;

                if(epoll_ctl(fdep,EPOLL_CTL_MOD,fd,&_ev)<0){
                    perror("MOD ...\n");
                }

                 if(revent[index].events & EPOLLOUT){
                    const char *msg = "HTTP/1.1 200 OK\r\n\r\n<h1>hello world  0.0</h1>\r\n";
                    send(fd,msg,strlen(msg),0);
                    epoll_ctl(fdep ,EPOLL_CTL_DEL,fd,NULL);
                    close(fd);
                }
            }
        }

                break;
        }
   }
 }

return 0;
}


}</span>



接下来,我们用浏览器看看:



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值