关于select函数中timeval和fd_s重新设置的问题

select原型: int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

 

select模型紧密结合的四个宏:

FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);


理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8fd
1)执行fd_set set; FD_ZERO(&set);set用位表示是0000,0000
2)若fd5,执行FD_SET(fd,&set);set变为0001,0000(5位置为1)
3)若再加入fd2fd=1,set变为
0001,0011
4)执行select(6,&set,0,0,0)阻塞等待

5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011注意:没有事件发生的fd=5被清空

基于上面的讨论,可以轻松得出select模型的特点:
1)可监控的文件描述符个数取决与sizeof(fd_set)的值。我这边服务器上sizeof(fd_set)512,每bit表示一个文件描述符,则我服务器上支持的最大文件描述符是512*8=4096。对调整fd_set的大小可参考http://www.cppblog.com/CppExplore/archive/2008/03/21/45061.html中的模型2,可以有效突破select可监控的文件描述符上限。
2)将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。
3)可见select模型必须在select前循环array(加fd,取maxfd),select返回后循环arrayFD_ISSET判断是否有事件发生)。

 

另外,如果select调用中设置了等待时间,那么每次调用时都需要重新对这个时间赋值么?就像对fd_set处理一样。
例如:
fd_set readfd;
struct timval tv;
while(1) {
  FD_ZERO(&readfd);
  FD_SET(fd, &readfd);
  tv.tv_sec = 2;
  tv.tv_usec = 0;
  select(maxfd+1, &readfd, NULL, NULL, &tv);
  ......;
}

如上代码,对fd_set需要每次调用都要重新设置,那么对tv来说是否也是一样呢?能不能把对tv的赋值放在while外面?

答案是不行,如果将时间的初始化放在外边,时间初始化为2秒,假设在1秒后发上了事件,则select将会返回并将tv的时间变成上次阻塞的剩余时间,即1秒,然后再进行监视套接字。这是因为linux系统对select()的实现中会修改参数tv为剩余时间。所以对于select函数中的最后一个参数,需要在循环中设置,每次循环要重新设置。如果设在循环外面,当循环执行起来后,每次循环select都会修改tv的值,tv的值越来越小,导致最后会产生select函数这tv时间内收不到有效时间,而返回-1,造成错误。

 

在 socket 编程select 函数是一个非常重要的函数,它可以用于实现 I/O 多路复用,即同时监视多个文件描述符的可读、可写和错误事件。select 函数的原型如下: ```c int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ``` 其,`nfds` 是需要监视的文件描述符最大的文件描述符加一;`readfds`、`writefds` 和 `exceptfds` 分别是需要监视的文件描述符集合,用于指定需要监视的文件描述符是否可读、可写或出现错误;`timeout` 是指定 select 函数的超时时间,如果为 NULL,则 select 函数将一直阻塞直到有事件发生。 当 select 函数返回时,可以通过检查 `readfds`、`writefds` 和 `exceptfds` 来确定哪些文件描述符上发生了可读、可写或错误事件。 select 函数的返回值表示就绪文件描述符的数量,如果超时则返回 0,如果出错则返回 -1。 需要注意的是,select 函数的参数的类型是 fd_set 结构体指针,而不是数组。fd_set 结构体是一个位图,每一位对应一个文件描述符,通过 FD_SET、FD_CLR、FD_ISSET 和 FD_ZERO 几个宏定义来操作 fd_set 结构体。在使用前需要调用 FD_ZERO 将 fd_set 结构体清零。 select 函数的使用可以参考以下伪代码: ```c fd_set read_fds; FD_ZERO(&read_fds); FD_SET(sock_fd, &read_fds); int max_fd = sock_fd + 1; while (1) { fd_set tmp_fds = read_fds; int ready_fd_cnt = select(max_fd, &tmp_fds, NULL, NULL, NULL); if (ready_fd_cnt < 0) { perror("select error"); break; } if (FD_ISSET(sock_fd, &tmp_fds)) { // 处理可读事件 // ... } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值