【TCP/IP详解 卷一:协议】第十八章 TCP连接 的建立与终止 (2)其余内容

18.5 TCP的半关闭

牢记 TCP 是 全双工 的。

半关闭:TCP提供了连接的一端 在结束了它的发送后 还能接收来自另外一端数据的能力。但是只有很少的应用程序利用它。
为了实现这个特性,编程接口必须提供一种方法来说明“我已经完成了数据的传送,并且发了FIN给另外一端,但是我还是想接收另外一端发送来的数据,直到结束(向我发送FIN)”。

在 执行半关闭 的一端 收到来自另外一端的 FIN 之后,传送一个EOF给应用程序,并对这个 FIN 进行确认并发送ACK,结束了这个连接。

18.6 重点:TCP的状态变迁图

参考:TCP的状态变迁图

状态:描述
CLOSED:无连接是活动的或正在进行
LISTEN:服务器在等待进入呼叫
SYN_RECV:一个连接请求已经到达,等待确认 被动打开
SYN_SENT:应用已经开始,打开一个连接 主动打开

ESTABLISHED:正常数据传输状态

**FIN_WAIT1:应用说它已经完成 **
**FIN_WAIT2:另一边已同意释放 **
ITMED_WAIT:等待所有分组死掉
CLOSING:两边同时尝试关闭
TIME_WAIT:另一边已初始化一个释放
LAST_ACK:等待所有分组死掉

注意:第一点,我们使用粗的实线箭头表示正常的客户端状态变迁,使用虚线箭头表示正常的服务器状态变迁。
第二点是两个导致进入 ESTABLISHED 状态的变迁 对应打开一个连接(SYN_SENT & SYN_RCVD/SYN_RECV),和两个导致ESTABLISHED状态离开的变迁 对应关闭一个连接(FIN_WAIT1 & FIN_WAIT2)。

主动打开:SYN_SENT 被动打开:SYN_RECV
主动关闭:FIN_WAIT1 FIN_WAIT2 TIME_WAIT CLOSING
被动关闭:CLOSE_WAIT LAST_ACK

课程通过 TCP时序图来说明

我们执行被动关闭(进入LISTEN),收到一个SYN,发送一个带ACK的SYN(进入SYN_RCVD),然后收到一个RST,而不是一个ACK,便又回到LISTEN状态并等待另外一个连接请求的到来。
左边的客户执行主动打开,而右边的服务器执行被动打开。图中显示客户端执行主动关闭,但是正如我们之前提到的,另外一端也可以执行主动关闭。

18.6.1 2MSL等待时间

TIME_WAIT状态也称为2MSL等待状态。
MSL:TCP实现选择的 一个报文段最大生存时间MSL。与IP的TTL类似。
对IP数据报TTL的限制是 基于跳数,而不是 定时器。

当TCP执行一个主动关闭 并且 发送最后一个ACK(由主动关闭端发送),该连接在 TIME_WAIT状态 停留的时间为2倍的MSL。这样如果最后的ACK丢失 -> 另外一端(被动关闭)超时 重新发送FIN -> 可以让TCP重发最后一个ACK。
在连接处于2MSL状态的时候,任何迟到的报文段都将被丢弃。因为处在2MSL状态等待的,由socket pair(目的IP地址,源IP地址,目的端口号,源端口号)定义的连接在这段时间内不能再次使用。

一个连接由socket pair定义,一个连接的新的实例(instance)称为该连接的替身。
前文有提到,当要建立一个有效的连接的时候,来自该连接的 一个较早替身 的迟到报文段(SYN/ACK...) 为了防止它作为 使用相同socket pair的新连接 的一部分 将会被曲解。
大概意思是:来自旧连接的 迟到的报文段,为了防止被当成 新连接(采用相同socket pair)的一部分,会被销毁。旧的东西不能被当成新的东西,应该销毁。

如果我们一直重启服务器程序,并测量它到成功的时间,我们就可以确定2MSL值。

18.6.2 平静时间

对于来自 某个连接的较早替身 的迟到报文段,2MSL等待可以 防止将它解释成 使用相同接口对(socket pair)的新连接 的一部分(就是防止解释成另外一个连接的部分)。

平静时间:TCP在重新启动的MSL秒内不能建立任何连接。
防止的是:处于2MSL等待状态的主机重新启动,如果使用 处于2MSL的socket pair(确定一个连接) 来建立一个新的连接,那么任何 之前迟到的(旧连接的)报文段 都会被错误的当成新连接的一部分。

18.6.3 FIN_WAIT_2状态

FIN_WAIT_2: 在主动关闭端 发送了第一个FIN 并且 接收到了ACK 之后,进入FIN_WAIT_2状态。
只有当:另外一端的应用层 意识到它已经接收了一个文件结束符(EOF),并且向我们发送 第二个FIN 来关闭另外一个方向的连接时,在主动关闭端接收到第二个FIN之后,我们才会从FIN_WAIT_2状态 转换到 TIME_WAIT状态。

18.7 复位报文段

TCP首部的 RST比特 是用于复位的。无论什么时候,发送往 基准的连接(即由socket pair确定的连接) 的报文段 出现错误,TCP都会发出一个复位报文段。
有以下三种情况:

  • 对方端口不存在
  • 异常关闭
  • 检查半打开连接

18.7.1 对方端口不存在

当连接请求到达时,目的端口没有进程正在LISTEN。这个时候,对于UDP:产生一个ICMP端口不可达报文。对于TCP:使用复位。

18.7.2 异常关闭

终止一个连接的正常方法是:发送FIN,进行四次挥手。称为 有序释放。
但是也有可能发送一个复位数据报而不是FIN来 中途释放一个连接。称为 异常释放。

异常终止一个连接 对应用程序的两个优点:

  • 丢弃任何待发数据报并立刻发送复位报文段。
  • RST的接收方会 区别 另外一端执行的是 异常关闭 还是 正常关闭,应用程序使用的API 必须 提供 产生异常关闭 而不是正常关闭 的手段。

需要注意的是:RST报文段 不会导致另外一端产生任何响应,另外一端根本不进行确认,收到RST的一端将终止该连接,并通知应用层连接进行复位。

19.7.3 检查半打开连接

半打开:一方已经关闭或者异常终止连接,而另外一方仍然不知道,这样的TCP连接称作 半打开。
常见原因:客户主机突然掉电。

我们异常关闭服务器,然后重新启动,让客户端向服务器发送数据(断电之前有连接),由于服务器的TCP已经重新启动,它将丢失 复位 之前连接的所有信息,因此它不知道客户端新发送的数据报中 提到的连接。
这个时候,TCP的处理是:接收方以复位作为应答。

18.8 同时打开

每一方必须发送一个SYN,并且这些SYN必须传递给对方。这需要每一方使用一个对方熟知的端口 作为本地端口。称为同时打开。
比如 主机A中 一个应用程序使用 7777本地端口,与主机B的端口8888 执行主动打开。B中的应用程序使用 8888本地端口,并与A中的端口7777 执行主动打开。
每一端 既是服务器 又是客户端。

tcp协议在遇到这种情况时,只会打开一条连接。(其他,比如OSI七层模型中的传输层 使用的是两条连接)
这个连接的建立过程需要4次数据交换,而一个典型的连接建立只需要3次交换(即3次握手)。

18.9 同时关闭

双方都执行主动关闭,同时关闭和正常关闭使用的 段交换数目 相同。

18.11 TCP服务器的设计

回顾 UDP服务器的设计,大多数UDP服务器是 重复型 的,这意味着 单个服务器的进程 对 单个UDP端口上 的所有客户请求进行处理。每一个UDP端口都与一个有限大小的输入队列相联系。

大多数TCP服务器是 并发的。当一个新的连接请求到达服务器的时候,服务器接受这个请求,并调用一个新进程 来处理这个新的客户请求,不同的操作系统使用不同的技术来调用新的服务器进程。也可以使用轻型进程 -线程(thread)。

18.11.1 TCP服务器端口

解决问题:TCP如何处理端口号?

不同的连接请求,有可能使用相同的服务器端口号,但是需要我们注意的一点是(或者说 应该牢记的是):TCP通过socket pair来确定一条连接。也就是说,TCP通过本地IP地址,源IP地址,本地端口号,源端口号 “四人帮”来处理多个连接请求。

18.11.2 & 18.11.3 限定的本地IP地址 & 限定的源端IP地址

介绍了 服务器必须使用 特定的本地IP地址 和 服务器指定远端IP地址和远端端口号 的情况。

18.11.4 呼入连接请求队列

一个并发型服务器 调用一个新的进程 来处理客户的连接请求,因此 处于被动连接请求 的服务器应该始终准备处理下一个 呼入的连接请求。

解决问题:服务器正忙的时候,如何处理新的请求?

(1)在等待连接的一端(服务器端)有一个固定长度的连接队列,该队列中的连接已经被TCP接受(完成三次握手),但是还没有被应用程序接受。
(2)积压值:应用层指明的 队列最长长度。取值范围:0-5
(3)我们期望:积压值 = 这一端点所能接受的最多连接。但是事实是 并不相等。
(4)队列还有空间的时候,TCP确认完之后,把新的请求放入队列中。
(5)队列没有空间了,TCP不理会新的连接请求,也不发会 RST。最终 客户端的主动打开会超时。

注意点:

  • 什么时候进队列?-答:TCP确认完毕之后,即三次握手完成。
  • 什么时候出队列?-答:应用层知道了这个连接,并接收了它。
  • 什么是积压值?-答:队列最长长度,所能容纳的最多连接请求数。

2016/8/13

posted @ 2016-08-12 23:03  Wasdns  阅读(640)  评论(0编辑  收藏  举报