最近項目中,有個需求是檢測某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