18.2.3 建立连接协议
1)请求端(通常是客户端)发送一个SYN段指明客户打算连接的服务器的端口,以及初始序号(ISN)。
2)服务器发回包含服务器的初始序号的SYN报文段作为应答,同时将确认序号设置为客户的ISN加1以对客户的SYN报文段进
行确认,一个SYN将占用一个序号。
3)客户必须将确认序号设置为服务器的ISN加1以对服务器的SYN报文段进行确认。
这三个报文段完成连接的建立。这个过程就是三次握手。
发送第一个SYN的一端将执行主动打开,接收这个SYN并发回下一个SYN的另一端执行被动打开。
当一端为建立连接而发送它的SYN时,它为连接选择一个初始序号(ISN)。
ISN随时间变化,因此每个连接都将具有不同的ISN。这样选择的目的是防止在网络中被延迟的分组在以后又被传送,而导致
某个连接的一方对它作错误的解释。
18.2.4 连接终止协议
终止一个连接要经过4次分手。
这是由TCP的半关闭造成的,既然一个TCP连接是全双工,因此每个方向必须单独地进行关闭。
这原则就是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向连接。当一端收到一个 FIN,它必须通知应用
层另一端已经终止了那个方向的数据传送。发送FIN通常是应用层进行关闭的结果。
收到一个FIN只意味着在这一方向上没有数据流动。
一个TCP连接在收到一个FIN后仍能发送数据。而这对利用半关闭的应用来说是可能的,尽管在实际应用中只有很少的TCP
应用程序这样做。
首先进行关闭的一方(即发送第一个 FIN)将执行主动关闭,而另一方(收到这个 FIN)执行被动关闭。通常一方完成主动
关闭而另一方完成被动关闭。
连接通常是由客户端发起的,这样第一个 SYN从客户传到服务器。
每一端都能主动关闭这个连接(即首先发送 FIN)。然而,一般由客户端决定何时终止连接,因为客户进程通常由用户交互
控制,用户会键入诸如“quit”一样的命令来终止进程。
18.4 最大报文段长度
最大报文段长度(MSS)表示TCP传往另一端的最大块数据的长度。
当一个连接建立时,连接的双方都要通告各自的MSS。
我们已经见过MSS都是1024,这导致IP数据报通常是40字节长:20字节的TCP首部和20字节的IP首部。
MSS并不是任何条件下都可协商,当建立一个连接时,每一方都有用于通告它期望接收的MSS选项(MSS选项只能出在SYN
报文段中)。
如果一方不接收来自另一方的MSS值,则MSS就定为默认值536字节(这个默认值允许20字节的IP首部和20字节的TCP首部以
适合576字节IP数据报)。
一般来说,如果没有分段发生,MSS还是越大越好,报文段越大允许每个报文段传送的数据就越多,相对IP和TCP首部有更
高的网络利用率。
当TCP发送一个SYN时,或者是因为一个本地应用进程想发起一个连接,或者是因为另一端的主机收到了一个连接请求,它
能将MSS值设置为外出接口上的MTU长度减去固定的IP首部和TCP首部长度。
对于一个以太网(MTU=1500),MSS值可达1460字节。对于IEEE802.3,MSS可达到1452字节。
如果目的IP地址为“非本地的”,MSS通常的默认值为536,而区分地址是本地还是非本地是简单的:
如果目的IP地址的网络号与子网号都和我们的相同,则是本地的;
如果目的IP地址的网络号与我们完全不同,则是非本地的;
如果目的IP地址的网络号与我们相同而子网号与我们的完全不同,则可能是本地的,也可能是非本地的。
大多数 TCP实现版都提供了一个配置选项,让系统管理员说明不同的子网是属于本地还是非本地。
MSS让主机限制另一端发送数据报的长度,加上主机也能控制它发送数据报的长度,这将使以较小MTU连接到一个网络上的
主机避免分段。
只有当一端的主机以小于576字节的MTU直接连接到一个网络中, 避免这种分段才会有效。如果两端的主机都连接到以太网
上,都采用 536字节 的MSS,但中间网络采用296的MTU,也将会出现分段。
使用路径上的MTU发现机制是关于这个问题的唯一方法。
18.5 TCP的半关闭
TCP提供了连接的一段在结束它的发送后还能接收来自另一端数据的能力,这就是半关闭。
为了使用这个特性,编程接口必须为应用程序提供一种方式来说明“我已经完成了数据传输,因此发送一个文件结束(FIN)
给另一端,但我还想接收另一端发送来的数据,直到它给我发来文件结束(FIN)”。
如果应用程序不调用close而调用shutdown,且第2个参数值为1,则插口的API支持半关闭。然而,大多数的应用程序通过调
用close终止两个方向的连接。
如图:
18.6 TCP的状态变迁图
图中要注意:
第一点:一个状态变迁的子集是“典型的”。我们用粗的实线箭头表示正常的客户端状态变迁,用粗的虚线箭头表示正常的服
务器状态变迁。
第二点:两个导致进入ESTABLISHED状态的变迁对应打开一个连接,而两个导致从ESTA BLISHED状态离开的变迁对应关闭
一个连接。
ESTABLISHED状态是连接双方能够进行双向数据传递的状态。
18.6.1 2MSL等待状态
TIME_WAIT状态也称为2MSL等待状态。
MSL是一个报文段最大生存时间,它是任何报文段被丢弃前在网络内的最长时间,这个时间是有限的,因为TCP报文段以IP
数据报在网络内传输,而IP数据报则有限制其生存时间的TTL字段。
当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间是2MSL,这样可让TCP再次发
送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。
这种2MSL等待的另一个结果是:这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的
IP地址和端口号)不能再被使用。
这个连接只能在2MSL结束后才能再被使用。
在连接处于2MSL等待时,任何迟到的报文段将被丢弃,
因为处于 2MSL等待的,由该插口对(socket pair)定义的连接在这段时间内不能被再用,因此当要建立一个有效的连接时,来
自该连接的一个较早替身(incarnation)的迟到报文段作为新连接的一部分不可能不被曲解(一个连接由一个插口对来定义,
一个连接的新的实例称为该连接的替身) 。
客户执行主动关闭并进入 TIME _ WAIT是正常的。
服务器通常执行被动关闭,不会进入TIME _ WA IT状态。
这暗示如果我们终止一个客户程序,并立即重新启动这个客户程序,则这个新客户程序将不能重用相同的本地端口。这不会
带来什么问题,因为客户使用本地端口,而并不关心这个端口号是什么。
一个插口对(即包含本地 IP地址、本地端口、远端 IP地址和远端端口的4元组)在它处于2MSL等待时,将不能再被使用。
尽管许多具体的实现中允许一个进程重新使用仍处于2MSL等待的端口(通常是设置选项 SO _ REUSEADDR),但TCP不能
允许一个新的连接建立在相同的插口对上。
18.6.2 平静时间的概念
如果使用处于2MSL等待端口的主机出现故障,它会在MSL秒内重新启动,并立即使用故障前仍处于2MSL的插口对来建立
一个新的连接吗?
如果是这样,在故障前从这个连接发出而迟到的报文段会被错误地当作属于重启后新连接的报文段。无论如何选择重启后新
连接的初始序号,都会发生这种情况。为了防止这种情况,RFC 793指出TCP在重启动后的MSL秒内不能建立任何连接。这就
称为平静时间(quiet time)。
18.6.3 FIN_WAIT_2 状态
在FIN_WAIT_2状态我们已经发出了 FIN,并且另一端也已对它进行确认。除非我们在实行半关闭,否则将等待另一端的
应用层意识到它已收到一个文件结束符说明,并向我们发一个FIN来关闭另一方向的连接。
只有当另一端的进程完成这个关闭,我们这端才会从FIN_WAIT_2状态进入TIME_WAIT状态。
这意味着我们这端可能永远保持这个状态。
另一端也将处于CLOSE _ WAIT状态,并一直保持这个状态直到应用层决定进行关闭。
18.7 复位报文段
TCP首部中的RST位适用于“复位”的,
无论何时,一个报文段发往基准的连接出现错误,TCP都会发出一个复位报文段(这里提到的“基准的连接”是指由目的IP
地址和目的端口号以及源 IP地址和源端口号指明的连接)。
18.7.1 到不存在的端口的连接请求
产生复位的一种常见情况是当连接请求到达时,目的端口没有进程正在听。
对于 UDP,当一个数据报到达目的端口时,该端口没在使用,它将产生一个ICMP端口不可达的信息,而TCP则使用复位。
18.7.2 异常终止一个连接
终止一个连接的正常方式是一方发送 FIN。有时这也称为有序释放,因为在所有排队数据都已发送之后才发送 FIN,正常情
况下没有任何数据丢失。
但也有可能发送一个复位报文段而不是 FIN来中途释放一个连接,有时称这为异常释放。
异常终止一个连接对应用程序来说有两个优点:
(1)丢弃任何待发数据并立即发送复位报文段。
(2)RST 的接收方会区分另一端执行的是异常关闭还是正常关闭,应用程序使用的API必须提供产生异常关闭而不是正常关
闭的手段。