请求可以在多个套接字或多个文件之间多路复用。使用 select(3C) 进行多路复用:
#include <sys/time.h> #include <sys/types.h> #include <sys/select.h> ... fd_set readmask, writemask, exceptmask; struct timeval timeout; ... select(nfds, &readmask, &writemask, &exceptmask, &timeout);
select(3C) 的第一个参数是列表中由接下来的三个参数指向的文件描述符数。
select(3C) 的第二、第三以及第四个参数指向三组文件描述符:一组描述符用于读取,一组用于写入,一组用于接受例外情况。带外数据是唯一的例外情况。可以将其中任一指针指定为空指针。每一组都是包含长整数位掩码数组的结构。可以使用 select.h 中定义的 FD_SETSIZE 来设置数组的大小。此数组的长度足以为每个 FD_SETSIZE 文件描述符存储一个比特位。
宏 FD_SET (fd, &mask) 和 FD_CLR (fd, &mask) 分别添加和删除组 mask 中的文件描述符 fd。此组应该在使用之前设置为零,并且宏 FD_ZERO (&mask) 将清除组 mask。
select(3C) 的第五个参数用于指定超时值。如果 timeout 指针为 NULL,则会阻止 select(3C),直到可以选择描述符或收到信号为止。如果 timeout 中的字段均设置为 0,则 select(3C) 会进行轮询并立即返回。
select(3C) 例程通常返回所选的文件描述符数,或者在已超时的情况下返回零。对于错误或中断,select(3C) 例程返回 -1,并且 errno 中的错误号以及文件描述符掩码保持不变。对于成功的返回,三个组指示哪些文件描述符可以读取、写入或具有暂挂的例外情况。
使用 FD_ISSET (fd, &mask) 宏在选择掩码中测试文件描述符的状态。如果 fd 位于组 mask 中,则此宏会返回非零值。否则,此宏会返回零。针对读取组先后使用 select(3C) 和 FD_ISSET (fd, &mask) 宏,以便检查套接字上的排队连接请求。
以下示例显示如何针对可读性在侦听套接字上做出选择,以确定何时可以通过调用 accept(3SOCKET) 接受新的连接。程序将接受连接请求,读取数据,并在单个套接字上断开连接。
#include <sys/types.h> #include <sys/socket.h> #include <sys/time/h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #define TRUE 1 /* * This program uses select to check that someone is * trying to connect before calling accept. */ main() { int sock, length; struct sockaddr_in6 server; int msgsock; char buf[1024]; int rval; fd_set ready; struct timeval to; /* Open a socket and bind it as in previous examples. */ /* Start accepting connections. */ listen(sock, 5); do { FD_ZERO(&ready); FD_SET(sock, &ready); to.tv_sec = 5; to.tv_usec = 0; if (select(sock + 1, &ready, (fd_set *)0, (fd_set *)0, &to) == -1) { perror("select"); continue; } if (FD_ISSET(sock, &ready)) { msgsock = accept(sock, (struct sockaddr *)0, (int *)0); if (msgsock == -1) perror("accept"); else do { memset(buf, 0, sizeof buf); if ((rval = read(msgsock, buf, sizeof(buf))) == -1) perror("reading stream message"); else if (rval == 0) printf("Ending connection\n"); else printf("-->%s\n", buf); } while (rval > 0); close(msgsock); } else printf("Do something else\n"); } while (TRUE); exit(0); }
在早期版本的 select(3C) 例程中,其参数是指向整数的指针,而不是指向 fd_sets 的指针。在文件描述符数小于整数中的位数时,此风格的调用仍然有效。
select(3C) 例程提供一种同步多路复用方案。高级套接字主题中介绍的 SIGIO 和 SIGURG 信号提供有关输出完成、输入可用性以及例外情况的异步通知。