函数说明
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
返回值说明:
成功:return > 0
超时:reutrn == 0
失败:return == -1
参数说明:
nfds: 最大监控套接字描述符加1
readfds|writefds|exceptfds: 需要监控可读|可写|异常文件描述符集合
timeout: 超时时间,为NULL则阻塞,时间为0则轮询检查文件描述符后立即返回
理解select
要理解select,必须清楚文件描述符集合在select中的变化及作用。
操作文件描述符集合的函数,以宏定义方式定义,如下:
删除:void FD_CLR(int fd, fd_set *set);
检测:int FD_ISSET(int fd, fd_set *set);
添加:void FD_SET(int fd, fd_set *set);
清空:void FD_ZERO(fd_set *set);
举例说明:
fd_set readfds;
FD_ZERO(&readfds); 表示清空文件描述符集合
FD_SET(1, &readfds); 表示添加文件描述符1到集合里面
FD_CLR(1, &readfds); 表示删除文件描述符1从集合里面
FD_ISSET(1, &readfds); 表示判断描述符1是否在集合里面
文件描述符集合变量说明,此变量类似于以下定义:
typedef long long fd_set;
fd_set变量有一块连续的内存空间,此空间访问按bit访问,如第一bit就表示描述符1,第二bit就表示2,以此类推,描述符在集合中就表示对应bit置为1,反之为0.
现在,举例说明select进行时集合变化,以集合set第一字节说明。
1)fd_set set清空,FD_ZERO(&set); set第一字节变为00000000.
2)设置fd = 5, FD_SET(fd, &set); set第一字节变化为00010000.
3)设置fd = 2, FD_SET(fd, &set); set第一字节变化为00010010.
4)执行select(6, &set, NULL, NULL, NULL)阻塞等待
5)若fd = 2发生时间,select返回,此时set第一字节变为00000010,fd = 5被清空。
通过以上步骤,可以知道,文件描述符集合在调用select是会变化的,在select返回只会返回发生事件的文件描述符。
select优缺点
优点:通过事件驱动,可以在一个线程监控多个文件描述符。
缺点:1、文件描述符有数量限制,一般linux默认1024
2、开销过大,每次调用select都会从用户态拷贝集合进入内核,并且内核是遍历所有文件描述符,开销过大
代码说明
void handle_select(int sockfd)
{
int i, result = 0, maxfd = sockfd;
int clientfds[10] = {-1};
struct timeval tv;
fd_set readfds;
listen(sockfd, 10);
tv.tv_sec = 1;
tv.tv_usec = 0;
while (1) {
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
maxfd = sockfd;
for (i = 0; i < 10; i++) {
if (clientfds[i] == -1)
continue;
FD_SET(clientfds[i], &readfds);
if (maxfd < clientfds[i])
maxfd = clientfds[i];
}
result = select(maxfd, &readfds, NULL, NULL, &tv);
if (result == 0)
continue;
if (result == -1)
break;
for (i = 0; i < 10; i++) {
if (clientfd[i] == -1)
continue;
if (FD_ISSET(clientfds[i], &readfds)) {
char buf[512] = {0};
result = read(clientfds[i], buf, 512);
if (result <= 0) {
close(clientfds[i]);
clientfds[i] = -1;
continue;
} else if (result > 0) {
printf("recv data, data = %s\n", buf);
write(clientfds[i], "hello client!", strlen("hello client!") + 1);
}
}
}
if (FD_ISSET(sockfd, &readfds)) {
struct sockaddr_in clientaddr;
bzero(&clientaddr, sizeof(clientaddr));
socklen_t clientlen = sizeof(clientaddr);
int clientfd = accept(sockfd, &clientaddr, &clientlen);
if (clientfd > 0) {
for (i = 0; i < 10; i++) {
if (clientfds[i] == -1) {
clientfds[i] = clientfd;
break;
}
}
if (i == 10) {
printf("too man clientfd!");
}
}
}
}
}