超时检测的必要性:避免进程在没有数据时无限制地阻塞,当设定的时间到时,进程从原操作返回继续运行。
实质
阻塞:如果缓冲区或者文件内没有数据,则会一直阻塞下去,直到有数据来为止
非阻塞:在运行函数时,会轮询的观察缓冲区或者文件内有没有数据,有数据,则正常执行,
如果没有数据,则立即返回
超时检测:设定一定的时间,在时间范围之内一直阻塞等待数据的到来,如果时间到时还没
有数据,则立即返回
网络超时检测:当客户端连接到服务器之后,服务器端就会开辟一块空间与客户端进行通信,
如果客户端长时间不与服务器通信,则浪费服务器资源,所以需要超时检测
方法一:
使用setscokopt函数实现网络超时检测
#include <sys/socket.h>
int setsockopt(int socket, int level, int option_name,
const void *option_value, socklen_t option_len);
功能:设置一个套接字的选项
参数:
sockfd:文件描述符
level:协议层次
SOL_SOCKET 套接字层次
optname:选项的名称(套接字层次)
SO_RCVTIMEO 设置接收超时时间
optval:获取到的选项的值
struct timeval
{
__time_t tv_sec; 秒
__suseconds_t tv_usec; 微秒
};
optlen:optval的长度
返回值:
成功:0
失败:-1
方法二:
使用select函数实现网络超时检测,设置select最后的一个参数:注意select调用后会清零超时结构体,需要重新设置时间
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
功能:同步一个IO操作,需要一个程序操作多个文件描述符,阻塞等待
直到有一个或者多个文件描述符准备就绪,如果有文件描述符准
备就绪,则函数立即返回,并执行相应的io操作
参数:
nfds:最大的文件描述符加1
readfds:读文件描述符集合
writefds:写文件描述符集合
exceptfds:其他或者异常的文件描述符集合
timeout:超时
struct timeval
{
__time_t tv_sec; 秒
__suseconds_t tv_usec; 微秒
};
0 立即返回
NULL 阻塞
返回值:
成功:准备就绪的文件描述符的个数
失败:-1
void FD_ZERO(fd_set *set);
清空一个集合set
void FD_SET(int fd, fd_set *set);
将文件描述符fd添加到集合set里面
void FD_CLR(int fd, fd_set *set);
将一个文件描述符fd从集合set当中移除
int FD_ISSET(int fd, fd_set *set);
判断文件描述符fd是否在集合里面
方法三:
用alarm闹钟实现网络超时检测
使用alarm设置时间,代码继续往下运行,当时间到达时,会结束整个进程,如果结合信号,当闹钟响时,会触发SIGALRM信号,处理信号处理函数,当信号处理,函数执行完毕,会接着之前的程序继续运行,这一属性称之为自重启属性,如果想实现超时,则需要关闭这一属性。
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
功能:
信号处理
参数:
signum:信号类型
act: 新的信号行为
oldact:旧的信号行为
返回值:
成功 0,失败 -1
#if 1
struct sigaction act;
sigaction(SIGALRM, NULL, &act);
//读
act.sa_handler = handler;
//改------>信号处理函数
act.sa_flags &= ~SA_RESTART;
--------->防止系统自重启
sigaction(SIGALRM, &act, NULL);//写
#endif
使用sigaction来修改一个信号的行为
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
功能:获取或者设置一个信号的行为
参数:
signum:信号
act:新的行为
oldact:旧的行为
struct sigaction {
void (*sa_handler)(int); 信号处理函数
void (*sa_sigaction)(int, siginfo_t *, void *); 信号处理函数
sigset_t sa_mask; 掩码(关于阻塞)
int sa_flags; 标志位
SA_RESTART 自重启属性
void (*sa_restorer)(void); 没有用
};
返回值:
成功:0
失败:-1
对寄存器或者位的操作,一般执行读、改、写三步
第一步:获取旧的行为
struct sigaction act;
if(sigaction(SIGALRM, NULL, &act) < 0)
{
perror("fail to sigaction");
return -1;
}
第二步:修改行为
act.sa_handler = handler;
act.sa_flags = act.sa_flags & (~SA_RESTART);
第三步:将新的行为写回去
if(sigaction(SIGALRM, &act, NULL) < 0)
{
perror("fail to sigaction");
return -1;
}