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 }
原文地址:https://www.cnblogs.com/yangziwen0709/p/5020711.html