zoukankan      html  css  js  c++  java
  • Linux I/O多路转接之select函数

    I/O多路转接的基本思想:先构造一张有关描述符的表,然后调用一个函数。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。

    注意:每一个路的IO操作,尽可能的时间短,不要有while循环。

    POSIX.1标准定义了select函数,这可使得我们能够执行I/O多路转接。

    #include<sys/select.h>
    
    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    
    返回值:准备就绪的描述符数,若超时则返回0, 若出错则返回-1

    select的第一个参数nfds的意思是“最大描述符加1”。在三个描述符集中找出最大描述符编号值,然后加1,这就是第一个参数。也可设置FD_SETSIZE,即常量1024,说明最大描述符数。

    select的中间三个参数readfds、writefds和exceptfds是指向描述符集的指针。这三个描述符说明了我们关心的可读、可写或处于异常条件的各个描述符。每个描述符集存放在一个fd_set数据类型中,这种数据类型为每一个可能的描述符保持一位。

    对fd_set数据类型可以进行的处理是:分配一个这种类型的变量;将这种类型的一个变量赋予同类型的另一个变量;或对这种类型的变量使用以下四个函数中的一个:

    #include<sys/select.h>
    
    int FD_ISSET(int fd, fd_set *set);  返回值:若fd在描述符集中则返回非零值,否则返回0
    
    void FD_CLR(int fd, fd_set *set);
    
    void FD_SET(int fd, fd_set *set);
    
    void FD_ZERO(fd_set *set);

    以上这些接口可实现为宏函数。调用FD_ZERO将一个指定的fd_set变量的所有位设置为0,即清空列表;调用FD_SET设置一个fd_set变量的指定位,即向表中添加一个文件描述符;调用FD_CLR则将一指定位清除,即从表中,移除一个文件描述符;最后,调用FD_ISSET测试一指定位是否设置,即判断文件描述符是否准备就绪。

    声明了一个描述符集后,必须用FD_ZERO清除其所有位,然后设置各个位。

    如:

     1 fd_set         rset;
     2 int               fd;
     3 
     4 FD_ZERO(&rset);
     5 FD_SET(fd, &rset);
     6 FD_SET(STDIN_FILENO, &rset);
     7 
     8 if(FD_ISSET(fd, &rset)){
     9             ………………
    10 }

    当然,select的这中间三个参数中的一个或全部都可以是空指针,这表示相应状态并不关心。

    select的最后一个参数timeout,表示愿意等待的时间:

    1 struct timeval{
    2     long tv_sec;  /*seconds*/
    3     long tv_usec;/*and  microseconds*/
    4 };

    有三种情况:

    (1)timeout == NULL

    永远等待。若捕捉到一个信号则中断此等待。当所等待的描述符中的一个已经准备好或捕捉到一个信号则返回。若捕捉到一个信号,则select返回-1,errno设置为EINTR。

    (2)timeout->tv_sec == 0 && timeout->tv_usec == 0

    完全不等待。测试所有指定的描述符并立即返回。

    (3)timeout->tv_sec != 0 || timeout->tv_usec != 0

    等待指定的秒数和微秒数。当指定的描述符之一已准备好,或当指定的时间值已经超过时立即返回。若超时时还没有一个描述符准备好,则返回值为0.

    对于select有三个可能的返回值

    (1)返回值为1表示出错。如:所指定的描述符都没准备好就捕捉到一个信号。

    (2)返回值0表示没有描述符准备好。若指定的描述符都没有准备好,而且指定的时间已经超时,则发生这种情况。此时所有描述符集皆被清零。

    (3)正返回值表示已经准备好的描述符数,该数值是三个描述符集中已准备好的描述符数之和,所以若同一描述符已准备好读和写,那么再返回值中将其记为2.

    示例程序:

     server.c

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <sys/types.h>
      4 #include <sys/socket.h>
      5 #include <unistd.h>
      6 #include <fcntl.h>
      7 #include <arpa/inet.h>
      8 #include <netinet/in.h>
      9 #include <sys/select.h>
     10 #include <sys/time.h>
     11 #include <string.h>
     12 
     13 #define  N  128
     14 
     15 
     16 int main(int argc, const char *argv[])
     17 {
     18     struct sockaddr_in serveraddr, clientaddr;
     19     int sockfd;
     20     int maxfd;
     21     fd_set readfds, tmpfds;
     22     int i = 0;
     23     char buf[N] = {0};
     24     int connectfd;
     25     socklen_t len = sizeof(struct sockaddr);
     26     
     27     if(argc < 3)
     28     {
     29         fprintf(stderr,"Usage:%s serverip port.", argv[0]);
     30         return -1;
     31     }
     32 
     33     if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
     34     {
     35         perror("fail to socket");
     36         return -1;
     37     }
     38 
     39     serveraddr.sin_family = AF_INET;
     40     serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
     41     serveraddr.sin_port = htons(atoi(argv[2]));
     42 
     43     if(bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0)
     44     {
     45         perror("fail to bind");
     46         return -1;
     47     }
     48 
     49     if(listen(sockfd, 10) < 0)
     50     {
     51         perror("fail to listen");
     52         return -1;
     53     }
     54 
     55     printf("sockfd = %d
    ", sockfd);
     56     
     57     maxfd = sockfd;
     58     
     59     FD_ZERO(&readfds);
     60     FD_SET(sockfd, &readfds);
     61 
     62     while(1)
     63     {
     64         tmpfds = readfds;
     65         if(select(maxfd+1, &tmpfds, NULL, NULL, NULL) < 0)
     66         {
     67             perror("fail to select");
     68             return -1;
     69         }
     70 
     71         for(i = 0; i < maxfd+1; i++)
     72         {
     73             if(FD_ISSET(i, &tmpfds))
     74             {
     75                 if(i == sockfd) // sockfd
     76                 {
     77                     if((connectfd = accept(sockfd, (struct sockaddr *)&clientaddr, &len)) < 0)    
     78                     {
     79                         perror("fail to accept");
     80                         return -1;
     81                     }
     82                     printf("client:%s %d
    ", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
     83                     
     84                     FD_SET(connectfd, &readfds);
     85                     maxfd = maxfd > connectfd ? maxfd:connectfd;
     86                 }
     87                 else  // A  B  C 
     88                 {
     89                     if(recv(i, buf, N, 0) < 0)
     90                     {
     91                         perror("fail to recv");
     92                         return -1;
     93                     }
     94                     buf[strlen(buf) -1] = '';
     95                     printf("client:%s ---> %s %d
    ", buf, inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
     96                     if(strncmp(buf, "quit", 4) == 0)
     97                     {
     98                         FD_CLR(i, &readfds);
     99                         close(i);
    100                         continue;
    101                     }
    102                     strcat(buf, "++++++++++++");
    103                     if(send(i, buf, N, 0) < 0)
    104                     {
    105                         perror("fail to send");
    106                         return -1;
    107                     }
    108                 } // connectfd
    109             } // fd_set
    110         } // for
    111 
    112     }//while
    113     
    114 
    115     close(sockfd);
    116 
    117     return 0;
    118 }

     client.c

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <sys/types.h>
     4 #include <sys/socket.h>
     5 #include <netinet/in.h>
     6 #include <arpa/inet.h>
     7 #include <errno.h>
     8 #include <string.h>
     9 #include <unistd.h>
    10 
    11 #define  LEN  128
    12 
    13 int main(int argc, const char *argv[])
    14 {
    15     
    16     int sockfd;
    17     struct sockaddr_in serveraddr;
    18     char buf[LEN];
    19 
    20     if(argc != 3)
    21     {
    22         fprintf(stderr, "Usage:%s serverip port.
    ", argv[0]);
    23         return -1;
    24     }
    25 
    26     sockfd = socket(AF_INET, SOCK_STREAM, 0);
    27     if(sockfd < 0)
    28     {
    29         perror("fail to socket");
    30     }
    31 
    32     serveraddr.sin_family = AF_INET;
    33     serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    34     serveraddr.sin_port = htons(atoi(argv[2]));
    35 
    36     if(connect(sockfd, (struct sockaddr*)&serveraddr,sizeof(struct sockaddr)) < 0)
    37     {
    38         perror("connect failed.");
    39         return -1;
    40     }
    41 
    42     while(1)
    43     {
    44 
    45         printf("input > ");
    46         fgets(buf, LEN, stdin);
    47         if(send(sockfd, buf, sizeof(buf), 0) < 0)
    48         {
    49             perror("fail to send");
    50             return -1;
    51         }
    52         if(strncmp(buf,"quit", 4) == 0)
    53         {
    54             break;
    55         }
    56         if(recv(sockfd, buf, sizeof(buf), 0) < 0)
    57         {
    58             perror("fail to recv");
    59             return -1;
    60         }
    61         printf("buf:%s
    ", buf);
    62     }
    63 
    64     close(sockfd);
    65 
    66 
    67     return 0;
    68 }
  • 相关阅读:
    在Repeater的FooterTemplate显示某列总计
    对数据库数据操作,工厂方法设计模式(Factory Method)
    网页(aspx)与用户控件(ascx)交互与逻辑处理
    使用反射把用户控件(ASCX)传至网页(ASPX)
    软件研发公司,外观设计模式(Facade)
    看菜谱点餐,迭代设计模式(Iterator)
    There is not enough space on the disk.
    站点某些网页想显示母版页内的用户控件,某些网页不想显示,怎样实现
    C#反射(Reflection)对类的属性get或set值
    MS access 数据定时导入MS SQL Server
  • 原文地址:https://www.cnblogs.com/yangziwen0709/p/5020711.html
Copyright © 2011-2022 走看看