连接套接字,阻塞的套接字超时时间很长无法接受,而是用非阻塞套接字时使用的方案也有多种。后者是个比较好的方法
方案1:不断重试,直到连接上或者超时:
int connect_socket_timeout(int sockfd,char *dest_host, int port, int timeout)
{
struct sockaddr_in address;
struct in_addr inaddr;
struct hostent *host;
int err, noblock=1 , connect_ok=0, begin_time=time(NULL);
log_debug("connect_socket to %s:%dn",dest_host,port);
if (inet_aton(dest_host, &inaddr))
{
log_debug("inet_aton ok now gethostbyaddr %sn",dest_host);
memcpy(&address.sin_addr, &inaddr, sizeof(address.sin_addr));
}
else
{
log_debug("inet_aton fail now gethostbyname %s n",dest_host);
host = gethostbyname(dest_host);
if (!host) {
/* We can't find an IP number */
log_error("error looking up host %s : %dn",dest_host,errno);
return -1;
}
memcpy(&address.sin_addr, host->h_addr_list[0], sizeof(address.sin_addr));
}
address.sin_family = AF_INET;
address.sin_port = htons(port);
/* Take the first IP address associated with this hostname */
ioctl(sockfd,FIONBIO,&noblock);
/** connect until timeout */
/*
EINPROGRESS A nonblocking socket connection cannot be completed immediately.
EALREADY The socket is nonblocking and a previous connection attempt has not been completed.
EISCONN The socket is already connected.
*/
if (connect(sockfd, (struct sockaddr *) &address, sizeof(address)) < 0)
{
err = errno;
if (err != EINPROGRESS)
{
log_error("connect = %d connecting to host %sn", err,dest_host);
}
else
{
// log_notice("connect pending, return %d n", err);
while (1) /* is noblocking connect, check it until ok or timeout */
{
connect(sockfd, (struct sockaddr *) &address, sizeof(address));
err = errno;
switch (err)
{
case EISCONN: /* connect ok */
connect_ok = 1;
break;
case EALREADY: /* is connecting, need to check again */
//log_info("connect again return EALREADY check again...n");
usleep(50000);
break;
default: /* failed, retry again ? */
log_error("connect fail err=%d n",err);
connect_ok = -1;
break;
}
if (connect_ok==1)
{
//log_info ("connect ok try time =%d n", (time(NULL) - begin_time) );
break;
}
if (connect_ok==-1)
{
log_notice ("connect failed try time =%d n", (time(NULL) - begin_time) );
}
if ( (timeout>0) && ((time(NULL) - begin_time)>timeout) )
{
log_notice("connect failed, timeout %d secondsn", (time(NULL) - begin_time));
break;
}
}
}
}
else /* Connect successful immediately */
{
// log_info("connect immediate success to host %sn", dest_host);
connect_ok = 1;
}
/** end of try connect */
return ((connect_ok==1)?sockfd:-1);
}
方案2:
补充关于select在异步(非阻塞)connect中的应用,刚开始搞socket编程的时候我一直都用阻塞式的connect,非阻塞connect的问题是由于当时搞proxy scan
而提出的呵呵,通过在网上与网友们的交流及查找相关FAQ,总算知道了怎么解决这一问题.同样用select可以很好地解决这一问题.大致过程是这样的:
1.将打开的socket设为非阻塞的,可以用fcntl(socket, F_SETFL, O_NDELAY)完成(有的系统用FNEDLAY也可).
2.发connect调用,这时返回-1,但是errno被设为EINPROGRESS,意即connect仍旧在进行还没有完成.
3.将打开的socket设进被监视的可写(注意不是可读)文件集合用select进行监视,如果可写,用getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int));
来得到error的值,如果为零,则connect成功.
在许多unix版本的proxyscan程序你都可以看到类似的过程,另外在solaris精华区->编程技巧中有一个通用的带超时参数的connect模块.
我们知道,缺省状态下的套接字都是阻塞方式的,这意味着一个套接口的调用不能立即完成时,进程将进入睡眠状态,并等待操作完成。对于某些应用,需要及时可控的客户响应,而阻塞的方式可能会导致一个较长的时间段内,连接没有响应。造成套接字阻塞的操作主要有recv, send, accept, connect.
下面主要以connect为例,讲讲非阻塞的connect的工作原理。当一个TCP套接字设置为非阻塞后&