select函数的介绍和使用

我们所使用的I/O模型一共有五种。

分别为阻塞I/O,非阻塞I/O,I/O复用,信号驱动I/O,异步I/O。

所谓I/O复用就是指管理多个I/O文件描述符,一般会使用(select,poll,epoll)3个函数中的一个来实现I/O复用。

下面就介绍一下其中的select

int select(int nfds, fd_set *readfds, fd_set *writefds,

                  fd_set *exceptfds, struct timeval *timeout);

       void FD_CLR(int fd, fd_set *set);

       int  FD_ISSET(int fd, fd_set *set);

       void FD_SET(int fd, fd_set *set);

       void FD_ZERO(fd_set *set);

select的5个参数:

第一个参数是当前管理的所有文件描述符的最大值加1

因为在select中采用的是轮询的机制,输入参数是不包含的。

你可以这样理解:   for(fd = 0; fd<maxfd+1 ; fd++)

[顺带一提: 文件描述符0是stdin,1是stdout,2是stderr]

第二个参数是读集合

你要管理哪些文件描述符的读的事件,就把它放到fd_set *readfds结构体中(用FD_SET()函数);

第三个参数是写集合

同样,要管理哪些文件描述符的写的事件,就把它放到fd_set *readfds结构体中(用FD_SET()函数);

第四个参数是异常集合

就是监视异常事件的发生

第五个参数是超时时间的设置

struct timeval {

 long tv_sec; 秒

 long tv_usec; 毫毛
}

不要忽略这也是一个输入输出参数(即该参数会被函数改变),如果是一个循环,那么每次都要重新赋值该参数。

注意:select中除了第一个参数都是输入输出参数,即放在这几个位置的值都会被改变

读,写,异常集合输入参数是要监视的集合,使用完select后会变成有事件发生的事件集合,而timeout会被select函数减少

第1个参数必须设置正确(在window系统中可以不设置,但在linux系统中必须设置)

第2,3,4个参数若设置为NULL,表示不关心任何事件,若不关心其中的一种事件,可将其设置为NULL。

顺带提一下[int fileno(FILE *stream);],这个函数可以用来取得参数stream指定的文件流所使用的文件描述符。

用select函数实现并发服务器并发数量受两方面限制

1.最大文件描述符(系统参数)

可在LINUX终端直接输入 ulimit -n 查看,用 ulimit -n [数值] 改变参数

或者在程序中用setrlimit 函数改变(注意:setrlimit 函数只能改变当前进程的最大文件描述符)

2.select中的fd_set集合容量FD_SETSIZE,不容易改变,需要重新编译内核

下面是select实现的(可实现多个客户端连接)回射服务器代码

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <sys/socket.h>
  4 #include <string.h>
  5 #include <sys/types.h>
  6 #include <unistd.h>
  7 #include <errno.h>
  8 #include <arpa/inet.h>
  9 #include <netinet/in.h>
 10 #include <sys/select.h>
 11 #include <sys/time.h>
 12 #include <sys/types.h>
 13 
 14 
 15 void myerr(char *m)
 16 {
 17     perror(m);
 18     exit(0);
 19 }
 20 
 21 int count = 0;
 22 int main()
 23 {
 24     int listenfd;
 25     if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
 26         myerr("socket error");
 27 
 28     int on = 1;
 29         if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
 30                 myerr("setsockopt error");
 31 
 32     
 33     struct sockaddr_in servaddr;
 34     memset(&servaddr, 0, sizeof(servaddr));
 35     servaddr.sin_port = htons(5188);
 36     servaddr.sin_family = AF_INET;
 37     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 38     
 39     if(bind(listenfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) < 0)
 40         myerr("bind error");
 41     
 42     if(listen(listenfd, SOMAXCONN) < 0)
 43         myerr("listen error");
 44     
 45     int i, maxi=0, conn, ret, maxfd, nready;
 46     char recvbuf[1024] = "";
 47     struct sockaddr_in peeraddr;
 48     int connfd[FD_SETSIZE];
 49     for(i=0; i<FD_SETSIZE; i++)
 50         connfd[i] = -1;
 51     socklen_t peerlen = sizeof(peeraddr);
 52 
 53     maxfd = listenfd;
 54     fd_set allset, rset;
 55     FD_ZERO(&allset);
 56     FD_ZERO(&rset);
 57     FD_SET(listenfd, &allset);
 58 
 59     for(;;)
 60     {
 61         rset = allset;
 62         sleep(1);
 63         nready = select(maxfd+1, &rset, NULL, NULL, NULL);
 64         if(nready == -1)
 65         {
 66             if(errno == EINTR)
 67                 continue;
 68             myerr("select");
 69         }
 70         if(FD_ISSET(listenfd, &rset))
 71         {
 72             if((conn = accept(listenfd, (struct sockaddr *)&peeraddr, &peerlen)) < 0)
 73                 myerr("accept error");
 74             for(i=0; i<FD_SETSIZE; i++)
 75             {
 76                 if(connfd[i] == -1)
 77                 {
 78                     connfd[i] = conn;
 79                     if(i > maxi)
 80                         maxi = i;
 81                     break;
 82                 }
 83             }
 84         //    count++;
 85         //    printf("%d
", count);
 86             if(i == FD_SETSIZE)
 87             {
 88                 fprintf(stderr, "too many clients
");
 89                 exit(1);
 90             }
 91             printf("ip: %s  port: %d
", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
 92             FD_SET(conn, &allset);
 93             if(conn > maxfd)
 94                 maxfd = conn;
 95             if(--nready <= 0)
 96                 continue;
 97         }
 98         for(i=0; i<=maxi; i++)
 99         {
100             if(connfd[i] == -1)
101                 continue;
102             conn = connfd[i];
103 
104             if(FD_ISSET(conn, &rset))
105             {
106                 memset(recvbuf, 0, sizeof(recvbuf));
107                 ret = read(conn, recvbuf, sizeof(recvbuf));
108                 if(ret == 0)
109                 {
110                     printf("client close
");
111                     close(conn);
112                     connfd[i] = -1;
113                     while(connfd[maxi] == -1)
114                     {
115                         maxi --;
116                         if(maxi == 0)
117                             break;
118                     }
119                     FD_CLR(conn, &allset);
120                     continue;
121                 }
122                 write(conn, recvbuf, ret);
123                 fputs(recvbuf, stdout);
124                 if(--nready <= 0)
125                     break;
126             }
127         }
128     }
129     close(listenfd);
130     return 0;
131 }
原文地址:https://www.cnblogs.com/kamicoder/p/6418034.html