1、什么是网络超时检测
我们都知道阻塞IO是进程如果有数据到来时,就会继续执行,如果没有数据到来时,就会无限制的阻塞下去,直到数据到来,比如:我在等一个人给我汇报一件事情,如果他一直没有做完,我就要一直等下去,这样就太浪费时间了,而如果我只给他10分钟时间,如果他还是没有完成,我就认为他事情没有完成,然后继续做我自己的事情,这就是超时检测,而不是一直死等,那么如果是用于在网络通信过程中进行超时检测的话,就称为网络超时检测
2、网络超时检测的必要性
1)避免进程在没有数据时无限制的阻塞
2)当设定的时间到时,进程从原操作返回继续执行
3、设置网络超时检测的方法
在网络通信中,要做到超时检测有三种方法:
1)设置socket的属性SO_RCVTIMEO
2)用select检测socket是否ready
3)设置定时器(timer),捕捉SIGALRM信号
接下来,我们分别去看怎么实现:
1)设置socket的属性SO_RCVTIMEO
使用setsockopt设置socket的属性为SO_RCVTIMEO,即接收超时
注意:如果是tcp服务器的话,会有两个套接字,一个是监听套接字一个是连接套接字,都可以进行设置
设置好了以后,定义结构体变量,设置超时时间,如下图:
2)用select检测socket是否ready
int select(int n, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *timeout);这是select函数的原型,后一个参数,就是用来设置超时的,如果后一个参数为NULL的话,也就是没有超时检测,那么select监控的文件描述符都没有准备好,select就会一直阻塞,直到有准备好的文件描述符,如要需要超时检测,后一个参数就是要设置的超时时间,一旦超时,此时select就会返回0,表示没有准备好的文件描述符,代码可参考下图:
但是要注意:我们在运行的时候发现select只有在第一次调用的时候,会阻塞等待,直 到超时,而后面”select timeout...”会不断打印,根本就不会等待,这是因为在第一次调用select函数的时候,就将tv.tv_sec的值改为0了
3)设置定时器(timer),捕捉SIGALRM信号
这种方式是想通过信号的方式,中断正在阻塞的系统调用的执行,也就是,首先在系统调用之前(比如recv函数)设置定时器,当时间到了以后,中断recv函数,然后再继续执行recv下面的操作,而不是像之前的如果没有数据可读,recv会一直阻塞
但要注意:在安装信号的时候,不能使用signal函数,因为signal默认会继续执行系统调用,也就是说当时间到了以后,又继续执行recv函数了,还是会继续阻塞,没有达到我们想要的效果,所以这个时候我们就要使用比它功能更强大的sigaction了,如下图:
下图当没有数据可读时的运行结果:
另外注意:上面的代码中SA_RESTART指的是重新执行被信号中断的系统调用,如果没有取反的话,就和signal的处理结果是一样的了