从零开始写select和epoll I/O多路复用网络模型

从零开始写select和epoll多路复用网络模型

epoll实例

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         #include 
        
          #include 
         
           #include 
          
            #include 
           
             #include 
            
              #include 
             
               using namespace std; #define MAXLINE 1024 #define LISTEN_MAX 10000 #define LISTENQ 20 void setnonblocking(int sock) { int opts; opts=fcntl(sock,F_GETFL); if(opts<0) { perror("fcntl(sock,GETFL)"); exit(1); } opts = opts|O_NONBLOCK; if(fcntl(sock,F_SETFL,opts)<0) { perror("fcntl(sock,SETFL,opts)"); exit(1); } } int main(int argc, char* argv[]) { int i, maxi, listenfd, connfd, sockfd,epfd,nfds, portnumber; ssize_t n; char line[MAXLINE]; socklen_t clilen; if ( 2 == argc ) { if( (portnumber = atoi(argv[1])) < 0 ) { fprintf(stderr,"Usage:%s portnumber/a/n",argv[0]); return 1; } } else { fprintf(stderr,"Usage:%s portnumber/a/n",argv[0]); return 1; } struct epoll_event ev,events[LISTEN_MAX]; epfd = epoll_create(LISTEN_MAX); struct sockaddr_in clientaddr; struct sockaddr_in serveraddr; listenfd = socket(AF_INET, SOCK_STREAM, 0); if (listenfd < 0) { printf("Create socket error,%s\n",strerror(errno)); return 0; } //把socket设置为非阻塞方式 setnonblocking(listenfd); //设置与要处理的事件相关的文件描述符 ev.data.fd=listenfd; //设置要处理的事件类型 ev.events=EPOLLIN|EPOLLET; //注册epoll事件 epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; const char *local_addr = "127.0.0.1"; inet_aton(local_addr,&(serveraddr.sin_addr));//htons(portnumber); serveraddr.sin_port=htons(portnumber); if (bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { printf("Bind error %s\n",strerror(errno)); return 0; } if (listen(listenfd, LISTENQ) < 0) { printf("Listen error :%s\n",strerror(errno)); return 0; } maxi = 0; for ( ; ; ) { //等待epoll事件的发生 nfds=epoll_wait(epfd,events,20,500); //处理所发生的所有事件 for(i=0;i 
               
              
             
            
           
          
         
       
      
      
     
     
    
    
   
   
select实例
#include 
    
    
     
       
#include 
     
     
      
        
#include 
      
      
       
         
#include 
       
       
        
          
#include 
        
        
          #include 
         
           #include 
          
            #define MAXLINE 1024 int main(int argc, char *argv[]) { int i, maxi, maxfd, listenfd, confd, sockfd,portnumber; if ( 2 == argc ) { if( (portnumber = atoi(argv[1])) < 0 ) { fprintf(stderr,"Usage:%s portnumber/a/n",argv[0]); return 1; } } else { fprintf(stderr,"Usage:%s portnumber/a/n",argv[0]); return 1; } // FD_SETSIZE 默认为1024,可以修改通过内核参数来修改比较麻烦 int nret, client[FD_SETSIZE]; ssize_t n; fd_set rset, allset; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; socklen_t clientaddr_len; struct sockaddr_in clientaddr, serveraddr; listenfd = socket(AF_INET, SOCK_STREAM, 0); if (listenfd < 0) { printf("Create socket errror fd = %d\n",listenfd); return 0; } bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); serveraddr.sin_port = htons(portnumber); if (bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { printf("Bind socket error,error %s\n",strerror(errno)); return 0; } if (listen(listenfd, 128) < 0) { printf("Listen socket error %s\n",strerror(errno)); return 0; } //maxfd为现在监听的文件描述符的最大值 maxfd = listenfd; //可用fd client[]的下标 maxi = -1; for (i = 0; i < FD_SETSIZE; i++) client[i] = -1; /* 用-1初始化client[] */ //FD_ZERO(&allset); //FD_SET(listenfd, &allset); printf("server start on port = %d\n",portnumber); for ( ; ; ) { /* *FD_ZERO(&allset); *FD_SET(listenfd, &allset); *一定要在每次select都要重新设置一遍,因为调用select后读socket数量同时会把不可读的socket从端口集中清除掉 *所以如果不重新设置有可能监听描述符等一些已连接上的client从集合中清除掉,将会造成收不到客户端连接请求或者数据 *如果设置超时也是一样,每次select都要设置 */ // 设置allset(所有监听文件描述符集合最新状态) FD_ZERO(&allset); //把监听描述符加到集合中 FD_SET(listenfd, &allset); //把已连接上的的client加到监听集合中 for (i = 0; i <= maxi;i ++) { if (client[i] > 0) { FD_SET(client[i],&allset); if (client[i] > maxfd) { maxfd = client[i]; } if (i > maxi) { maxi = i; } } } /* * 等待读取 注:调用select后读socket数量同时会把不可读的socket从端口集中清除掉 * 因此后面再次FD_SET判断,如果仍然在端口集中socket可读 */ nret = select(maxfd+1, &allset, NULL, NULL, NULL);//检测“读” if (nret < 0) { printf("select error"); return 0; } if (FD_ISSET(listenfd, &allset)) //如果是新的连接到来 { clientaddr_len = sizeof(clientaddr); confd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddr_len); printf("New connect fd = %d\n",confd); for (i = 0; i < FD_SETSIZE; i++) { if (client[i] < 0) { client[i] = confd; //将新的文件描述符添加到client里 break; } } // 达到select能监控的文件个数上限1024 if (i == FD_SETSIZE) { printf("too many clients\n"); exit(1); } FD_SET(confd, &allset); //将新的新的文件描述符添加到监控信号集里 if (confd > maxfd) { maxfd = confd; } if (i > maxi) { maxi = i; } } for (i = 0; i <= maxi; i++) { // 检测哪个clients 有数据就绪 if ( (sockfd = client[i]) < 0) continue; if (FD_ISSET(sockfd, &allset)) //对连接到服务器的每个客户机(文件描述符),检测是否有数据到达 { if ( (n = read(sockfd, buf, MAXLINE)) == 0) { // 当client关闭链接时,服务器端也关闭对应链接 close(sockfd); FD_CLR(sockfd, &allset); // 解除select监控此文件描述符 client[i] = -1; } else { printf("RecvData from fd = %d,data is %s \n",client[i],buf); //给客户端恢复数据 while(1) { int iSendBytes = write(sockfd, buf, n); if (iSendBytes == n) { break; } else { if (0 >= iSendBytes && EINTR == errno) { continue; } break; } } } } } } close(listenfd); return 0; } 
           
          
        
       
       
      
      
     
     
    
    
client测试例子
#include
     
     
      
      
#include
      
      
       
       
#include
       
       
        
        
#include
        
        
         
         
#include
         
         
           #include 
          
            #include 
           
             #include 
            
              #include 
             
               #include 
              
                #define MAXLINE 1024 int main(int argc,char *argv[]) { int sockfd; char sendbuffer[MAXLINE]; struct sockaddr_in server_addr; struct hostent *host; int portnumber,nbytes; if(argc!=3) { fprintf(stderr,"Usage :%s hostname portnumber\a\n",argv[0]); exit(1); } if((host=gethostbyname(argv[1]))==NULL) { herror("Get host name error\n"); exit(1); } if((portnumber=atoi(argv[2]))<0) { fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]); exit(1); } if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) { fprintf(stderr,"Socket Error:%s\a\n",strerror(errno)); exit(1); } bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family=AF_INET; server_addr.sin_port=htons(portnumber); server_addr.sin_addr=*((struct in_addr *)host->h_addr); if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) { fprintf(stderr,"Connect error:%s\n",strerror(errno)); exit(1); } while(1) { printf("Please input your word:\n"); scanf("%s",sendbuffer); printf("\n"); if(strcmp(sendbuffer,"quit")==0) break; send(sockfd,sendbuffer,sizeof(sendbuffer),0); } close(sockfd); exit(0); } 
               
              
             
            
           
         
        
        
       
       
      
      
     
     
这两种I/O多路复用机制在原理上有根本的区别: 点击打开链接,另外在使用上也有所不同,调用select时会把没有事件的socket从集合中清除掉,在上面的select例子中也有提到,而epoll_wait则不会,这点一定要注意。(注:上面的例子只是最简单的实例,对于tcp的粘包和拆包等问题并没有做处理,还有tcp的端口重用,发送超时等tcp设置并没有做处理。)




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值