第二种更通用的、使connect调用超时的方法是使套接字成为无阻塞的,然后用select等待它完成。这种方法避免了使用alarm时遇到的很多问题,但我们必须承认,即使是在UNIX实现中,这种方法还是存在很多可移植性问题。
int main(int argc, char **argv)
{
fd_set rdevents;
fd_set wrevents;
fd_set exevents;
struct sockaddr_in peer;
struct timeval tv;
SOCKET s;
int flags;
int rc;
INIT();
set_address(argv[1], argv[2], &peer, "tcp");
s=socket(AF_INET,SOCK_STREAM,0);
if(!isvalidsock(s))
error(1,errno,"socket call failed");
if((flags=fcntl(s,F_GETFL,0)) <0)
error(1,errno,"fcntl(F_GETFL) failed");
if((rc = connect(s,(struct sockaddr *)&peer,
sizeof(peer))) && errno != EINPROGRESS)
error(1,errno,"connect failed");
if(rc == 0)
{
if(fcntl(s,F_SETFL,flags) < 0)
error(1,errno, "fcntl(restore flags) failed");
client(s,&peer);
EXIT(0);
}
FD_ZERO(&rdevents);
FD_SET(s,&rdevents);
wrevents = rdevents;
exevents = rdevents;
tv.tv_sec = 5;
tv.tv_usec = 0;
rc = select(s+1, &rdwvents, &wrevents, &exevents, &tv);
if(rc < 0)
error(1,errno, "select failed");
else if(rc == 0)
error(1,0, "connect timed out\n");
else if(isconnected(s,&rdevents, &wrevents, &exevents))
{
if(fcntl(s, F_SETFL, flags) < 0)
error(1 errno, "fcntl(restore flags) failed");
client(s,&peer);
}
else
error(1,errno, "connect failed");
EXIT(0);
}
int isconnected(SOCKET s,fd_set *rd, fd_set *wr, fd_set *ex)
{
int err;
int len=sizeof(err);
errno = 0;
if(!FD_ISSET(s, rd) && !FD_ISSET(s, wr))
return 0;
if(getsockopt(s,SOL_SOCKET,SO_ERROR,&err,&len) < 0)
return 0;
errno = err;
return err == 0;
}