函数申明
int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds, struct timeval *restrict timeout);
参数
- nfds 最大文件描述符加1. (FD_SETSIZE ,大于等于系统支持的最大文件描述符number)
- readfds 检查可读性的描述符集合,函数调用后可能被覆盖为可读描述符的结果返回
- writefds 检查可写的描述符集合,函数调用后可能被覆盖为可写描述符的结果返回
- errorfds 检查异常的描述符集合
- timeout 时间结构体指针
是否阻塞
timeout 参数决定select是否阻塞
当 timeout 为 NULL 时, select 函数会一直阻塞直到监听的文件描述符发生变化.
当 timeout 为 0 时, select 函数立刻返回.
当 timeout > 0 时, select 函数阻塞特定时间返回.
返回值
对于 select 的返回值, 负值表示 select 函数执行中发生错误. 0 表示超时结束. 正值表示有可读描述符.
示例代码
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 5555
#define MAXMSG 512
int
read_from_client (int filedes)
{
char buffer[MAXMSG];
int nbytes;
nbytes = read (filedes, buffer, MAXMSG);
if (nbytes < 0)
{
/* Read error. */
perror ("read");
exit (EXIT_FAILURE);
}
else if (nbytes == 0)
/* End-of-file. */
return -1;
else
{
/* Data read. */
fprintf (stderr, "Server: got message: `%s'\n", buffer);
return 0;
}
}
int
main (void)
{
extern int make_socket (uint16_t port);
int sock;
fd_set active_fd_set, read_fd_set;
int i;
struct sockaddr_in clientname;
size_t size;
/* Create the socket and set it up to accept connections. */
sock = make_socket (PORT);
if (listen (sock, 1) < 0)
{
perror ("listen");
exit (EXIT_FAILURE);
}
/* Initialize the set of active sockets. */
FD_ZERO (&active_fd_set);
FD_SET (sock, &active_fd_set);
// 以上为常规 socket 初始化, 监听特定端口, 置空描述符集合变量 active_fd_set read_fd_set
// active_fd_set 是服务端 accept 的所有 socket 描述符, read_fd_set 是数据操作临时变量
while (1)
{
/* Block until input arrives on one or more active sockets. */
read_fd_set = active_fd_set;
// 阻塞直到 read_fd_set 中有可读的 socket 描述符号
// 覆盖 read_fd_set 变量,将可读的 socket 描述符集合赋值给 read_fd_set 变量
// 返回可读描述符集合 read_fd_set 中描述符累加的和
if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
{
perror ("select");
exit (EXIT_FAILURE);
}
/* Service all the sockets with input pending. */
// 这个循环好吓人 没用更好的办法 遍历 read_fd_set 集合?
for (i = 0; i < FD_SETSIZE; ++i)
// 判断 read_fd_set 集合中指是否存在 i
if (FD_ISSET (i, &read_fd_set))
{
// 新的 socket 请求则加入到 active_fd_set 集合中
// 已存在文件描述符则直接读
if (i == sock)
{
/* Connection request on original socket. */
int new;
size = sizeof (clientname);
new = accept (sock,
(struct sockaddr *) &clientname,
&size);
if (new < 0)
{
perror ("accept");
exit (EXIT_FAILURE);
}
fprintf (stderr,
"Server: connect from host %s, port %hd.\n",
inet_ntoa (clientname.sin_addr),
ntohs (clientname.sin_port));
FD_SET (new, &active_fd_set);
}
else
{
/* Data arriving on an already-connected socket. */
if (read_from_client (i) < 0)
{
close (i);
FD_CLR (i, &active_fd_set);
}
}
}
}
}