select函数的阻塞和非阻塞态理解(实践总结)

1、select函数的阻塞和非阻塞

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 

返回值: 

<0:select错误

>0:有可读写或出错文件,个数为返回值个数

=0:等待超时,没有可读写或错误的文件 

struct timeval
{
       time_t tv_sec;
       time_t tv_usec;
};

select函数的阻塞和非阻塞主要看最后一个参数 timeout超时时间的值,timeout的取值决定了select的状态:

        1、timeout传入NULL,则select为阻塞状态,即需要等到监视文件描述符集合中某个文件描述符发生变化才会返回;

               ----------相当于无穷大的时间,一直等

         2、timeout置为0秒、0微秒,则select为非阻塞状态,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;

         3、timeout置为大于0的值,即等待的超时时间,select在timeout时间内阻塞,超时时间之内有事件到来就返回,否则在超时后不管怎样一定返回,返回值同上述。 

           -----------select函数在使用的时候,我们一般都会设置timeout时间,比如为3s,此时的select函数最多阻塞timeout时长的时间。

扫描二维码关注公众号,回复: 14490247 查看本文章

小结:在select函数的使用中,我们一般都会设置阻塞时长,使其在一定时间内处于阻塞状态。

ps:阻塞是指调用select函数的线程或进程处于sleep状态,此时不占用CPU。

2、select函数的作用

select是多路I/O复用的一种机制;调用select函数后,内核可以同时监视多路I/O的的读、写、异常事件的发生;以监视所有socket的读事件为例,当有>=1个socket有监视的事件发生时,select就会返回一个正值,此时调用FD_ISSET(int ,fd_set* );判断是哪个socket有事件发生。

小结:如果与套接字相关联的两个缓冲区中有读的数据或者写的空间,那么select会返回给进程相应的消息。

也就是说,检测到输入缓冲区有一个字节,select也会返回,select并不知道recv的buff长度,所以,当select返回正值时,recv不会阻塞,肯定有读的内容,但长度不定。。(参考select与阻塞和非阻塞_rheostat的专栏-CSDN博客_select非阻塞)---》跟这篇文章观点有冲突。

或参考该文

注意:

1)select监视多个socket时候,一般要将所有socket都要设置成非阻塞态,因为倘若有一个socket是阻塞态的话,以recv为例,线程就会阻塞在该socket的recv上,而耽误其他socket的读操作。

上述观点是错误的,只要select返回了,说明该fd是可读的,即使是阻塞的recv读完也会立即返回。

2)select函数也可以只监视一个socket的读操作,此时socket设置为阻塞态、非阻塞态都可以,此时使用select意义不大,select的作用可以理解为监视端口是否可用

3)select函数与一个阻塞态socket连用的时候,假设监视该socket的读事件,当select返回1时,表示socket有读事件发生,此时调用recv函数,recv函数也有可能发生阻塞,i)比如输入缓冲区中的数据少于要读取的buff长度,recv会阻塞,直到读到需要的所有字节  ii)比如,recv正常接收数据之后,接着send,但是输出缓冲区剩余空间不足,又阻塞在send函数这里了,

(参考:https://blog.csdn.net/u012566181/article/details/25150961

所以select与阻塞态socket连用时,select返回正值时,recv函数和send函数还是有可能会发生阻塞

所以,此时应该将socket设置为非阻塞的,当出现如上两种i)、ii)情况的时候,recv或者send函数就不会阻塞,而是返回-1,errno错误为 EAGAIN (EWOULDBLCOK)

4)select函数与一个非阻塞态的socket连用的时候,select可以起到监视端口是否可用的作用,如果端口不可用,直接返回错误信息,而不需要再去recv,或者send了;此外select的timeout参数赋正值,也可以起到sleep休眠线程,释放cpu的作用。

所以,select函数到底用不用?怎么用?与socket阻塞和非阻塞怎么搭配使用?参考该文

1)select函数与阻塞的socket和非阻塞的socket都能搭配使用。

2)当只有一个socket时,比如客户端程序,此时我们使用阻塞、非阻塞都可以,推荐使用默认的阻塞方式,fd不可读的时候阻塞住,不消耗CPU,阻塞的话,需要死循环判断,长时间占用cpu.

3)当有多个sokcet时,可以采用两种方式,一种是给每个fd都起一个线程,大家互不影响,阻塞住自己的线程就行。另一种方法就是配合select函数,select函数是多路IO复用, FD_ISSET(socket, &fd_read);进行各自的判断。因为select只要返回正值了,就说明有fd可读了(假设监视的读fd),各自也互补影响。其实,仔细想想,select函数就是多个顺序执行的阻塞socket整合到一起,只有返回的socket才会执行。

如果多个fd,全是阻塞的,且直接顺序执行,一个阻塞住就影响其他的fd读取,这种方法肯定不行。

如果全是非阻塞的,顺序执行,就还是需要while循环不停的遍历,消耗CPU。

4)当只有一个socket的时候,我们也会与select配合使用:至少可以控制阻塞时间。。。

socket与select配合使用时,socket设置为阻塞还是非阻塞???????
该文

该文、、、

使用epoll时需要将socket设为非阻塞吗? - 云+社区 - 腾讯云(这篇可以重点看)

说有必要设置为非阻塞的。。。。

非阻塞connect对于select时应注意的问题 | 易学教程https://www.e-learn.cn/topic/1105930

【win网络编程】socket中的recv阻塞和select的用法_kikilizhm的博客-CSDN博客_recv select转载请注明出处:作者 kikilizhm在编写ftp客户端程序时,在联通后使用recv函数进行接收欢迎信息时,需要申请内存进行接收数据保存,一次读取成功,但是由于一个随机的ftp服务端在说,欢迎信息的大小是不知道的,所以在尝试使用死循环,在阅读recv的说明时讲到返回值即是接收到的字节数,那么返回0的时候就代表结束了,实践发现recv是个阻塞函数,在连接不断开的情况下,会一直处于阻塞状https://blog.csdn.net/kikilizhm/article/details/8201512?spm=1001.2101.3001.6650.10&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~default-10-8201512-blog-7519417.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~default-10-8201512-blog-7519417.pc_relevant_aa&utm_relevant_index=13

这篇实验也很有参考价值。 

https://max.book118.com/html/2018/0608/171553707.shtmhttps://max.book118.com/html/2018/0608/171553707.shtm

3、附录,select函数返回-1,errno含义:

EBADF 文件描述词为无效的或该文件已关闭
EINTR 此调用被信号所中断
EINVAL 参数n 为负值。
ENOMEM 核心内存不足

猜你喜欢

转载自blog.csdn.net/modi000/article/details/106764595#comments_22760981