zoukankan      html  css  js  c++  java
  • I/O复用-select模型

    IO复用:

    I/O复用使得程序可以同时监听多个文件描述符,这对提高程序的性能至关重要。例如TCP服务器要同时处理监听socket和连接socket,客户端要同时处理用户输入和网络连接.

    Linux下实现I/O复用的系统调用主要有select、poll和epoll.

    select函数:

    #include <sys/select.h>
     int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* excepefds,struct timeval* timeout);

     函数参数介绍:

      (1)nfds:指定被监听的文件描述符的总数.它通常被设置为select监听的所有的文件描述中的最大值加1.因为文件描述符是从0开始的.

      (2)readfds、writefd和exceptfds参数:指向可读、可写和异常等事件对应的文件描述符集合.select调用返回时,内核将修改它们来通知应用程序哪些文件描述符已经就绪.(fd_set结构体仅包含一个整型数组,该数组的每个元素的每一位(bit)都标记一个文件描述符.fd_set能够容纳的文件描述符数量是由FD_SETSIZE来指定,这就限制了select能同时处理的文件描述符的总量).

      因为位操作比较繁琐,所以使用下列宏来实现:

       FD_ZERO(fd_set *fdset); //清除fdset的所有位

       FD_SET(int fd,fd_set *fdset);  //设置fdset的位

       FD_CLR(int fd,fd_set *fd_set);  //清除fdset的位fd

       int FD_ISSET(int fd,fd_set *fd_set);//判断fdset的位fd是否被设置

      (3)timeout参数:被用来设置select函数的超时时间.使用指针参数是因为内核将修改它以告诉用户select等了多久.

       struct timeval结构体定义:

    struct timeval{
        long tv_sec;/*秒数*/
        long tv_usec; /*微秒*/
    };
    • timeout变量的tv_sec和tv_usec成员都被设为0,则select将立即返回.
    • timeout=NULL:select将一直阻塞,直到某个文件描述符就绪.

    返回值:>0  成功时返回就绪(可读、可写和异常)文件描述符的总数.

           =0   在超时时间内没有任何文件描述符就绪.

           =-1    失败时,同时并设置errno.

    例子:利用select接受普通数据和带外数据都将使select返回.但socket处于不同的就绪状态:前者处于可读状态,后者处于异常状态.

    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <fcntl.h>
    
    int main(int argc,const char* argv[]){
          if(argc<=2){
              printf("usage:%s ip port
    ",argv[0]);
              return -1;
          }
          
          const char* ip=argv[1];
          int port=atoi(argv[2]);
          
          struct sockaddr_in address;
          address.sin_family=AF_INET;
          inet_pton(AF_INET,ip,&address.sin_addr);
          address.sin_port=htons(port);
          
          int sockfd=socket(AF_INET,SOCK_STREAM,0);
          assert(sockfd!=-1);
          
          int ret=bind(sockfd,(struct sockaddr*)&address,sizeof(address));
          assert(ret!=-1);
          
          ret=listen(sockfd,5);
          assert(ret!=-1);
          
          struct sockaddr_in client_address;
          socklen_t len=sizeof(client_address);
          int connfd=accept(listenfd,(struct sockaddr*)&client_address,&len);
          assert(connfd>=0);
          
          fd_set readSet;
          fd_set exceptionSet;
          FD_ZERO(&readSet);
          FD_ZERO(&exceptionSet);
          char buf[1024];
          
          while(1){
               memset(buf,'',1024);
               
               FD_SET(connfd,&readSet);
               FD_SET(connfd,&exceptionSet);
               
               ret=select(connfd+1,&readSet,NULL,&exceptionSet,NULL);
               if(ret<0){
                      printf("select error
    ");
                      break;
               }
              
              if(FD_ISSET(connfd,&readSet)){
                  ret=recv(connfd,buf,sizeof(buf),0);
                  if(ret<=0){
                      break;
                  }
                  
                  printf("recv data:%s and length:%d
    ",buf,ret);
              }
              
              else if(FD_ISSET(connfd,&exceptionSet)){
                  ret=recv(connfd,buf,sizeof(buf),MSG_OOB);
                  if(ret<=0){
                      break;
                  }
                  
                  printf("recv oob data:%s and length:%d
    ",buf,ret);
              }
          }
          
          close(connfd);
          close(sockfd);
          return 0;
    }
  • 相关阅读:
    Linux libcurl使用 (收藏)
    公钥和私钥与认证和签名
    fedora下配置ipv6 dns服务器
    SHA1
    linux IP 命令
    SSL/TLS协议簇加解密流程
    MD5算法实现原理
    container_of深入理解
    diff和patch使用指南
    oracle 笔记
  • 原文地址:https://www.cnblogs.com/sixue/p/3977347.html
Copyright © 2011-2022 走看看