现在的位置: 首页 > 综合 > 正文

Linux 下socket超时(connect超时/recv超时)

2013年08月22日 ⁄ 综合 ⁄ 共 3525字 ⁄ 字号 评论关闭

 connect超时:

目前各平台通用的设置socket connect超时的办法是通过select(),具体方法如下:

1.建立socket;

2.将该socket设置为非阻塞模式;

3.调用connect();

4.使用select()检查该socket描述符是否可写;

5.根据select()返回的结果判断connect()结果;

6.将socket设回阻塞模式。

 

下面给出的是我写的client程序(已经编译通过):

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#include <asm/ioctls.h>
 
#define MAXDATASIZE 4926

int detect_imap(const char *server ,char *protocol,unsigned short port)
{
    int sockfd,numbytes;
    char buf[MAXDATASIZE];
    struct hostent *he;
    struct sockaddr_in their_addr;

    if ((he = gethostbyname(server)) == NULL)
    {
        herror("gethostbyname");
        return 0; 
    } 
   
    if ((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        perror("socket");
        return 0;
    }
   
    unsigned long ul=1;
    int rm=ioctl(sockfd,FIONBIO,&ul);
    if(rm==-1)
    {
      close(sockfd);
      return 0;  
    }
   
    their_addr.sin_family = AF_INET;
    their_addr.sin_port = htons(port);
    their_addr.sin_addr = *((struct in_addr *)he->h_addr);
    bzero(&(their_addr.sin_zero), 8);
  
    if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == 0)
    {
     printf("connected/n");
    }
    if(errno!=EINPROGRESS)
    {
     perror("connect");
      printf("cannot connect:%s/n",server);
      return 0;
    }    
 
    struct timeval timeout;
    fd_set r;         
    FD_ZERO(&r);
    FD_SET(sockfd,&r);
    timeout.tv_sec=0;   
    timeout.tv_usec=100;
    int retval = select(sockfd+1,NULL,&r,NULL,&timeout);
    if(retval==-1)
    {
      perror("select");
      return 0;
    }
    else if(retval == 0)
    {
      fprintf(stderr,"timeout/n");
      return 0;
    }
    printf("%sconnected/n",server);

    unsigned long ul1=0;
    rm=ioctl(sockfd,FIONBIO,(unsigned long*)&ul1);
    if(rm==-1)
    {
      close(sockfd);
      return 0;     
    }
  
    if ((numbytes=recv(sockfd,buf,MAXDATASIZE,0)) == -1)
    {
      perror("recv");
      return 0; 
    }
   
    buf[numbytes] = '/0';
    if (0 == strncmp(buf,"* OK",4))
    {
      printf("Received: %s",buf);
      close(sockfd);
      return 1;
    }
    else
    {
      printf("Error protocol!");
      close(sockfd);
      return 0; 
    }
}

int
main(int argc,char *argv[])
{
   int i;
   if (argc!=2)
    {
       fprintf(stderr,"usage:client hostname/n");
       exit(1);
    }

   i = detect_imap(argv[1] ,argv[2],143);
   printf ("%d",i);
}

 

 

以上代码工作的很好,并且也可以通过getsockopt()获得连接发生错误的确切信息,但这总方法难免觉得有些复杂,因为要涉及到阻塞状态的解除和回置。

这里有个简单的操作方法,同样可以设置连接超时:即通过SO_SNDTIMO套节字参数让超时操作跳过select。

原因是:Linux内核源码中connect的超时参数和SO_SNDTIMO操作的参数一致。

因此,在linux平台下,可以通过connect之前设置SO_SNDTIMO来达到控制连接超时的目的。

部分修正代码如下:

    struct timeval timeo;
    socklen_t len = sizeof(timeo);
    timeo.tv_sec = overtime;

     if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len) == -1)
    {
        strcpy(reason,strerror(errno));
        perror("setsockopt");
       return 0;
    }
   
    their_addr.sin_family = AF_INET;
    their_addr.sin_port = htons(serverStruct->port);
    their_addr.sin_addr = *((struct in_addr *)he->h_addr);
    bzero(&(their_addr.sin_zero), 8);
 
    if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1)
    { 
      if (errno == EINPROGRESS)
       {
          strcpy(reason,"timeout");
          return 0;
        }  
       strcpy(reason,strerror(errno));
       perror("connect");
        return 0;
    } 

 

编译运行:gcc client_select.c -o client_select

                    ./client_select imap.21cn.com

ps:列子是对imap协议进行分析时的代码。

 

recv超时:

如果要设置接收超时,可以用setsockopt()和send超时一样,只需将如下代码添加在connect之后就可以了。

 timeout.tv_sec=0;   
    timeout.tv_usec=500;
    int result = setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout.tv_sec,sizeof(struct timeval));
    if (result < 0)
    {
      perror("setsockopt"); 
    }

【上篇】
【下篇】

抱歉!评论已关闭.