第十七篇:IO复用之select实现
前言
在看过前文:初探IO复用后,想必你已对IO复用这个概念有了初步但清晰的认识。
接下来,我要在一个具体的并发客户端中实现它(基于select函数),使得一旦服务器中的客户进程被终止的时候,客户端这边立即得到通知并返回异常。
select函数
函数原型:int select ( int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout )
包含头文件:sys/select.h sys/time.h ( 两个都要包含 )
参数说明:maxfdp1表示监听描述符个数( 一般直接赋予最大描述符号的值+1 ),中间几个参数表示具体的监听描述符集( 读/写/异常描述符集,本质是位向量组 ),最后一个参数表示监听的时间限制结构体。
配套使用宏:
1. void FD_ZERO ( fd_set *fdset ) 监听描述符集清零
2. void FD_SET ( int fd, fd_set *fset ) 注册要监听的描述符
3. void FD_CLR ( int fd, fd_set *fdset ) 注销不要监听的描述符
4. int FD_ISSET ( int fd, fd_set *fdset ) 判断某个监听信号是否接收到( 一般用于在select函数调用后某个信号是否接收到判断 )
返回值:返回已经就绪的描述符的个数,如果是定时器到时则返回0,出错则返回-1。
大致用法:
1. 定义一个空的描述符集
以下部分循环:
2. 注册要监听的描述符
3. 调用select函数
4. 依次编写IO处理代码( 一般是if ( FD_ISSET(描述符), &监听描述符集 ) { } 这样的结构 )。
代码实现
下面的代码对之前的客户端代码做了修改,增加了基于select函数的IO复用机制:
1 #include "unp.h" 2 3 void 4 str_cli(FILE *fp, int sockfd) 5 { 6 int maxfdp1; 7 // 定义描述符集 8 fd_set rset; 9 char sendline[MAXLINE], recvline[MAXLINE]; 10 11 // 清空描述符集 12 FD_ZERO(&rset); 13 for ( ; ; ) { 14 // 注册要监听的两个描述符 15 FD_SET(fileno(fp), &rset); 16 FD_SET(sockfd, &rset); 17 // 计算最大描述符并将它+1后作为select函数的第一个参数 18 maxfdp1 = max(fileno(fp), sockfd) + 1; 19 // 调用并阻塞于select函数 20 Select(maxfdp1, &rset, NULL, NULL, NULL); 21 22 // 读取回射以及处理TCP分节 23 if (FD_ISSET(sockfd, &rset)) { 24 if (Readline(sockfd, recvline, MAXLINE) == 0) 25 err_quit("str_cli: server terminated prematurely"); 26 Fputs(recvline, stdout); 27 } 28 29 // 处理用户输入 30 if (FD_ISSET(fileno(fp), &rset)) { 31 if (Fgets(sendline, MAXLINE, fp) == NULL) 32 return; 33 Writen(sockfd, sendline, strlen(sendline)); 34 } 35 } 36 }
运行测试
经过测试,发现当服务器杀死客户端子进程后,客户端这边立刻报错并退出了程序。