connect socket的超時設置

最近項目中,有個需求是檢測某ip地址是不是通的,使用了socket的connect函數。可是,當ip地址寫錯的話,connect就會一直阻塞在那裏,大概二、3分鐘才能返回鏈接失敗。這對於用戶來講是不可接受的。下面的文章介紹了兩種方法實現這種超時設置:linux

轉自http://blog.csdn.net/ast_224/article/details/2957294編程

 connect超時:socket

目前各平臺通用的設置socket connect超時的辦法是經過select(),具體方法以下:函數

1.創建socket;
2.將該socket設置爲非阻塞模式;
3.調用connect();
4.使用select()檢查該socket描述符是否可寫;
5.根據select()返回的結果判斷connect()結果;
6.將socket設回阻塞模式。

 

    //設socket爲非阻塞
    unsigned long ul=1;
    int rm=ioctl(sockfd,FIONBIO,&ul);
    if(rm==-1)
    {
      close(sockfd);
      return 0;  
    }

    if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == 0)
    {
     printf("connected/n");//正常鏈接(小小:使用gdb調試時,歷來走不到這一步"設置爲非阻塞,connect調用後,不管鏈接是否創建當即返回-1")
    }
    if(errno!=EINPROGRESS)//若errno不是EINPROGRESS,則出錯(EINPROGRESS:以非阻塞的方式來進行鏈接的時候,返回的結果若是是 -1,這並不表明此次鏈接發生了錯誤,若是它的返回結果是 EINPROGRESS,那麼就表明鏈接還在進行中)
    {
     perror("connect");
     printf("cannot connect:%s/n",server);
     return 0;
    }    
    //使用select設置超時
    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);
    //將socket設置回正常的阻塞模式
    ul1=0;
    rm=ioctl(sockfd,FIONBIO,(unsigned long*)&ul1);
    if(rm==-1)
    {
      close(sockfd);
      return 0;     
    }

 

以上代碼工做的很好,而且也能夠經過getsockopt()得到鏈接發生錯誤的確切信息,但這總方法不免以爲有些複雜,由於要涉及到阻塞狀態的解除和回置。spa

這裏有個簡單的操做方法,一樣能夠設置鏈接超時:即經過SO_SNDTIMO套節字參數。.net

緣由是:Linux內核源碼中connect的超時參數和SO_SNDTIMO操做的參數一致。調試

所以,在linux平臺下,能夠經過connect以前設置SO_SNDTIMO來達到控制鏈接超時的目的。code

    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;
    }  

 

小小:上述兩種實現方式均可以達到目的。第二種比較簡單,利用直接修改connect內部實現用到的參數。可是這種方式可能會產生其餘問題吧?(我不肯定),可能會影響到其餘socket編程接口的超時設定。根據SO_SNDTIMO字面意思來看,是send time out的意思,感受可能會影響某些發送數據的函數,好比sendserver

相關文章
相關標籤/搜索