设置socket的Connect超时

http://blog.csdn.net/chenguangf/article/details/2107302

1.首先将标志位设为Non-blocking模式,准备在非阻塞模式下调用connect函数
2.
调用connect,正常情况下,因为TCP三次握手需要一些时间;而非阻塞调用只要不能立即完成就会返回错误,所以这里会返回EINPROGRESS,表示在建立连接但还没有完成。
3.
在读套接口描述符集(fd_set rset)和写套接口描述符集(fd_set wset)中将当前套接口置位(用FD_ZERO()FD_SET()宏),并设置好超时时间(struct timeval *timeout)
4.
调用select( socket, &rset, &wset, NULL, timeout )
返回0表示connect超时
如果你设置的超时时间大于75秒就没有必要这样做了,因为内核中对connect有超时限制就是75秒。


[From]http://www.ycgczj.com.cn/34733.html

网络编程中 socket 的分量我想大家都很清楚了, socket 也就是套接口,在套接口编程中,提到超时的概念,我们一下子就能想到 3 个:发送超时,接收超时,以及 select 超时(注: select 函数并不是只用于套接口的,但是套接口编程中用的比较多),在 connect 到目标主机的时候,这个超时是不由我们来设置的。不过正常情况下这个超时都很长,并且 connect 又是一个阻塞方法,一个主机不能连接,等着 connect 返回还能忍受,你的程序要是要试图连接多个主机,恐怕遇到多个不能连接的主机的时候,会塞得你受不了的。我也废话少说,先说说我的方法,如果你觉得你已掌握这种方法,你就不用再看下去了,如果你还不了解,我愿意与你分享。本文是已在 Linux 下的程序为例子,不过拿到 Windows 中方法也是一样,无非是换几个函数名字罢了。
  Linux 中要给 connect 设置超时,应该是有两种方法的。一种是该系统的一些参数,这个方法我不讲,因为我讲不清楚 :P ,它也不是编程实现的。另外一种方法就是变相的实现 connect 的超时,我要讲的就是这个方法,原理上是这样的:
 1
.建立 socket
 2
.将该 socket 设置为非阻塞模式
 3
.调用 connect()
 4
.使用 select() 检查该 socket 描述符是否可写(注意,是可写)
 5
.根据 select() 返回的结果判断 connect() 结果
 6
.将 socket 设置为阻塞模式(如果你的程序不需要用阻塞模式的,这步就省了,不过一般情况下都是用阻塞模式的,这样也容易管理)
如果你对网络编程很熟悉的话,其实我一说出这个过程你就知道怎么写你的程序了,下面给出我写的一段程序,仅供参考。
/******************************
* Time out for connect() 
*  Write by Kerl W
******************************/
#include <sys/socket.h>
#include <sys/types.h>
#define TIME_OUT_TIME 20 //connect 超时时间 20
int main(int argc , char **argv)
{
   ………………
   int sockfd = socket(AF_INET, SOCK_STREAM, 0);
   if(sockfd < 0) exit(1);
   struct sockaddr_in serv_addr;
   ………//
以服务器地址填充结构 serv_addr
   int error=-1, len;
   len = sizeof(int);
   timeval tm;
   fd_set set;
   unsigned long ul = 1;
   ioctl(sockfd, FIONBIO, &ul); //
设置为非阻塞模式
   bool ret = false;
   if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
   {
     tm.tv_set  = TIME_OUT_TIME;
     tm.tv_uset = 0;
     FD_ZERO(&set);
     FD_SET(sockfd, &set);
     if( select(sockfd+1, NULL, &set, NULL, &tm) > 0)
     {
       getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
       if(error == 0) ret = true;
 else ret = false;
    } else ret = false;
  }
 else ret = true;
 ul = 0;
 ioctl(sockfd, FIONBIO, &ul); //
设置为阻塞模式
 if(!ret) 
 {
  close( sockfd );
  fprintf(stderr , "Cannot Connect the server!/n");
   return;
   }
  fprintf( stderr , "Connected!/n");
  //
下面还可以进行发包收包操作
  ……………
}

 
以上代码片段,仅供参考,也是为初学者提供一些提示,主要用到的几个函数, select, ioctl, getsockopt 都可以找到相关资料,具体用法我这里就不赘述了,你只需要在 linux 中轻轻的敲一个 man < 函数名 > 就能够看到它的用法。
  此外我需要说明的几点是,虽然我们用 ioctl 把套接口设置为非阻塞模式,不过 select 本身是阻塞的,阻塞的时间就是其超时的时间由调用 select 的时候的最后一个参数 timeval 类型的变量指针指向的 timeval 结构变量来决定的, timeval 结构由一个表示秒数的和一个表示微秒数( long 类型)的成员组成,一般我们设置了秒数就行了,把微妙数设为 0 (注: 1 秒等于 100 万微秒)。而 select 函数另一个值得一提的参数就是上面我们用到的 fd_set 类型的变量指针。调用之前,这个变量里面存了要用 select 来检查的描述符,调用之后,针对上面的程序这里面是可写的描述符,我们可以用宏 FD_ISSET 来检查某个描述符是否在其中。由于我这里只有一个套接口描述符,我就没有使用 FD_ISSET 宏来检查调用 select 之后这个 sockfd 是否在 set 里面,其实是需要加上这个判断的。不过我用了 getsockopt 来检查,这样才可以判断出这个套接口是否是真的连接上了,因为我们只是变相的用 select 来检查它是否连接上了,实际上 select 检查的是它是否可写,而对于可写,是针对以下三种条件任一条件满足时都表示可写的:
1
)套接口发送缓冲区中的可用控件字节数大于等于套接口发送缓冲区低潮限度的当前值,且或者 i) 套接口已连接,或者 ii) 套接口不要求连接( UDP 方式的)
2
)连接的写这一半关闭。
3
)有一个套接口错误待处理。
这样,我们就需要用 getsockopt 函数来获取套接口目前的一些信息来判断是否真的是连接上了,没有连接上的时候还能给出发生了什么错误,当然我程序中并没有标出那么多状态,只是简单的表示可连接 / 不可连接。
  下面我来谈谈对这个程序测试的结果。我针对 3 种情形做了测试:
1
  目标机器网络正常的情况
 
可以连接到目标主机,并能成功以阻塞方式进行发包收包作业。
2   目标机器网络断开的情况
 
在等待设置的超时时间(上面的程序中为 20 秒)后,显示目标主机不能连接。
3   程序运行前断开目标机器网络,超时时间内,恢复目标机器的网络
在恢复目标主机网络连接之前,程序一只等待,恢复目标主机后,程序显示连接目标主机成功,并能成功以阻塞方式进行发包收包作业。
  以上各种情况的测试结果表明,这种设置 connect 超时的方法是完全可行的。我自己是把这种设置了超时的 connect 封装到了自己的类库,用在一套监控系统中,到目前为止,运行还算正常。这种编程实现的 connect 超时比起修改系统参数的那种方法的有点就在于它只用于你的程序之中而不影响系统。

 

你可能感兴趣的:(设置socket的Connect超时)