select、poll、epoll的區別以及epoll的兩種模式(LT、ET)以及實現

select和poll的缺點:

(1)、每輪循環都要從用戶空間往內核空間拷貝數據;

(2)、內核輪詢,檢測每個描述符有沒有就緒事件,O(n);

(3)、I/O函數返回後,遍歷每個描述符找到有事件就緒的描述符,O(n);

(select、poll)和(epoll)的區別:

(1)、select、poll每次循環都需要從用戶空間向內核空間傳遞數據;

             epoll直接在內核空間創建事件表,每個描述符只需要傳遞一次;

(2)、select、poll在內核中以輪詢的方式檢測就緒事件描述符;

             epoll在每個描述符上註冊回調函數,事件就緒後執行回調函數將描述符添加到就緒隊列;

(3)、select、epoll返回後,需要遍歷所有文件描述符,才能找到就緒的文件描述符;

             epoll返回後,直接得到就緒描述符不需要遍歷所有描述符;

epoll的兩種模式:

(1)、LT模式(電平觸發):描述符事件就緒後,如果用戶沒有處理完數據,epoll會一直提醒,直到處理完成;代碼實現(epoll-LT.c

(2)、ET模式(邊沿觸發):高效模式。描述符事件就緒後,無論用戶有沒有處理完數據,epoll都只會提醒一次,所以在處理時要獲取完整數據,需要循環獲取所有數據;代碼實現(epoll-ET.c

//epoll-LT.c
//I/O複用:epoll() LT模式
//LT模式:描述符時間就緒後,如果用戶沒有處理完數據,epoll會反覆提醒,直到處理完成

#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/epoll.h>

#define MAXFD 10
int create_socket();
void epoll_add(int epfd,int fd)
{
    struct epoll_event ev;
    ev.events = EPOLLIN;      //LT模式
    ev.data.fd = fd;

    if( epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev) == -1 )
    {
	perror("epoll_ctl error");
    }
}

void epoll_del(int epfd,int fd)
{
    if( epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL) == -1 )
    {
	perror("epoll del erreo");
    }
}
int main()
{
    int sockfd = create_socket();
    assert(sockfd != -1);
    
    int epfd = epoll_create(MAXFD);
    assert(epfd != -1);
    
    epoll_add(epfd,sockfd);

    struct epoll_event events[MAXFD];
    while (1)
    { 
	int n = epoll_wait(epfd,events,MAXFD,5000);
	if( n == -1 )
	{
	    perror("epoll error");
	}
	else if(n == 0)
	{
	    printf("time out\n");
	}
	else
	{
	    int i = 0;
	    for(;i<n;i++)
	    {
		    if(events[i].events & EPOLLIN)
		    {
			if( events[i].data.fd == sockfd )
			{
			    struct sockaddr_in caddr;
			    int len = sizeof(caddr);
			    int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
			    if ( c < 0 )
			    {
				continue;
			    }
			    epoll_add(epfd,c);
			    printf("accept = %d\n",c);
			}
		       else
			{
			    char buff[128] = {0};
			    int num = recv(events[i].data.fd,buff,1,0);
			    if( num <= 0)
			    {
				epoll_del(epfd,events[i].data.fd);
				close(events[i].data.fd);
				printf("one client over\n");

			    }
			    else
			    {
				printf("recv(%d):%s\n",events[i].data.fd,buff);
				send(events[i].data.fd,"ok",2,0);
			    }
			}
		    }
	    }
	}
    }
    
}

int create_socket()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
    {
	return -1;
    }

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

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    assert(res != -1);
    listen(sockfd,5);
    return sockfd;
}
//epoll-ET.c
//I/O複用:epoll()  ET模式
//ET模式必須使用非阻塞模式

#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <fcntl.h>

#define MAXFD 10
int create_socket();
void setnonblock(int fd)
{
    int oldflg = fcntl(fd,F_GETFL);  //fcntl()可以設置非阻塞
    int newflg = oldflg | O_NONBLOCK;

    if(fcntl(fd,F_SETFL,newflg) == -1)
    {
	perror("fcntl error\n");
    }
}
void epoll_add(int epfd,int fd)
{
    struct epoll_event ev;
    ev.events = EPOLLIN | EPOLLET;    //ET模式
    ev.data.fd = fd;
    
    setnonblock(fd);

    if( epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev) == -1 )
    {
	perror("epoll_ctl error");
    }
}

void epoll_del(int epfd,int fd)
{
    if( epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL) == -1 )
    {
	perror("epoll del erreo");
    }
}
int main()
{
    int sockfd = create_socket();
    assert(sockfd != -1);
    
    int epfd = epoll_create(MAXFD);
    assert(epfd != -1);
    
    epoll_add(epfd,sockfd);

    struct epoll_event events[MAXFD];
    while (1)
    { 
	int n = epoll_wait(epfd,events,MAXFD,5000);
	if( n == -1 )
	{
	    perror("epoll error");
	}
	else if(n == 0)
	{
	    printf("time out\n");
	}
	else
	{
	    int i = 0;
	    for(;i<n;i++)
	    {
		    int fd = events[i].data.fd;
		    if(events[i].events & EPOLLIN)
		    {
			if( fd == sockfd )
			{
			    struct sockaddr_in caddr;
			    int len = sizeof(caddr);
			    int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
			    if ( c < 0 )
			    {
				continue;
			    }
			    epoll_add(epfd,c);
			    printf("accept = %d\n",c);
			}
		       else
			{
			    while(1)
			    {
				char buff[128] = {0};
				int num = recv(fd,buff,1,0);
				if( num == -1 )
				{
				    send(fd,"ok",2,0);
				    break;
				}
				else if( num == 0 )
				{
				    epoll_del(epfd,fd);
				    close(fd);
				    printf("one client over\n");
				    break;
				}
				else
				{
				    printf("buff(%d) = %s\n",fd,buff);
				}
			    }
			}
		    }
		}
	    }
	}
}

int create_socket()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
    {
	return -1;
    }

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

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    assert(res != -1);
    listen(sockfd,5);

    return sockfd;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章