`
lionvp
  • 浏览: 30901 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

如何设置socket的Connect超时(linux)

 
阅读更多

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秒。


////////////////////////////////////////////////////////////////////////////////////////////////////////////
网络编程中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设置为阻塞模式(如果你的程序不需要用阻塞模式的,这步就省了,不过一般情况下都是用阻塞模式的,这样也容易管理)
如果你对网络编程很熟悉的话,其实我一说出这个过程你就知道怎么写你的程序了,下面给出我写的一段程序,仅供参考。
#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超时比起修改系统参数的那种方法的有点就在于它只用于你的程序之中而不影响系统。


////////////////////////////////////////////////////////////////////////////////////////////////////
非阻塞connect
在一个TCP套接口被设置为非阻塞之后调用connect,connect会立即返回EINPROGRESS错误,表示连接操作正在进行中,但是仍未完成;同时TCP的三路握手操作继续进行;在这之后,我们可以调用select来检查这个链接是否建立成功;非阻塞connect有三种用途:
1.我们可以在三路握手的同时做一些其它的处理.connect操作要花一个往返时间完成,而且可以是在任何地方,从几个毫秒的局域网到几百毫秒或几秒的广域网.在这段时间内我们可能有一些其他的处理想要执行;
2.可以用这种技术同时建立多个连接.在Web浏览器中很普遍;
3.由于我们使用select来等待连接的完成,因此我们可以给select设置一个时间限制,从而缩短connect的超时时间.在大多数实现中,connect的超时时间在75秒到几分钟之间.有时候应用程序想要一个更短的超时时间,使用非阻塞connect就是一种方法;
非阻塞connect听起来虽然简单,但是仍然有一些细节问题要处理:
1.即使套接口是非阻塞的,如果连接的服务器在同一台主机上,那么在调用connect建立连接时,连接通常会立即建立成功.我们必须处理这种情况;
2.源自Berkeley的实现(和Posix.1g)有两条与select和非阻塞IO相关的规则:
  A:当连接建立成功时,套接口描述符变成可写;
  B:当连接出错时,套接口描述符变成既可读又可写;
  注意:当一个套接口出错时,它会被select调用标记为既可读又可写;

非阻塞connect有这么多好处,但是处理非阻塞connect时会遇到很多可移植性问题;

处理非阻塞connect的步骤:
第一步:创建socket,返回套接口描述符;
第二步:调用fcntl把套接口描述符设置成非阻塞;
第三步:调用connect开始建立连接;
第四步:判断连接是否成功建立;
       A:如果connect返回0,表示连接简称成功(服务器可客户端在同一台机器上时就有可能发生这种情况);
       B:调用select来等待连接建立成功完成;
         如果select返回0,则表示建立连接超时;我们返回超时错误给用户,同时关闭连接,以防止三路握手操作继续进行下去;
         如果select返回大于0的值,则需要检查套接口描述符是否可读或可写;如果套接口描述符可读或可写,则我们可以通过调用getsockopt来得到套接口上待处理的错误(SO_ERROR),如果连接建立成功,这个错误值将是0,如果建立连接时遇到错误,则这个值是连接错误所对应的errno值(比如:ECONNREFUSED,ETIMEDOUT等).
"读取套接口上的错误"是遇到的第一个可移植性问题;如果出现问题,getsockopt源自Berkeley的实现是返回0,等待处理的错误在变量errno中返回;但是Solaris会让getsockopt返回-1,errno置为待处理的错误;我们对这两种情况都要处理;

这样,在处理非阻塞connect时,在不同的套接口实现的平台中存在的移植性问题,首先,有可能在调用select之前,连接就已经建立成功,而且对方的数据已经到来.在这种情况下,连接成功时套接口将既可读又可写.这和连接失败时是一样的.这个时候我们还得通过getsockopt来读取错误值;这是第二个可移植性问题;
移植性问题总结:
1.对于出错的套接口描述符,getsockopt的返回值源自Berkeley的实现是返回0,待处理的错误值存储在errno中;而源自Solaris的实现是返回0,待处理的错误存储在errno中;(套接口描述符出错时调用getsockopt的返回值不可移植)
2.有可能在调用select之前,连接就已经建立成功,而且对方的数据已经到来,在这种情况下,套接口描述符是既可读又可写;这与套接口描述符出错时是一样的;(怎样判断连接是否建立成功的条件不可移植)

这样的话,在我们判断连接是否建立成功的条件不唯一时,我们可以有以下的方法来解决这个问题:
1.调用getpeername代替getsockopt.如果调用getpeername失败,getpeername返回ENOTCONN,表示连接建立失败,我们必须以SO_ERROR调用getsockopt得到套接口描述符上的待处理错误;
2.调用read,读取长度为0字节的数据.如果read调用失败,则表示连接建立失败,而且read返回的errno指明了连接失败的原因.如果连接建立成功,read应该返回0;
3.再调用一次connect.它应该失败,如果错误errno是EISCONN,就表示套接口已经建立,而且第一次连接是成功的;否则,连接就是失败的;

被中断的connect:
如果在一个阻塞式套接口上调用connect,在TCP的三路握手操作完成之前被中断了,比如说,被捕获的信号中断,将会发生什么呢?假定connect不会自动重启,它将返回EINTR.那么,这个时候,我们就不能再调用connect等待连接建立完成了,如果再次调用connect来等待连接建立完成的话,connect将会返回错误值EADDRINUSE.在这种情况下,应该做的是调用select,就像在非阻塞式connect中所做的一样.然后,select在连接建立成功(使套接口描述符可写)或连接建立失败(使套接口描述符既可读又可写)时返回;

分享到:
评论

相关推荐

    Linux下Socket连接超时的一种实现方法

    在 Linux 环境下,设置套接字(Socket)连接超时是一件非常重要的事情。下面我们将详细介绍 Linux 下 Socket 连接超时的一种实现方法。 首先,需要创建套接字,并将其设置成非阻塞状态。这是因为在连接时,我们不...

    linux客户端Socket非阻塞connect编程[归纳].pdf

    Linux 客户端Socket 非阻塞 connect 编程详解 本文档详细介绍了 Linux 客户端 Socket 非阻塞 connect 编程的实现方法和原理。非阻塞 connect 是一种高效的网络编程技术,能够提高程序的性能和可靠性。 一、非阻塞 ...

    Linux异步通信socket

    3. **设置超时时间**:定义一个`struct timeval`类型的变量,设置`select`函数的超时时间。这决定了`select`函数在没有任何描述符就绪时的最大等待时间。 4. **调用`select`函数**:使用`select`函数开始监听描述符...

    LinuxSocket示例代码

    在IT行业中,Linux Socket是进行网络通信的重要工具,尤其对于系统和网络程序员来说,理解和掌握Linux Socket编程至关重要。本示例代码提供了客户端(client)和服务器端(server)的实现,帮助初学者深入理解如何在...

    Linux下connect超时处理(总结)

    本文将对Linux下`connect`超时处理进行总结,探讨如何实现非阻塞的`connect`以及如何通过`select`函数设置超时。 首先,理解`connect`函数的基本行为。默认情况下,`connect`是一个阻塞调用,意味着它会等待直到...

    linux客户端Socket非阻塞connect编程收集.pdf

    Linux 客户端 Socket 非阻塞 connect 编程收集 本文档主要讨论了 Linux 客户端 Socket 非阻塞 connect 编程的实现与应用,非阻塞 connect 编程的主要用途有三种: 1. 三次握手同时做其他的处理。在 connect 过程中...

    Linux Socket教程.zip

    而Linux Socket学习(十一).txt可能会讲解错误处理和异常情况,例如连接超时、中断和重传策略。 最后,Linux Socket学习(十八)--完.txt可能是对整个教程的总结,回顾了重要的概念和技巧,并可能介绍了实际项目...

    sc.rar_linux socket 例子_linux socket编程

    "sc.rar_linux socket 例子_linux socket编程" 提供了一个客户端的示例,对于初学者来说,这是一个非常有价值的资源,可以帮助理解如何在Linux环境下实现socket通信。 首先,我们需要了解什么是Socket。Socket在...

    Linux Socket

    - **sockopt()**:设置或获取Socket选项,如超时、缓冲区大小等。 - **fcntl()**:用于设置Socket的非阻塞模式、文件描述符标志等。 6. **多路复用与并发处理** - **select()**/**poll()**/**epoll()**:用于多...

    实战linux socket编程--示例源码.rar_C++ socket_linux 源码_socket 编程 linux_s

    - `setsockopt()`和`getsockopt()`函数用于设置和获取Socket的选项,这些选项可以影响Socket的行为,例如超时设置、缓冲区大小等。 - 常见的套接字级别有SOL_SOCKET(适用于所有类型套接字)和IPPROTO_TCP/UDP...

    实战Linux socket编程Linux Socket Programming By Example

    7. **套接字选项**:`setsockopt()`和`getsockopt()`函数可以设置和获取套接字的属性,如超时时间、重传策略等。 8. **关闭套接字**:使用`close()`函数关闭不再使用的套接字,释放资源。 通过"Linux Socket ...

    linux socket编程

    1. **套接字选项**:`setsockopt()`函数可以用来设置Socket的特定选项,如超时、重试次数等。 2. **多线程/多进程**:为了同时处理多个客户端连接,服务器端通常会采用多线程或多进程模型。 3. **错误处理**:网络...

    Linux环境下的Socket编程

    3. `listen()`:设置Socket为监听模式,等待客户端连接。 4. `accept()`:接受来自客户端的连接请求,返回一个新的Socket描述符。 5. `connect()`:客户端使用此函数连接服务器的Socket。 6. `send()` 和 `recv()`:...

    Linux环境下实时系统的Socket通讯.pdf

    此外,还可以通过设置Socket选项如SO_RCVTIMEO和SO_SNDTIMEO来限制操作的超时时间,进一步保证实时性。 总之,在Linux环境下,通过Berkeley Socket实现TCP/IP协议的实时通信,结合适当的编程技术和系统特性,可以...

    LINUX-socket源码

    - **连接服务器**:`socket()`创建套接字,`connect()`函数尝试连接到服务器的指定地址和端口。 - **数据收发**:同样,客户端也会使用`read()`和`write()`或`recv()`和`send()`函数来发送和接收数据。 3. **错误...

    Socket通信:Linux服务器与多个Aandroid客户端

    Android客户端需要创建`java.net.Socket`对象,通过`connect()`方法连接到服务器的IP地址和端口号。同时,为了处理多客户端连接,服务器端通常会创建一个线程池,每个新到来的连接都会在一个新的线程中处理,这样...

    linux c socket api

    `getsockopt()`和`setsockopt()`允许设置和获取套接字选项,如超时、重传策略等。 ```c int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int sockfd, int ...

    《实战 Linux Socket编程》练习代码

    6. **套接字选项和标志**:setsockopt()和getsockopt()函数允许开发者调整Socket的行为,如设置超时、禁用 Nagle 算法等。此外,send()和recv()的 flags 参数可以改变数据传输的特性,如MSG_DONTWAIT用于非阻塞I/O。...

    总结:Linux中socket常见错误分析

    在Linux系统中,Socket编程是网络通信的基础,但在这个过程中,开发者可能会遇到各种错误情况,这些错误需要正确理解和处理。本文主要围绕Linux中Socket编程的常见错误进行分析,特别是涉及连接过程、系统调用的中断...

Global site tag (gtag.js) - Google Analytics