17.1 优于select的epoll

1. 实现IO复用的传统方法select和poll,但是性能不满意,因此有Linux的epoll,BSD的kqueue,Solaris的/dev/poll,Windows的IOCP。

如,select不适合以web服务器端开发为主流的现代开发环境。

a. 调用select后常见的针对所有文件描述符的循环语句;

b. 每次调用select函数时都需要向函数传递监视对象信息。  —— 致命弱点


2. select的优点:

a. 程序具有兼容性;

b. 服务器端接入者少。


3. epoll可以克服select的缺点。epoll服务器端用到的三个函数

epoll_create:创建保存epoll文件描述符的空间 //对应 fd_set

epoll_ctl:向空间注册并注销文件描述符 //对应FD_SET,FD_CLR

epoll_wait:等待文件描述符发生变化 //对应select

//epoll将发生事件的文件描述符集中在一起,放在epoll_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;

4. epoll_create函数

调用epoll_create函数时创建的文件描述符保存空间称为“epoll例程”,size只是建议epoll例程大小,实际大小由操作系统决定。

Linux2.6.8之后,内核忽略size参数,会根据情况自动调整。

epoll_create函数创建的资源与套接字相同,由操作系统管理,终止时要close。                                                   

#include <sys/epoll.h>

int epoll_create(int size)

成功时返回epoll文件描述符,失败返回-1

size epoll实例的大小

5. epoll_ctl函数

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

成功时返回0,失败-1

epfd:用于注册监视对象的epoll例程的文件描述符
op:用于指定监视对象的添加、删除或更改	//EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD
fd:需要注册的监视对象文件描述符
event:监视对象事件类型

struct epoll_event event;

event.events = EPOLLIN; //
event.data.fd = sockfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event);

events 成员:
EPOLLIN:需要读取数据的情况
EPOLLOUT:输出缓冲为空,可以立即发送数据的情况
EPOLLPRI:受到OOB数据情况
EPOLLRDHUP:断开连接或半关闭的情况
EPOLLERR:发生错误的情况;
EPOLLET:以边缘触发的方式得到事件通知
EPOLLONESHOT:发生一次事件后,相应的文件描述符不再受到事件通知

6. epoll_wait函数

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

成功时返回事件的文件描述符数,失败-1

epfd:事件发生监视范围epoll例程的文件描述符
events:保存发生事件的文件描述符集合的结构体地址值
maxevents:第二个参数中可以保存的最大事件数
timeout:以1/1000秒为单位等待时间

7. epoll实现的echo服务器

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#include <sys/socket.h>  
#include <sys/epoll.h>
  
#define BUF_SIZE    100
#define EPOLL_SIZE	50
  
void error_handling(char *message);  
  
int main(int argc, char *argv[]){  
    int serv_sock,clnt_sock;  
    char buf[BUF_SIZE];  
    struct sockaddr_in serv_adr;  
    struct sockaddr_in clnt_adr;  
    socklen_t adr_sz;  
    int str_len,i;
    struct epoll_event *ep_events;
    struct epoll_event event;
    int epfd,event_cnt;  

    if(argc != 2){  
        printf("Usage : %s <port>\n",argv[0]);  
        exit(1);  
    }  

    serv_sock = socket(PF_INET,SOCK_STREAM,0);  
    if(serv_sock == -1){  
        error_handling("socket error");  
    }  
  
    memset(&serv_adr,0,sizeof(serv_adr));  
    serv_adr.sin_family=AF_INET;  
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);  
    serv_adr.sin_port=htons(atoi(argv[1]));  
  
    if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1){  
        error_handling("bind() error");  
    }  
  
    if(listen(serv_sock,5) == -1){  
        error_handling("listen() error");  
    }  
    
    epfd = epoll_create(EPOLL_SIZE);
    ep_events = malloc(sizeof(struct epoll_event)*EPOLL_SIZE);

    event.events = EPOLLIN;
    event.data.fd = serv_sock;
    epoll_ctl(epfd,EPOLL_CTL_ADD,serv_sock,&event);

    while(1){ 
	event_cnt = epoll_wait(epfd,ep_events,EPOLL_SIZE,-1);

	if(event_cnt  == -1){
	    puts("epoll wait error");
	    break;
	}

	for(i=0;i<event_cnt;i++){
	    if(ep_events[i].data.fd == serv_sock){
		adr_sz = sizeof(clnt_adr);
        	clnt_sock = accept(serv_sock,(struct sockaddr *)&clnt_adr,&adr_sz);  
		event.events = EPOLLIN;
		event.data.fd = clnt_sock;
		epoll_ctl(epfd,EPOLL_CTL_ADD,clnt_sock,&event);
		printf("connected client : %d \n", clnt_sock);
	    }else{	//read message
		str_len = read(ep_events[i].data.fd,buf,BUF_SIZE);

		if(str_len == 0){	//close
		    epoll_ctl(epfd,EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);
		    close(ep_events[i].data.fd);
		    printf("closed client %d \n",ep_events[i].data.fd);
		}else{
			write(ep_events[i].data.fd,buf,str_len);	//echo


		}	    
	    }
	}
    }  
    close(serv_sock);  
    close(epfd);
    return 0;  
}  
  
void error_handling(char *message){  
  
    fputs(message,stderr);  
    fputs("\n",stderr);  
    exit(1);  
}

执行结果:

alex@alex-virtual-machine:/extra/tcpip/17$ ./a.out 9190
connected client : 5
closed client 5
connected client : 5
closed client 5
connected client : 5
connected client : 6
closed client 6
closed client 5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值