select函数
select()函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定时间后才唤醒它
#include <sys/select.h> #include <sys/time.h> // 返回值:若有就绪描述符,则返回就绪描述符数目;若超时则返回0,出错返回-1 int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
select的参数:
maxfdp1:
指定待测试的描述符个数,它的值是待测试的最大描述符加1
指定待测试的描述符个数,它的值是待测试的最大描述符加1
readset、writeset、exceptset:
指定让内核测试读、写、异常条件的描述符
指定让内核测试读、写、异常条件的描述符
异常条件:
某个套接字的带外数据到达
某个已置为分组模式的伪终端存在可以从其主端读取的控制状态信息
fd_set *: select使用的描述符集
系统提供了4个宏对描述符集进行操作:
系统提供了4个宏对描述符集进行操作:
#include <sys/select.h> #include <sys/time.h> void FD_SET(int fd, fd_set *fdset); // 设置文件描述符集fdset中对应于文件描述符fd的位(设置为1) void FD_CLR(int fd, fd_set *fdset); // 清除文件描述符集fdset中对应于文件描述符fd的位(设置为0) void FD_ISSET(int fd, fd_set *fdset); // 检测文件描述符集fdset中对应于文件描述符fd的位是否被设置 void FD_ZERO(fd_set *fdset); // 清除文件描述符集fdset中的所有位(既把所有位都设置为0
注意:
在使用FD_ISSET测试fd_set数据类型中的描述符后,描述符集内任何与未就绪描述符对应
的位返回时均被清0,因此,每次重新调用select函数时,都需要将描述符集内所关心的位置为1
描述符必须被初始化,因为作为自动变量分配的一个描述符如果没有被初始化,那么发生的后果
不可预期
readset、writeset、exceptset三个参数中,若对其中任何参数条件不感兴趣,则可将其设为NULL
timeout:
告知内核等待指定描述符中的任何一个就绪的时间限制
timeval结构:
a.设为空指针:永远等待下去,仅在有描述符就绪时才返回
b.正常设置timeout,在不超过timeout设置的时间内,在有描述符就绪时返回
c.将timeout.tv_sec和timeout.tv_usec都设为0:检查描述符后立即返回(轮询)
timeval结构:
struct timeval{ long tv_sec; // 秒 long tv_usec; // 微秒 }
timeout参数的三种可能:
a.设为空指针:永远等待下去,仅在有描述符就绪时才返回
b.正常设置timeout,在不超过timeout设置的时间内,在有描述符就绪时返回
c.将timeout.tv_sec和timeout.tv_usec都设为0:检查描述符后立即返回(轮询)
a b 两种情形的等待通常会被进程在等待期间捕获的信号中断,并从信号处理函数返回,因此在
考虑可移植性的情况下,若我们在捕获信号,那必须做好select返回EINTR错误的准备
描述符的就绪条件:
可读条件:
1.该套接字接收缓冲区中的数据字节数大于等于套接字接收缓冲区低水位标记的当前大小
2.该连接的读半部关闭(即接收了FIN的TCP连接)
3.该套接字是一个监听套接字且已完成的连接数不为0
4.该套接字上有一个套接字错误待处理
可读条件:
1.该套接字接收缓冲区中的数据字节数大于等于套接字接收缓冲区低水位标记的当前大小
2.该连接的读半部关闭(即接收了FIN的TCP连接)
3.该套接字是一个监听套接字且已完成的连接数不为0
4.该套接字上有一个套接字错误待处理
可写条件:
1.该套接字发送缓冲区中的可用空间字节数大于等于套接字发送缓冲区低水位标记的当前大小
2.该连接的写半部关闭
3.使用非阻塞式connect的套接字已建立连接,或者connect已经以失败告终
4.该套接字上有一个套接字错误待处理
1.该套接字发送缓冲区中的可用空间字节数大于等于套接字发送缓冲区低水位标记的当前大小
2.该连接的写半部关闭
3.使用非阻塞式connect的套接字已建立连接,或者connect已经以失败告终
4.该套接字上有一个套接字错误待处理
异常条件:
a.该套接字存在带外数据或者仍处于带外标记
a.该套接字存在带外数据或者仍处于带外标记
接受缓冲区低水位标记(读):让select返回套接字接收缓冲区所需数据量
发送缓冲区低水位标记(写):让select返回套接字发送缓冲区可用空间
发送缓冲区低水位标记(写):让select返回套接字发送缓冲区可用空间
示例:
91 // 使用select的cli_io函数,使得在服务器进程终止后客户可以马上获取通知 92 void cli_io_select(int sockfd, char *mark, FILE *fp) 93 { 94 int maxfdp1, n; 95 fd_set rset; 96 char sendline[MAXLINE], recvline[MAXLINE]; 97 98 FD_ZERO(&rset); 99 100 for ( ; ; ) 101 { 102 FD_SET(fileno(fp), &rset); 103 FD_SET(sockfd, &rset); 104 105 // fileno() 函数,将文件流指针转换为文件描述符· 106 maxfdp1 = max(fileno(fp), sockfd) + 1; 107 108 if (select(maxfdp1, &rset, NULL, NULL, NULL) < 0) 109 { 110 printf("Error select!\n"); 111 exit(1); 112 } 113 114 if (FD_ISSET(sockfd, &rset)) 115 { 116 if ( (n = read(sockfd, recvline, MAXLINE)) > 0 ) 117 { 118 recvline[n] = '\0'; 119 fputs(recvline, stdout); 120 } 121 } 122 123 if (FD_ISSET(fileno(fp), &rset)) 124 { 125 if (fgets(sendline, MAXLINE, fp) == NULL) 126 { 127 return; 128 } 129 130 if (write(sockfd, sendline, strlen(sendline)) < 0) 131 { 132 printf("Error write!\n"); 133 exit(1); 134 } 135 } 136 } 137 }
来源:https://www.cnblogs.com/lnlin/p/9607930.html