套接字设置超时总结

来源:互联网 发布:js超链接跳转 编辑:程序博客网 时间:2024/05/17 02:44

涉及套接字I/O操作上设置超时的方法:

1.调用alarm

当一个低速调用阻塞期间捕捉到一个信号, 则该系统调用就被中断不再继续执行。 该系统调用返回出错,起errono设置为EINTR。 因为发生信号, 进程捕捉到它, 这将是一个很好的机会来唤醒阻塞的系统调用。但有一个问题就是如果该系统调为read(), 正在等待终端输入, 如果被信号中断的话, 难免会影响整个程序的正确性, 所以有些系统使这类系统调用自动重启动。就是一旦被某信号中断, 立即再启动。read是一个低速系统调用,按照posix.1标准,默认的方式是重启由信号中断的系统调用。

通过下面的实例测试发生信号时,read重启。接收到信号后,read被中断后重启,不会返回,所以"read return"不会被打印。

#include <stdio.h>#include <signal.h>#include <unistd.h>void handler(int s){printf("recieve signal\n");return;}main(){char buf[10];signal(SIGALRM, handler);alarm(5);read(0, buf, sizeof(buf));printf("read return\n");}

下面的实例通过设置SA_INTERRUPT可以让read被信号中断后不重启,立即返回。
接收到信号后,read返回,打印"read return"

#include <stdio.h>#include <signal.h>#include <unistd.h>#include <stdlib.h>void handler(int s){printf("recieve signal\n");return;}main(){int r;char buf[10];struct sigaction act;        act.sa_handler = handler;  sigemptyset(&act.sa_mask);  act.sa_flags = 0;  act.sa_flags |= SA_INTERRUPT;  //由此中断的系统调用不会自动重启if(sigaction(SIGALRM, &act, NULL) == -1)  {perror("");exit(-1);}alarm(5);read(0, buf, sizeof(buf));printf("read return\n");}

alarm在到达指定时间会产生SIGALRM信号,使I/O调用返回,但涉及信号处理,可能会干扰进程中现有的alarm调用。connect在内核中的超时时间通常位75s,下面的实例可以用于设置比75小的超时,如果大于75的超时,connect仍然是75s后超时。

void SIGALRM_handler(int s){return;}int Connect_timeout(int sockfd,const struct sockaddr *addr,socklen_t addrlen,unsigned int sec){int r;void (*func) (int);func = signal(SIGALRM, SIGALRM_handler);if (func == SIG_ERR)return -1;alarm(sec);if ((r = connect(sockfd, addr, addrlen)) == -1)  {Close(sockfd);if (errno == EINTR)errno = ETIMEDOUT;}alarm(0);signal(SIGALRM, func);return r;}


2.使用select

select函数的最后一个参数是等待描述符可读写的超时时间,更多关于select的内容见http://blog.csdn.net/aspnet_lyc/article/details/24035071

下面是一个用select实现的到达超时时间返回的函数read_timeout

#include <sys/select.h>#include <stdlib.h>#include <stdio.h>char buf[1024];int read_timeout(int fd, unsigned int sec){fd_set rset;struct timeval tv;int r, i;FD_ZERO(&rset);FD_SET(fd, &rset);tv.tv_sec = sec;tv.tv_usec = 0;r = select(fd+1, &rset, NULL, NULL, &tv);//超时if (r == 0) return r;if (r == -1)exit(-1);//可读if (FD_ISSET(fd, &rset))          r = read(fd, buf, sizeof(buf));        return r;}main(){//read(0, buf, sizeof(buf));if (read_timeout(0, 10) <= 0)printf("read_timeout timeout return\n");elseprintf("%s", buf);}


3.设置SO_RCVTIMEO或SO_SNDTIMEO套接子选项

这两个套接子选项给套接子的接收和发送设置一个超时值,通过struct timeval结构来指定要设置的超时时间。

接收超时影响5个输入函数:read、readv、recv、recvfrom和recvmsg。

发送超时影响5个输出函数:write、writev、send、sendto和sendmsg。

这个方法的优点是一次性设置,而上面的两个方法设置超时需要每次在操作前做一些工作,例如使用信号中断要设置信号处理函数并调用alarm,使用select每次都要对select的参数进行初始化和设置。值得注意的是,这两个套接子选项不能为connect设置超时。


下面展示了一个简单的客户-服务器程序,客户连接到服务器后设置SO_RCVTIMEO套接字选项并调用read,而服务器accept到连接后不会向客户发送任何数据,这将导致客户程序在10s后read超时而返回。


net.h

#ifndef MY_NET_H  #define MY_NET_H        #include <sys/types.h>        #include <sys/socket.h>  #include <stdio.h>  #include <stdlib.h>  #include <arpa/inet.h>  #include <unistd.h>  #include <time.h>  #include <string.h>  #include <sys/select.h>  #include <sys/time.h>  #include <errno.h>#include <signal.h>      #define MAXLINE 4096  #define SA struct sockaddr  #define LISTENEQ 10        /*err_quit*/  void err_quit(const char* err_string)  {  printf("%s\n", err_string);      exit(-1);  }        /*err_sys*/  void err_sys(const char* err_string)  {      perror(err_string);      exit(-1);  }            /*Socket*/  int Socket(int domain, int type, int protocol)      {      int sockfd = socket(domain, type, protocol);      if (sockfd == -1)          err_sys("socket error");      return sockfd;  }        /*Connect*/  int Connect(int sockfd,               const struct sockaddr *addr,              socklen_t addrlen)  {      int r;      if ((r = connect(sockfd, addr, addrlen)) == -1)          err_sys("connect error");      return r;  }        /*Bind*/  int Bind(int sockfd,            const struct sockaddr *addr,           socklen_t addrlen)  {      int r;      r = bind(sockfd, addr, addrlen);      if (r == -1)          err_sys("bind error");      return r;  }        /*Listen*/  int Listen(int sockfd, int backlog)  {      int r;      r = listen(sockfd, backlog);      if (r == -1)          err_sys("listen error");      return r;  }        /*Accept*/  int Accept(int sockfd,              struct sockaddr *addr,              socklen_t *addrlen)  {      int r;      r = accept(sockfd, addr, addrlen);      if (r == -1)          err_sys("accept error");      return r;  }        /*Close*/  int Close(int fd)  {      int r = close(fd);      if (r == -1)          err_sys("close error");      return r;  }        /*Read*/  int Read(int fd, void *buf, size_t count)  {      int r;      r = read(fd, buf, count);      if (r == -1)          err_sys("read error");      return r;  }        /*Write*/  int Write(int fd, const void *buf, size_t count)  {      int r;      r = write(fd, buf, count);      if (r == -1)          err_sys("write error");      return r;  }        /*Writen *返回剩余的字符 */  int Writen(int fd, const void *buf, int len)  {      int r;      const char* ptr;      int nleft = len;                if(len <= 0)          return -1;                ptr = (char*)buf;      while (1)      {          r = Write(fd, ptr, len);          nleft -= r;          ptr += r;          len -= r;                if (nleft == 0)              return 0;      }     }        /*Readn*/  int Readn(int fd, void *buf, int len)  {      int r;      char* ptr;                ptr = (char*)buf;      while (1)      {          r = Read(fd, ptr, len);          if (r == 0)              return 0;          if (len-r > 0)          {              len -= r;              ptr += r;          }          else if (len-r == 0)              return len;      }  }      /*getsockname*/int Getsockname(int sockfd, SA *addr, socklen_t* len){int r;    r = getsockname(sockfd, addr, len);    if (r == -1)    err_sys("write error");      return r;}     /*inet_pton*/int Inet_pton(int af, const char *src, void *dst){    int r;    r = inet_pton(af, src, dst);    if (r == -1)    err_sys("inet_pton error");  if (r == 0)err_sys("inet_pton error:not contain a character string rep‐resenting  a valid network address\n");return r;}/*Init_sockaddr*/int Init_sockaddr(struct sockaddr_in *stru, int protoc, char *addr, unsigned int port){if (stru == NULL || addr == NULL)return -1;if (inet_addr(addr) == -1)return -1;stru->sin_family = protoc;(stru->sin_addr).s_addr = inet_addr(addr);stru->sin_port = htons(port);return 0;}/*tcp_connect * 指定要连接到地址和端口 */int Tcp_connect(char *addr, unsigned int port){int sockfd;struct sockaddr_in saddr;int r;if (addr == NULL)return -1;sockfd = Socket(AF_INET, SOCK_STREAM, 0);Init_sockaddr(&saddr, AF_INET, addr, port);Connect(sockfd, (SA*)&saddr, sizeof(saddr));return sockfd;}/*tcp_listen * 指定要监听的地址和端口和监听排队数 */int Tcp_listen(char *addr, unsigned int port, int backlog){int sockfd;struct sockaddr_in saddr;int r;if (addr == NULL)return -1;sockfd = Socket(AF_INET, SOCK_STREAM, 0);Init_sockaddr(&saddr, AF_INET, addr, port);Bind(sockfd, (SA*)&saddr, sizeof(saddr));Listen(sockfd, backlog);return sockfd;}#endif  

客户程序

#include "net.h"main(){int sockfd;int r;struct timeval tv;char buf[1024+1];sockfd = Tcp_connect("127.0.0.1", 9999);tv.tv_sec = 10;tv.tv_usec = 0;//设置SO_RCVTIMEO套接子选项r = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));if (r == -1)exit(-1);read(sockfd, buf, sizeof(buf));printf("read timeout\n");}

服务程序

#include "net.h"main(){int sockfd, clifd;sockfd = Tcp_listen("127.0.0.1", 9999, 10);clifd = Accept(sockfd, NULL, NULL);while(1);}



0 0
原创粉丝点击