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;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值