简单的几种网络超时检测

来源:互联网 发布:监控上显示无网络视频 编辑:程序博客网 时间:2024/05/16 13:43


【1】网络超时检测的定义
     在网络通信中,很多操作会使得进程阻塞

           TCP套接字中的recv/accept

           UDP套接字中的recvfrom

     超时检测的必要性
          避免进程在没有数据时无限制地阻塞
           当设定的时间到时,进程从原操作返回继续运行
【2】实质
     阻塞函数,当缓冲区或者文件里面没有数据写入,则会一直阻塞直到有数据为止。
     非阻塞,及时缓冲区或者文件里面没有数据,也会立即返回,接着往下执行 超时检测,设定一定的时间,在时间范围之内,一直阻塞等待数据的写入,如果时间到时还没有数据,则立即返回,变成非阻塞状态
      网络超时检测,当客户端连接服务器,服务器端会开辟一块空间与客户端进行通信,如果客户端在连接之后不与服务器进行通信,则会浪费服务器资源,所以使用超时 将其断开连接,节省服务器资源

【3】方法1:使用setsockopt实现网络超时检测

    int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len);
    功能:设置套接字的选项
    参数:
      socket:文件描述符
      level:协议层次
          SOL_SOCKET 套接字层次
          option_name:选项的名称(套接字层次)
          SO_RCVTIMEO 设置接收超时时间
     option_value:设置的选项的值
            struct timeval {                                                                                                
                  __kernel_time_t     tv_sec;    秒
                  __kernel_suseconds_t    tv_usec;   微秒
                };    
     option_len:value的长度
   返回值:
       成功:0
       失败:-1

使用setsockopt设置的超时时间,设置一次,永久有效。对所有和网络相关的阻塞函数有效

定义结构体变量,并设置超时检测时间5秒

struct timeval time_out;time_out.tv_sec=5;time_out.tv_usec=0;if(setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &time_out, sizeof(time_out)) < 0){  perror("fail to setsockopt");}
while(1)
{
//阻塞等待客户端的连接
   if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) < 0)     {      //printf("errno = %d\n", errno);
//超时之后返回的错误码是11。
     if(errno == 11)      {
//超时打印       printf("连接超时...\n");      }      else      {       perror("fail to accept");      }
}

  
【4】方法二:使用select函数实现网络超时检测
 
  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                                                                             __kernel_time_t     tv_sec;    秒
              __kernel_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是否在集合set里面


  该函数也是永久有效                             

fd_set readfds;
int maxfd,ret,i;
struct timeval time_out; 
//第一步清空集合
FD_ZERO(&readfds);
//第二步将需要的文件描述符添加到集合里
FD_SET(sockfd,&readfds);
maxfd=sockfd;    //当前最大描述符值
while(1)
{
  time_out.tv_sec = 5;  time_out.tv_usec = 0;
//调用select函数阻塞等待文件描述符准备就绪  if((ret = select(maxfd + 1, &tempfds, NULL, NULL, &time_out)) < 0)  {   errlog("fail to select");  }
//返回值为0时代表超时  else if(ret == 0)   {   printf("time out...\n");  }
   
}

【5】方法3:使用alarm闹钟实现网络超时检测  
  
  alarm闹钟,当设定一定个的时间之后,代码接着往下运行,当时间到达时候,会退出进程如果结合信号,当闹钟响时,会触发SIGALRM信号,执行信号处理函数,当信号处理函数执行完毕,会接着刚才的代码继续执行,这一属性,称之为自重启属性,如果想实现网络超时检测,需要关闭这一属性
 
 使用sigaction函数设置信号的行为

    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
 功能:获取或者修改一个信号的行为
 参数:
   signum:信号
   act:新的行为
     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);  没有用
       };

   oldact:旧的行为
 返回值:
   成功: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; }
while(1) {  alarm(5);   //定时5秒    //阻塞等待客户端的连接请求  if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) < 0)  {   //printf("errno = %d\n", errno);
//延时错误码为4
   if(errno == 4)   {    printf("连接超时...\n");   }   else   {    errlog("fail to accept");   }  }



原创粉丝点击