您的位置:首页 > 理论基础 > 计算机网络

长期使人困惑的问题:TCP连接中断的实时检测

a511244213 2016-03-24 17:29 45 查看
目前TCP/IP已经成为网络的主导技术。通过对TCP底层实现的分析,对TCP/IP编程中一个长期使人困惑的问题----网络连接中断的实时检测—进行深入的分析,并提出相应的解决方案。

  

  0引言

  作为现代网络的主导技术,TCP/IP编程看起来非常简单,但在经历了最初的高效率后,往往会在细节面前停滞不前,这常常是因为对TCP协议底层细节的缺乏了解所导致的。

  TCP是面向连接协议,而UDP是无连接协议,许多初学者发现可以没有任何数据流通过一个空闲的TCP连接,如果TCP连接的双方都没有向对方发送数据,则在两个TCP模块之间不交换任何信息。这意味着可以启动一个客户与服务器建立一个连接,然后离去数小时至数个星期连接依然保持。中间路由器可以崩溃和重启,电话线可以被挂断再连通,只要两端的主机没有被重启,则连接依然保持建立。

  因此,初次接触TCP/IP协议组的程序员感到很迷惑:TCP中并没有可以在其他网络协议中发现的连接阶段的轮询,甚至发现TCP不给应用程序提供既时的网络连接中断的通知。一些程序员据此断定TCP不适用于一般的应用程序到应用程序的通信。TCP为什么不提供通知呢?

  1原理分析

  TCP通常被称为可靠的协议,即“TCP保证发送数据的传输”,这通常会产生误解:TCP不会出错。事实是只要双方保持连接,TCP就能保证数据的正确传输,但是当连接中断时,就会产生问题,原因有3个:1)永久的或暂时的网络紊乱;2)对等方应用程序崩溃;3)对等方主机崩溃,当出现以上问题时,会使双方应用程序不能互相通信,而其中一个应用程序却不能立刻意识到。发送数据给对等方的应用程序可能在知道TCP在放弃重发之前才会发现连接中断,。如果应用程序没有发送数据,可能永远不会发现网络已经中断。例如应用程序可能是一个正在等待对等方发出下一个请求的服务器,因为客户端不能和服务器通信,下一个请求永远不会到达,甚至客户端的TCP放弃并撤销连接,导致客户端中断,服务器也没有意识到这一点。

  其他的通信协议如SNA和X.25,当连接中断时会给应用程序提供通知。比如简单的直接点对点专有链接复杂的任何协议都必须使用一种轮询协议来连续地测试对等方是否存在。轮询-选择协议可能会采用显式地发送“你有要发送给我的任何数据吗?”诸如此类的消息的形式,或者它们会采用后台静态帧的形式来检测虚拟线路是否仍然存在。每一个轮询消息都会消耗网络资源,而这些资源本来可以用于“有效负载”数据的传输。

  对可获得的网络带宽的消耗是TCP不提供网络中断立即通知的一个原因。因为大多数的应用程序不需要即时的通知,所以没有必要以降低带宽的代价来提供这个功能。需要以一种及时的方式知道对等方不可到达的应用程序可以实现它们自己的机制来发现网络中断,如后面介绍的那样。

  TCP/IP设计中使用的一个基本原则是终端对终端参数[Saltzerelal.1984],该参数应用到网络上时可以表述为所有的智能应当尽可能地接近连接的终端点,而网络本身应当相对没有智能。这就是为什么TCP自己处理错误控制而不是依靠网络来提供它的原因。当这个原则应用到监控对等应用程序之间的连接时,应用程序应当提供它自己需要的功能,而不是不管应用程序是否需要这个功能都由下层提供。

  TCP不提供及时连接中断通知的最重要的原因是:网络突然中断时仍可以维持通讯的能力。TCP最早是美国国防部发起的一项研究的成果,它要求提供一个遇到战争或自然灾害引起的网络中断时仍然可以维持计算机之间可靠的通信的网络协议。通常网络紊乱是暂时的,路由器也可能找到连接的另一条路径。通过允许连接的暂时中断,甚至在终端应用程序意识到中断之前TCP就已经处理好了紊乱。

  2解决方案

  2.1方案一:使用TCPKeep-alive机制

  人们希望知道连接是否中断了,因此许多TCP的具体实现提供了一个称作Keep-alive的机制用于检测死连接,但是它并不经常用于应用程序。如果应用程序启用Keep-alive机制时,TCP就会在连接已经空闲了一段时间间隔后发送一个特殊的段给对等方。如果对等方主机可到达而且对等方应用程序仍然运行,对等方TCP就会响应一个ACK应答。在这种情况下,TCP发送Keep-alive重置空闲时间为零,并且应用程序不会收到消息交换的任何通知。

  如果对等方主机可以到达但是对等方应用程序没有运行,对等方TCP就响应RST消息,发送Keep-alive消息的TCP撤销连接并返回ECONNRESET错误给应用程序。这通常是对等方主机崩溃后重起的结果,因为如果仅仅是对等方应用程序中断或崩溃了,对等方TCP可能已经发送FIN消息了。

  如果对等方主机没有响应ACK或RST消息,发送Keep-alive消息的TCP继续发送Keep-alive探询消息,直到它认为对等方不可到达或已经崩溃了。这时它就撤销连接并通知应用程序ETIMEDOUT错误,如果路由器已经返回主机或网络不可到底的ICMP消息的话,就返回EHOSTUNREACH或ENERUNREACH错误。

  通过Keep-alive机制,TCP提供了协议层面的网络中断通知功能,但这种机制有很多问题以至于很少用于应用程序。

  首先,Keep-alive并不是TCP规范中的一部分,长期以来,是否在TCP中提供Keep-alive机制一直是有争论的话题,因此,Keep-alive不是所有的TCP实现都提供,而且实现细节也有所不同。

  需要即时通知网络中断的应用程序使用Keep-alive功能的第二个问题是和时间间隔有关的。RFC1122[Braden1989]认为,如果TCP实现了Keep-alive,保活间隔必须是可配置的,但是其默认值必须不小于两个小时,之后它才能发送Keep-alive探询消息。那么因为对等方的ACK消息并不是可靠地递交,它必须在放弃连接之前重复发送探询消息。4.4BSD具体实现在撤销连接之前以75秒的时间间隔发送9个探询消息。

  这意味着BSD派生的具体实现,大约需要2小时11分钟15秒才能发现连接已经中断了。这个时间值只有在我们认识Keep-alive是用于释放被死连接占有的资源时才有意义。例如,当客户端连接到服务器而客户端主机崩溃时就有可能发生这样的连接。如果没有Keep-alive机制,服务器就会永远等待客户端的下一个请求,这是因为它永远没有接收到FIN消息。(因为基于PC系统的用户仅仅是关闭计算机和modem而不是正确地关闭应用程序,所以这种情况正越来越普遍。)

  由于2小时的时间对于实时检测几乎没有意义,因此一些具体实现允许改变一个或两个时间间隔值,但因为保活间隔时间是系统级的变量,这些值的改变影响系统上所有的TCP连接,这是Keep-alive作为一个连接监控机制没有实际使用的主要原因:默认的时间段太长了,如果改变了默认值,它们就失去了清除长时间死连接这一最初的意义。

  Keep-alive的另一个问题是它们不仅仅检测死连接,同时也撤销它们。这有可能是应用程序所希望的,但是也有可能不是应用程序所希望的。

  2.2方案二:使用heartbeat检测

  第二种方案是在应用层来实现对连接中断的检测,其基本思想是象Keep-alive一样,定时向对等方发送探针,由于是在应用层实现,可以根据应用程序灵活掌握探测时间。实际上,边界网关协议BGP就是通过定期发送Keep-alive报文给其邻站来检测TCP连接对端的链路或主机失败,两个报文之间的时间间隔建议值为30秒。这种在应用层实现的对连接中断的检测通常称为“heartbeat检测”。

  以上算法原则尽管是在TCP上讨论,但一样适用于UDP。这种方案的最大优点在于提供了最大的灵活性。

  2.3方案三:利用TCP-KEEPALIVE套接字选项

  第三种方案是使用新的POSIX1003.1g套接字选项TCP-KEEPALIVE,它允许在每一个连接的基础上指定超时时间间隔,但是它没有广泛地实现,因此应用中使用的不多。在Linuxkernel2.4之后的TCP实现中,可以这样设置socket的探针间隔:

  #ifdefTCP_KEEPALIVE

  intsecs=120;/*2minutes*/

  setsockopt(s,IPPROTO_TCP,TCP_KEEPALIVE,&secs,sizeof(secs));

  #endif

  这种方案的缺点是,由于TCP-KEEPALIVE套接字选项是比较新的POSIX特性,不是所有TCP实现都给予支持,因此存在移植性问题;同时,由于是在传输层进行探测,灵活性不如在应用层的实现。

  3总结

  综上所述,对于TCP连接中断的检测,原理上都是通过定时向对等方发送探针数据来进行检测,不同方案的区别在于实现于不同层次,应用中可以根据需要进行不同的选择,关键在于对TCP连接的原理要透彻理解。

原文地址:http://www.guigu.org/news/guiguvip/201206117802.html
标签: