select函数解析
select()函数允许进程指示内核等待多个事件(文件描述符)中的任何一个发生,并只在有一个或多个事件发生或经历一段指定时 间后才唤醒它,然后接下来判断究竟是哪个文件描述符发生了事件并进行相应的处理。
头文件:
#include <sys/select.h>
#include <sys/time.h>
struct timeval
{
long tv_sec; //seconds
long tv_usec; //microseconds
};
函数集:
FD_ZERO(fd_set* fds) //清空集合
FD_SET(int fd, fd_set* fds) //将给定的描述符加入集合
FD_ISSET(int fd, fd_set* fds) //判断指定描述符是否在集合中
FD_CLR(int fd, fd_set* fds) //将给定的描述符从文件中删除
函数原型:
int select(int max_fd, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout);
说明: select监视并等待多个文件描述符的属性发生变化,它监视的属性分3类,分别是readfds(文件描述符有数据到来可读)、 writefds(文件描述符可写)、和exceptfds(文件描述符异常)。调用后select函数会阻塞,直到有描述符就绪(有数据可读、可写、 或者有错误异常),或者超时( timeout 指定等待时间)发生函数才返回。当select()函数返回后,可以通过遍历 fdset,来找到 究竟是哪些文件描述符就绪。
参数:
-
select函数的返回值是就绪描述符的数目,超时时返回0,出错返回-1;
-
第一个参数max_fd指待测试的fd的总个数,它的值是待测试的最大文件描述符加1。Linux内核从0开始到max_fd-1扫描文件描述 符,如果有数据出现事件(读、写、异常)将会返回;假设需要监测的文件描述符是8,9,10,那么Linux内核实际也要监测0~7,此时真 正带测试的文件描述符是0~10总共11个,即max(8,9,10)+1,所以第一个参数是所有要监听的文件描述符中最大的+1。
-
中间三个参数readset、writeset和exceptset指定要让内核测试读、写和异常条件的fd集合,如果不需要测试的可以设置为 NULL;
-
最后一个参数是设置select的超时时间,如果设置为NULL则永不超时;
注意:
待测试的描述集总是从0, 1, 2, …开始的。 所以, 假如你要检测的描述符为8, 9, 10, 那么系统实际也要 监测0, 1, 2, 3, 4, 5, 6, 7, 此时真正待测试的描述符的个数为11个, 也就是max(8, 9, 10) + 1
在Linux内核有个参数__FD_SETSIZE定义了每个FD_SET的句柄个数中,这也意味着select所用到的FD_SET是有限的,也正是这个 原因select()默认只能同时处理1024个客户端的连接请求:
/linux/posix_types.h:
#define __FD_SETSIZE 1024
流程图:
代码:
/* ARRAY_SIZE(x)可以确定一个数组中的元素个数 */
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
int i,j;
int fds_array[1024];/* 收录文件描述符 */
int listenfd,connfd;/* 文件描述符 */
fd_set rdset;/* 文件描述符的集合 */
int maxfd=0;
char buf[1024]; /* 客户端发来的内容(事件) */
int rv;
int found;
for(i=0; i<ARRAY_SIZE(fds_array) ; i++)
{
fds_array[i]