浅谈select与epoll函数

第一篇技术博客,哈哈。这里讲的内容都是很浅的,很多细节没讲到,比如select函数读集数量上限,超时,非堵塞设置等等,自己百度吧~~


应用场景:
很多个客户端同时访问服务器,服务器需要处理各个客户端消息。


用法:
select:
1. 设置读集:将一堆客户端fd通通装入readfds集合


FD_ZERO(&readfds); //先清空
for(i = 0; i < num_of_client; i++)
     FD_SET(&fd[i],&readfds);


2. 调用select函数等待数据,函数返回说明有数据,或者出错,或者超时


int err=select(maxfd+1,&readfds,NULL,NULL,NULL) ;


3. 根据select函数返回值判断什么情况


err = -1 出错

err = 0 超时
else 客户端有数据


4. 在上一步else的基础上用 FD_ISSET 逐个判断这堆客户端是否有数据


		for( i = 0; i< num_of_client; i++)
		if(FD_ISSET(fd[i],readfds)){ //第i号客户端有数据啦
			dosomething(i);
		}


最后一步可能有点莫名其妙,这里介绍下select下函数原理。
之前不是设置了一个读集 readfds 吗,把所有客户端fd都放进去了,然后调用select函数,系统就开始轮询这个读集里所有的fd。所以轮询周期取决于你放了几个fd到集合里,放越多效率自然就低。比如在某一次轮询结束后中,系统检测到有20个客户端有新数据过来,它就把这20客户端的fd做个标记。然后函数返回,返回值大于0(具体几我也没去看过)。


然后你需要做的就是把这20个客户端找出来。怎么找?看第4步。所以你自己也得手动轮询一遍,把那些被标记的客户端找出来,再处理消息。比如这个读集里有1000个客户端,然后这1000个客户端,你就需要用 FD_ISSET 每个都去问他一下,哎哥们是你有话要说吗?哥们说,不是,找错人了。而且不管这哥们是不是有话说,你都得把剩下的问完,一个不能拉下。


看到这里你是不是也觉得这个过程有些重复劳动?为什么系统在轮询的时候,在检测到某个客户端有数据的时候,不直接把这个客户端的fd拎出来放到一个地方存起来,还标记毛啊。这样我们可以直接得到那个地方放着的fd。毕竟1000个客户端只有20个有数据过来,问其他980个是不是有数据是浪费时间。

这个时候epoll函数出现了。形式有点不同,理解起来可以当一回事。当然内部实现不一样。
epoll函数就是只把那些有数据的客户端fd放到某个地方,而且函数返回值为有数据的客户端的数量。这样我们就省事多了。20个客户端有数据过来,函数返回20,然后我们只需要循环20次,就把每个消息给处理了。而且成功率百分百,妈妈再也不用担心我像之前那样吃980/1000个闭门羹了。


epoll函数内部实现不像select函数那样用轮询,轮询我也会,这么low。epoll用的是回调函数。这个我自己也没搞清楚怎么实现的,要继续研究。epoll就不一步步讲解了,用起来跟select差不多的。直接贴源码。

#include 
   
   
    
    	
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         #include 
        
          #include 
         
           #include 
          
            #include 
           
             #include 
            
              /* typedef union epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t; struct epoll_event { __uint32_t events; // Epoll events epoll_data_t data; // User data variable }; EPOLLIN :表示对应的文件描述符可以读; EPOLLOUT :表示对应的文件描述符可以写; EPOLLPRI :表示对应的文件描述符有紧急的数据可读 EPOLLERR :表示对应的文件描述符发生错误; EPOLLHUP :表示对应的文件描述符被挂断; EPOLLET :表示对应的文件描述符有事件发生; */ #define MAXNUM 1024 #define MAXBUFFISZE 1024 int main(int argc,char *argv[]) { int ret = 0; int i = 0; int connection_num = 0; int clientfd = -1; int readsize = 0; char buff[MAXBUFFISZE] = ""; struct epoll_event new_connection; struct epoll_event events[MAXNUM]; int sockfd = socket(AF_INET,SOCK_STREAM,0); if(sockfd == -1){perror("socket");exit(EXIT_FAILURE);} struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family=AF_INET; servaddr.sin_port=htons(atoi(argv[2])); inet_aton(argv[1],(struct in_addr*)servaddr.sin_addr.s_addr); ret = bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)); if(ret){perror("bind");exit(EXIT_FAILURE);} ret = listen(sockfd,20); if(ret){perror("listen");exit(EXIT_FAILURE);} struct sockaddr clientaddr; socklen_t addrlen=sizeof(clientaddr); int efd = epoll_create(MAXNUM); if(efd == -1){perror("epoll_create");exit(1);} new_connection.data.fd = sockfd; new_connection.events = EPOLLIN|EPOLLET; ret = epoll_ctl(efd, EPOLL_CTL_ADD, sockfd, &new_connection); if(ret == -1){perror("epoll_ctl");exit(1);} while(1){ printf("waitting...\n"); ret = epoll_wait(efd, events, MAXNUM, -1); if(ret == -1){perror("epoll_wait");break;} if(ret == 0){} for(i = 0;i < ret; i++){ if(events[i].data.fd == sockfd){ clientfd = accept(sockfd,&clientaddr,&addrlen); if(clientfd==-1){perror("accept");continue;} new_connection.data.fd = clientfd; new_connection.events = EPOLLIN|EPOLLET; ret = epoll_ctl(efd, EPOLL_CTL_ADD, clientfd, &new_connection); if(ret == -1){perror("epoll_ctl");continue;} printf("new connection:%d, total connection nums:%d\n", clientfd, ++connection_num); }else{ clientfd = events[i].data.fd; memset(buff,0,MAXBUFFISZE); readsize = read(clientfd, buff,MAXBUFFISZE); //设为非阻塞 if(readsize < 0){ printf("client %d has disconnect\n", clientfd); close(clientfd); ret = epoll_ctl(efd, EPOLL_CTL_DEL, clientfd, &new_connection); if(ret == -1){perror("epoll_ctl");} continue; } if(strcmp(buff, "end") == 0){ printf("quitting...\n"); close(clientfd); close(sockfd); return 0; } if(strcmp(buff, "done") == 0){ close(clientfd); ret = epoll_ctl(efd, EPOLL_CTL_DEL, clientfd, &new_connection); if(ret == -1){perror("epoll_ctl");} printf("client %d done\n", clientfd); connection_num--; continue; } printf("read %d bytes from client %d:%s\n", readsize, clientfd, buff); } } } return 0; } 
             
            
           
          
         
       
      
      
     
     
    
    
   
   

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值