zoukankan      html  css  js  c++  java
  • linux epoll,poll,select

    epoll函数用法,还有点poll和select

    1,LT的epoll是select和poll函数的改进版。

    特点是,读完缓冲区后,如果缓冲区还有内容的话,epoll_wait函数还会返回,直到把缓冲区全部读完。

    2,ET的epoll(阻塞)

    特点是,读完缓冲区后,不管缓冲区还有没有内容,epoll_wait函数都不会再返回,直到对端再一次发送信息过来。估计有的读者朋友会想到用while去读,但是有个致命的问题,因为文件描述符是阻塞的,所以当全部读完后,进程就会阻塞在recv函数那里,就不能够再处理别的连接了。

    3,ET的epoll(非阻塞),效率最高的使用方法。

    特点是,读完缓冲区后,不管缓冲区还有没有内容,epoll_wait函数都不会再返回,直到对端再一次发送信息过来。但是可以事先用fcntl把文件描述符设置成非阻塞的方式,让后用while一直去读,当全部读完后,recv函数也不会阻塞。

    ET的epoll(非阻塞)的例子:

    #include <stdio.h>
    #include <sys/epoll.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <errno.h>
    
    int main(int argc, char** argv){
    
      int port = atoi(argv[1]);
      int lfd = socket(AF_INET, SOCK_STREAM, 0);
    
      struct sockaddr_in addr;
      addr.sin_family = AF_INET;
      addr.sin_port = htons(port);
      addr.sin_addr.s_addr = INADDR_ANY;
    
      bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
      listen(lfd, 5);
    
      int efd = epoll_create(10);
    
      struct epoll_event re;
      re.events = EPOLLIN;
      re.data.fd = lfd;
    
      epoll_ctl(efd, EPOLL_CTL_ADD, lfd, &re);
    
      struct epoll_event events[100];
      
      while(1){
        int ret = epoll_wait(efd, events, 100, -1);
        printf("======================wait=======
    ");
        if(ret == -1){
          perror("epoll_wait");
          exit(1);
        }
    
        for(int i = 0; i < ret; ++i){
          if(events[i].data.fd == lfd){
        	int cfd = accept(lfd, NULL, NULL);
    
    	    int flags = fcntl(cfd, F_GETFL);
    	    flags |= O_NONBLOCK;
    	    fcntl(cfd, F_SETFL, flags);
    	
    	    struct epoll_event re;
        	re.events = EPOLLIN | EPOLLET;
        	re.data.fd = cfd;
    	    epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &re);
    	    break;
          }
          char buf[3];
    
          int ret;
          while((ret = recv(events[i].data.fd, buf, sizeof buf, 0)) > 0){
    	    write(STDOUT_FILENO, buf, ret);
          }
          
          if(ret == 0){
        	epoll_ctl(efd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
    	    close(events[i].data.fd);
        	printf("client disconnet
    ");
          }
          else if(ret == -1 && errno == EAGAIN){
        	printf("read over
    ");	
          }
        }
      }
    }
    

    poll函数例子:

    #include <stdio.h>
    #include <poll.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    int main(int argc, char** argv){
    
      int port = atoi(argv[1]);
      int lfd = socket(AF_INET, SOCK_STREAM, 0);
    
      struct sockaddr_in addr;
      addr.sin_family = AF_INET;
      addr.sin_port = htons(port);
      addr.sin_addr.s_addr = INADDR_ANY;
    
      bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
      listen(lfd, 5);
    
      struct pollfd pfd[1024];
      for(int i = 0; i < 1024; ++i){
        pfd[i].fd = -1;
      }
      pfd[0].fd = lfd;
      pfd[0].events = POLLIN;
      nfds_t maxfd = 0;
      
      while(1){
        int ret = poll(pfd, maxfd + 1, -1);
        printf("--------------poll------
    ");
        if(pfd[0].revents & POLLIN){
          int cfd = accept(lfd, NULL, NULL);
          for(int i = 0; i < 1024; ++i){
    	    if(pfd[i].fd == -1){
    	      pfd[i].fd = cfd;
    	      pfd[i].events = POLLIN;
    	      maxfd++;
    	      break;
    	    }
          }
          continue;
        }
    
        for(int i = 0; i <= maxfd; ++i){
          if(pfd[i].revents & POLLIN){
    	    char buf[64];
    	    int ret = recv(pfd[i].fd, buf, sizeof buf, 0);
    	    if(ret == 0){
    	      pfd[i].fd = -1;
    	      close(pfd[i].fd);
    	      printf("client is disconnet
    ");
    	    }
    	    else{
    	      write(STDOUT_FILENO, buf, ret);
    	    }
          }
        } 
        
      }
    }
    
    

    通过对比epoll和poll的例子可以看出来:

    • epoll不需要事先决定数组的大小。poll需要。
    • epoll内部是用红黑树实现的效率,不会随着连接的增多,而明显的变低。poll是用链表实现的,所以性能随着连接的增多而降低。poll还不能在windows下使用。epoll是跨平台的。
    • 顺便说下,select是用数组实现的,数组的大小由内核代码写死了,就是1024,所以想增大,只能重新编译内核。但是select是在跨平台的。

    关于EPOLLOUT的补足:内核检查写的缓冲区,如果写缓冲区未满,处于可写的状态,epoll_wait函数就会返回。否则阻塞。

    • 水平模式:如果写缓冲区未满,epoll_wait会一直返回。
    • 边缘模式:epoll_wait会先返回一次;然后,写缓冲区从满的状态变成了未满的状态,epoll_wait返回。
      -注意点:调用send等函数的时候,如果写缓冲区满了的话,套接字如果是阻塞的,程序就费了,不再能相应任何事件。如果是非阻塞的话,send就会失败,有些数据就丢失了。所以,正确的做法是,当监听到EPOLLIN事件的时候,把数据读出来后,不要直接调用send等函数,要:把当前节点从树上删掉,然后加入一个EPOLLOUT的节点上去,等待epoll_wait的下一次返回,epoll_wait返回了,说明肯定可写。

    select函数例子

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <stdio.h>
    #include <arpa/inet.h>
    #include <sys/time.h>
    #include <unistd.h>
    
    int main(){
    
      int fd = socket(AF_INET, SOCK_STREAM, 0);
      
      struct sockaddr_in addr;
      addr.sin_family = AF_INET;
      addr.sin_addr.s_addr = INADDR_ANY;
      addr.sin_port = htons(12345);
      bind(fd, (struct sockaddr*)&addr, sizeof(addr));
    
      listen(fd, 5);
    
      fd_set readers, temp;
      FD_ZERO(&readers);
      FD_ZERO(&temp);
    
      FD_SET(fd, &readers);
    
      int maxfd = fd;
    
      int selret = 0;
    
      char rbuf[1024] = {0};
      while(1){
        temp = readers;
        selret = select(maxfd + 1, &temp, NULL, NULL, NULL); 
    
        
        if(FD_ISSET(fd, &temp)){
          //server
          int cfd = accept(fd, NULL, 0);
          maxfd = cfd;
          FD_SET(cfd, &readers);
          maxfd = maxfd < cfd ? cfd : maxfd;
          continue;
        }
    
        //client
        for(int i = fd + 1; i <= maxfd; ++i){
          if(FD_ISSET(i, &temp)){
    	
    	int ret = read(i, rbuf, sizeof(rbuf));
    	printf("recv:%s
    ", rbuf);
    	if(ret == 0){
    	  FD_CLR(i, &readers);
    	}
    	ret = write(i, rbuf, sizeof(rbuf));
          }
        }
      }
    
    }
    
    

    c/c++ 学习互助QQ群:877684253

    本人微信:xiaoshitou5854

  • 相关阅读:
    批量替换文本的工具
    wcf异常显示错误到客户端
    文件以二进制存入数据库和从数据库读取二进制文件
    关于关系数据库的范式
    对于挑战书上的很久之前都看不懂的DP看懂的突破
    操作系统概念
    关于P,V操作理解的突破,关于并发设计与并行
    关于快速沃尔什变换
    我觉得我应该养成经常翻收藏夹的习惯
    目前我的思考模式
  • 原文地址:https://www.cnblogs.com/xiaoshiwang/p/11110204.html
Copyright © 2011-2022 走看看