速读原著-TCP/IP(TCP的状态变迁图)

第18章 TCP连接的建立与终止

18.6 TCP的状态变迁图

我们已经介绍了许多有关发起和终止 T C P连接的规则。这些规则都能从图 1 8 - 1 2所示的状态变迁图中得出。
在这里插入图片描述
在这个图中要注意的第一点是一个状态变迁的子集是“典型的”。我们用粗的实线箭头表示正常的客户端状态变迁,用粗的虚线箭头表示正常的服务器状态变迁。

第二点是两个导致进入E S TA B L I S H -E D状态的变迁对应打开一个连接,而两个导致从E S TA B L I S H E D状态离开的变迁
对应关闭一个连接。E S TA B L I S H E D状态是连接双方能够进行双向数据传递的状态。以后的章节将介绍这个状态。

将图中左下角 4个状态放在一个虚线框内,并标为“主动关闭”。其他两个状态(C L O S E _ WA I T和L A S T _ A C K)也用虚线框住,并标为“被动关闭”。 在 这 个 图 中 1 1 个 状 态 的 名 称 (CLOSED, LISTEN, SYN_SENT等)是有意与n e t s t a t命令显示的状态名称一致。n e t s t a t对状态的命名几乎与 在 RFC 793中 的 最 初 描述 一 致 。C L O S E D状态不是一个真正的状态,
而是这个状态图的假想起点和终点。

从L I S T E N到S Y N _ S E N T的变迁是正确的,但伯克利版的T C P软件并不支持它。只有当S Y N _ R C V D状态是从L I S T E N状态(正常情况)进入,而不是从 S Y N _ S E N T状态(同时打开)进入时,从 S Y N _ R C V D回到L I S T E N的状态变迁才是有效的。这意味着如果我们执行被动关闭(进入L I S T E N),收到一个S Y N,发送一个带A C K的S Y N(进入S Y N _ R C V D),然后收到一个R S T,而不是一个A C K,便又回到L I S T E N状态并等待另一个连接请求的到来。图1 8 - 1 3显示了在正常的T C P连接的建立与终止过程中,客户与服务器所经历的不同状态。

它是图1 8 - 3的再现,不同的是仅显示了一些状态。假定在图1 8 - 1 3中左边的客户执行主动打开,而右边的服务器执行被动打开。尽管图中显示出由客户端执行主动关闭,但和早前我们提到的一样,另一端也能执行主动关闭。可以使用图1 8 - 1 2的状态图来跟踪图1 8 - 1 3的状态变化过程,以便明白每个状态的变化。

18.6.1 2MSL等待状态

T I M E _ WA I T状态也称为2 M S L等待状态。每个具体 T C P实现必须选择一个报文段最大生存时间M S L(Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间。

我们知道这个时间是有限的,因为 T C P报文段以I P数据报在网络内传输,而I P数据报则有限制其生存时间的T T L字段。RFC 793 [Postel 1981c] 指出MSL为2分钟。然而,实现中的常用值是30秒,1分钟,或2分钟。

从第8章我们知道在实际应用中,对 I P数据报T T L的限制是基于跳数,而不是定时器。对一个具体实现所给定的 M S L值,处理的原则是:当 T C P执行一个主动关闭,并发回最后一个A C K,该连接必须在 T I M E _ WA I T状态停留的时间为 2倍的M S L。

这样可让T C P再次发送最后的A C K以防这个A C K丢失(另一端超时并重发最后的 F I N)。这种2 M S L等待的另一个结果是这个 T C P连接在2 M S L等待期间,定义这个连接的插口(客户的I P地址和端口号,服务器的 I P地址和端口号)不能再被使用。这个连接只能在 2 M S L结束后才能再被使用。

遗憾的是,大多数 T C P实现(如伯克利版)强加了更为严格的限制。在 2 M S L等待期间,插口中使用的本地端口在默认情况下不能再被使用。我们将在下面看到这个限制的例子。

某些实现和A P I提供了一种避开这个限制的方法。使用插口A P I时,可说明其中的S O _ R E U S E A D D R选项。它将让调用者对处于2 M S L等待的本地端口进行赋值,但我们将看到TCP原则上仍将避免使用仍处于2MSL连接中的端口。在连接处于2 M S L等待时,任何迟到的报文段将被丢弃。因为处于 2 M S L等待的、由该插口对(socket pair)定义的连接在这段时间内不能被再用,因此当要建立一个有效的连接时,来自该连接的一个较早替身( i n c a r n a t i o n)的迟到报文段作为新连接的一部分不可能不被曲解(一个连接由一个插口对来定义。一个连接的新的实例( i n s t a n c e)称为该连接的替身)。我们说图1 8 - 1 3中客户执行主动关闭并进入 T I M E _ WA I T是正常的。服务器通常执行被动关闭,不会进入T I M E _ WA I T状态。

这暗示如果我们终止一个客户程序,并立即重新启动这个客户程序,则这个新客户程序将不能重用相同的本地端口。这不会带来什么问题,因为客户使用本地端口,而并不关心这个端口号是什么。

然而,对于服务器,情况就有所不同,因为服务器使用熟知端口。如果我们终止一个已经建立连接的服务器程序,并试图立即重新启动这个服务器程序,服务器程序将不能把它的这个熟知端口赋值给它的端点,因为那个端口是处于 2 M S L连接的一部分。在重新启动服务器程序前,它需要在1 ~ 4分钟。

可以通过s o c k程序看到这一切。我们启动服务器程序,从一个客户程序进行连接,然后停止这个服务器程序。
在这里插入图片描述
当重新启动服务器程序时,程序报告一个差错信息说明不能绑定它的熟知端口,因为该端口已被使用(即它处于2 M S L等待)。

运行n e t s t a t程序来查看连接的状态,以证实它的确处于 2 M S L等待状态。如果我们一直试图重新启动服务器程序,并测量它直到成功所需的时间,我们就能确定出2 M S L值。对于SunOS 4.1.3、S V R 4、B S D / 3 8 6和AIX 3.2.2,它需要1分钟才能重新启动服务器程序,这意味着它们的M S L值为3 0秒。而对于Solaris 2.2 ,它需要4分钟才能重新启动服务器程序,这表示它的MSL值为2分钟。

如果一个客户程序试图申请一个处于 2 M S L等待的端口(客户程序通常不会这么做),就会出现同样的差错。
在这里插入图片描述
我们在第1次执行客户程序时采用 - v选项来查看它使用的本地端口为( 11 6 2)。第2次执行客户程序时则采用- b选项来选择端口 11 6 2为它的本地端口。正如我们所预料的那样,客户程序无法那么做,因为那个端口是一个还处于 2 M S L等待连接的一部分。

需要再次强调2 M S L等待的一个效果,因为我们将在第 2 7章的文件传输协议F T P中遇到它。和以前介绍的一样,一个插口对(即包含本地 I P地址、本地端口、远端 I P地址和远端端口的4元组)在它处于2 M S L等待时,将不能再被使用。尽管许多具体的实现中允许一个进程重新使用仍处于2 M S L等待的端口(通常是设置选项 S O _ R E U S E A D D R),但T C P不能允许一个新的连接建立在相同的插口对上。可通过下面的试验来看到这一点:
在这里插入图片描述
在第1次运行s o c k程序中,我们将它作为服务器程序,端口号为 6 6 6 6,并从主机b s d i上的一个客户程序与它连接,这个客户程序使用的端口为 1 0 9 8。我们终止服务器程序,因此它将执行主动关闭。这将导致 4元组 1 4 0 . 2 5 2 . 1 3 . 3 3(本地 I P地址)、6 6 6 6 (本地端口号)、1 4 0 . 2 5 2 . 1 3 . 3 5(另一端I P地址)和1 0 9 8(另一端的端口号)在服务器主机进入 2 M S L等待。

在第2次运行s o c k程序时,我们将它作为客户程序,并试图将它的本地端口号指明为6 6 6 6,同时与主机b s d i在端口1 0 9 8上进行连接。但这个程序在试图将它的本地端口号赋值为6 6 6 6时产生了一个差错,因为这个端口是处于 2 M S L等待4元组的一部分。

为了避免这个差错,我们再次运行这个程序,并使用选项- A来设置前面提到的S O _ R E U S E A D D R。这将让s o c k程序能将它的本地端口号设置为 6 6 6 6,但当我们试图进行主动打开时,又出现了一个差错。即使它能将它的本地端口设置为 6 6 6 6,但它仍不能和主机b s d i在端口1 0 9 8上进行连接,因为定义这个连接的插口对仍处于 2 M S L等待状态。

如果我们试图从其他主机来建立这个连接会如何?首先我们必须在 s u n上以-A标记来重新启动服务器程序,因为它需要的端口( 6 6 6 6)是还处于2 M S L等待连接的一部分。

sun % sock -A -s 6666 启动服务器程序,在端口 6 6 6 6监听

接着,在2 M S L等待结束前,我们在b s d i上启动客户程序:

bsdi % sock -b1098 sun 6666
connected on 140.252.13.35.1098 to 140.252.13.33.6666

不幸的是它成功了!这违反了 T C P规范,但被大多数的伯克利版实现所支持。这些实现允许一个新的连接请求到达仍处于 T I M E _ WA I T状态的连接,只要新的序号大于该连接前一个替身的最后序号。在这个例子中,新替身的 I S N被设置为前一个替身最后序号与 128 000的和。附录的RFC 1185 [Jacobsan、B r a d e n和Zhang 1990] 指出了这项技术仍可能存在缺陷。

对于同一连接的前一个替身,这个具体实现中的特性让客户程序和服务器程序能连续地重用每一端的相同端口号,但这只有在服务器执行主动关闭才有效。我们将在图 2 7 - 8中使用F T P时看到这个2 M S L等待条件的另一个例子。也见习题 1 8 . 5。

18.6.2 平静时间的概念

对于来自某个连接的较早替身的迟到报文段, 2 M S L等待可防止将它解释成使用相同插口对的新连接的一部分。但这只有在处于 2 M S L等待连接中的主机处于正常工作状态时才有效。

如果使用处于2 M S L等待端口的主机出现故障,它会在 M S L秒内重新启动,并立即使用故障前仍处于2 M S L的插口对来建立一个新的连接吗?如果是这样,在故障前从这个连接发出而迟到的报文段会被错误地当作属于重启后新连接的报文段。无论如何选择重启后新连接的初始序号,都会发生这种情况。

为了防止这种情况,RFC 793指出T C P在重启动后的M S L秒内不能建立任何连接。这就称为平静时间(quiet time)。

只有极少的实现版遵守这一原则,因为大多数主机重启动的时间都比MSL秒要长。

18.6.3 FIN_WAIT_2 状态

在F I N _ WA I T _ 2状态我们已经发出了 F I N,并且另一端也已对它进行确认。除非我们在实行半关闭,否则将等待另一端的应用层意识到它已收到一个文件结束符说明,并向我们发一个F I N来关闭另一方向的连接。只有当另一端的进程完成这个关闭,我们这端才会从F I N _ WA I T _ 2状态进入T I M E _ WA I T状态。

这意味着我们这端可能永远保持这个状态。另一端也将处于 C L O S E _ WA I T状态,并一直保持这个状态直到应用层决定进行关闭。许多伯克利实现采用如下方式来防止这种在F I N _ WA I T _ 2状态的无限等待。如果执行主动关闭的应用层将进行全关闭,而不是半关闭来说明它还想接收数据,就设置一个定时器。如果这个连接空闲1 0分钟7 5秒,T C P将进入C L O S E D状态。在实现代码的注释中确认这个实现代码违背协议的规范。

发布了1519 篇原创文章 · 获赞 1441 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/weixin_42528266/article/details/104757370
今日推荐