共计 11432 个字符,预计需要花费 29 分钟才能阅读完成。
五、运输层
5.1 运输层概述
之前课程所介绍的计算机网络体系中的物理层、数据链路层以及网络层他们共同解决了将主机通过异构网络互联起来所面临的问题,实现了主机到主机的通信。
但实际上在计算机网络中进行 通信的真正实体是位于通信两端主机中的进程。
如何为运行在不同主机上的应用进程提供直接的通信服务是运输层的任务,运输层协议又称为端到端协议。

运输层向高层用户屏蔽了下面网络核心的细节(如网络拓扑、所采用的路由选择协议等),它使应用进程看见的就 好像是在两个运输层实体之间有一条端到端的逻辑通信信道。
根据应用需求的不同,因特网的运输层 为应用层提供了两种不同的运输协议,即 面向连接的 TCP 和 无连接的 UDP,这两种协议就是本章要讨论的内容。

5.2 运输层端口号、复用与分用的概念
运行在计算机上的进程使用 进程标识符 PID 来标志。
因特网上的计算机并不是使用统一的操作系统,不同的操作系统(windows, Linux, Mac OS)又使用 不同格式的进程标识符。
为了使运行不同操作系统的计算机的应用进程之间能够进行网络通信,就必须 使用统一的方法对 TCP / IP 体系的应用进程进行标识。
1. 端口号
TCP / IP 体系的运输层使用 端口号 来区分应用层的不同应用进程。
- 端口号使用 16 比特表示,取值范围 0 ~ 65535;
- 熟知端口号:0 ~ 1023,IANA 把这些端口号指派给了 TCP / IP 体系中最重要的一些应用协议,例如:FTP 使用 21/20,HTTP 使用 80,DNS 使用 53.
- 登记端口号:1024 ~ 49151,为没有熟知端口号的应用程序使用。使用这类端口号必须在 IANA 按照规定的手续登记,以防止重复。例如:Microsoft RDP 微软远程桌面使用的端口号是 3389。
- 短暂端口号:49152 ~ 65535,留给客户进程选择暂时使用。当服务器进程收到客户进程的报文时,就知道了客户进程所使用的动态端口号。通信结束后,这个端口号可供其他客户进程以后使用。
- 端口号只具有本地意义,即端口号只是为了 标识本计算机应用层中的各进程,在因特网中,不同计算机中的相同端口号是没有联系的。
2. 复用与分用
发送方的复用和接收方的分用

TCP / IP 体系的应用层常用协议所使用的运输层熟知端口号

3. 举例:运输层端口号
当输入域名时,用户 PC 中的 DNS 客户端进程会发送一个 DNS 查询请求报文,DNS 查询请求报文需要使用运输层的 UDP 协议封装成 UDP 用户数据报,其首部中源端口的值在短暂端口号 49151 ~ 65535 中挑选一个未被占用的,用来表示 DNS 客户端进程,目的端口字段设置为 53,这是 DNS 服务器端进程所使用的熟知端口号。
之后将 UDP 用户数据报封装在 IP 数据报中,通过以太网发送给 DNS 服务器。

DNS 服务器端收到该数据报后,从中解封出 UDP 用户数据报。UDP 首部中的目的端口号为 53,这表明应将该 UDP 用户数据报的数据载荷部分,也就是 DNS 查询请求报文,交付给本服务器中的 DNS 服务器端进程。
DNS 服务器端进程解析 DNS 查询请求报文的内容,然后查找对应的 IP 地址。之后会给用户 PC 发送 DNS 响应报文,DNS 响应报文需要使用运输层的 UDP 协议封装成 UDP 用户数据报,其首部中的源端口字段设置为熟知端口号 53,表明这是 DNS 服务器端进程所发送的 UDP 用户数据报;目的端口字段的值设置为 49152,这是之前用户 PC 中发送 DNS 查询请求报文的 DNS 客户端进程所使用的短暂端口号。
之后,将 UDP 用户数据报封装在 IP 数据报中,通过以太网发送给用户 PC。

用户 PC 收到给数据报后,从中解封出 UDP 用户数据报,UDP 首部中的目的端口号为 49152,这表明应将该 UDP 用户数据报的数据载荷部分,也就是 DNS 响应报文,交付给用户 PC 中的 DNS 客户端进程。
DNS 客户端进程解析 DNS 响应报文的内容,就可知道之前所请求的域名所对应的 IP 地址。

现在用户 PC 中的 HTTP 客户端进程将 TCP 报文段封装在 IP 数据报中,通过以太网发送给 Web 服务器。

Web 服务器收到该数据报后,从中解封出 TCP 报文段。TCP 首部中的目的端口号为 80,这表明应将该 TCP 报文段的数据载荷部分,也就是 HTTP 请求报文,交付给本服务器中的 HTTP 服务器端进程。
HTTP 服务器端进程解析 HTTP 请求报文的内容,然后按其要求查找首页内容。之后给用户 PC 发送 HTTP 响应报文,HTTP 响应报文需要使用运输层的 TCP 协议封装成 TCP 报文段,其首部中的源端口字段设置为熟知端口号 80,表明这是 HTTP 服务器端进程所发送的 TCP 报文段;目的端口值设置为 49152,这是之前用户 PC 中,发送 HTTP 请求报文的 HTTP 客户端进程所使用的短暂端口号。
之后将 TCP 报文段封装在 IP 数据报中,通过以太网发送给用户 PC。

用户 PC 收到该数据报后,从中解封出 TCP 报文段。TCP 首部中的目的端口号为 49152,这表明应将该 TCP 报文段的数据载荷部分,也就是 HTTP 响应报文,交付给用户 PC 中的 HTTP 客户端进程。HTTP 客户端进程解析 HTTP 响应报文的内容,并在网页浏览器中进行显示。

5.3 UDP 和 TCP 的对比
UDP 和 TCP 是 TCP / IP 体系结构运输层中的两个重要协议



UDP
发送方的进程将应用层报文交付给运输层的 UDP。UPD 直接给应用层报文添加一个 UDP 首部,使之成为 UDP 用户数据报。然后进行发送。
接收方的 UDP 收到该 UDP 用户数据报后,去掉 UDP 首部,将应用层报文交付给应用进程。即,UDP 对应用进程交下来的报文即不合并也不拆分,而是保留这些报文的边界。
TCP
发送方 TCP 把应用进程交付下来的数据块仅仅看作是一连串的、无结构的字节流, TCP 并不知道这些待传送的字节流的含义,仅将他们编号并存储在自己的发送缓存中。TCP 根据发送策略,从发送缓存中提取一定数量的字节,构建 TCP 报文段并发送。
接收方的 TCP,一方面从所接收到的 TCP 报文段中取出数据载荷部分并存储在接收缓存中;一方面将接收缓存中的一些字节交付给应用进程。TCP 不保证接收方应用进程所收到的数据块与发送方应用进程所发送的数据块具有对应大小的关系。例,发送方应用进程交付给发送方 TCP 共 10 个数据块,但接收方 TCP 可能只用了 4 个数据块就把收到的字节流交付给了上层的应用进程。
TCP是面向字节流的,这也是 TCP 实现可靠传输、流量控制、以及拥塞控制的基础。



5.4 TCP 的流量控制
1. 概述
一般来说,我们总是希望数据传输得更快一些。
- 但如果发送方把数据发送的过快,接收方就可能来不及接收,这就会造成数据的丢失
所谓流量控制(flow control)就是 让发送方的发送速率不要太快,要让接收方来得及接收。
利用 滑动窗口 机制可以很方便的在 TCP 连接上实现对发送方的流量控制。
2. 举例




3. 习题
题一

查看答案

5.5 TCP 的拥塞控制
在某段时间,若 对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏。这种情况就叫做 拥塞(congestion)
- 在计算机网络中的链路容量(即带宽)、交换结点中的缓存和处理机等,都是网络的资源。
若 出现拥塞而不进行控制,整个网络的 吞吐量将随输入负荷的增大而下降。

下面介绍四种拥塞控制算法的基本原理,假定如下条件:
- 数据是单方向传送,而另一个方向只传送确认。
- 接收方总是有足够大的缓存空间,因而发送方发送窗口的大小由网络的拥塞程度来决定。
- 以最大报文段 MSS 的个数为讨论问题的单位,而不是以字节为单位。
发送方维护一个叫做 拥塞窗口 cwnd 的状态变量,其值 取决于网络的拥塞程度,并且 动态变化。
- 拥塞窗口 cwnd 的维护原则:只要网络 没有出现拥塞,拥塞窗口 就再 增大 一些;但只要网络 出现拥塞,拥塞窗口就减少 一些。
- 判断出现 网络拥塞的依据:没有按时收到应当到达的确认报文(即 发生超时重传)。
发送方将拥塞窗口作为 发送窗口 swnd,即 swnd = cwnd。
维护一个慢开始门限 ssthresh 状态变量:
- 当 cwnd < ssthresh 时,使用慢开始算法;
- 当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法;
- 当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞避免算法;
1. 慢开始(slow-start)
每个传输轮次结束后,拥塞窗口值按指数规律增大。

2. 拥塞避免(congestion avoidance)
每个传输轮次结束后,拥塞窗口值只能线性加一。
假设 24 个数据报文段在传输时丢失了几个,则触发发送方对这些丢失报文段的超时重传。
重传计时器超时,判断网络很可能出现了拥塞,进行以下工作:
- 将 ssthresh 值更新为发送拥塞时 cwnd 值的一半;
- 将 cwnd 值减少为 1,并重新开始执行慢开始算法。


注意:
- “慢开始” 是指 一开始 向网络注入的 报文段少,并不是指拥塞窗口 cwnd 增长速度慢;
- “拥塞避免” 并非指完全能够避免拥塞,而是指在拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞;
慢开始和拥塞避免算法是 1988 年提出的 TCP 拥塞控制算法(TCP Tahoe 版本)
1990 年又增加了两个新的拥塞控制算法(改进 TCP 的性能),这就是快重传和快恢复(TCP Rene 版本)。
- 有时,个别报文段 会在网络中 丢失,但实际上网络 并未发生拥塞。
- 这将导致 发送方 超时重传,并 误认为 网络发生了 拥塞;
- 发送方把拥塞窗口 cwnd 又设置为最小值 1,并错误的启动慢开始算法,因而 降低了传输效率。
采用快重传算法可以 让发送方尽早知道发生了个别报文段的丢失。
3. 快重传(fast retransmit)
所谓快重传,就是使发送方 尽快进行重传,而 不是等超时重传计时器超时 再重传。
- 要求接收方不要等待自己发送数据时才进行捎带确认,而是要 立即发送确认;
- 即使收到了失序的报文段也要立即发出对已收到的报文段的 重复确认。
- 发送方一旦 收到 3 个连续的重复确认,就将相应的报文段 立即重传,而不是等该报文段的超时重传计时器超时再重传。
- 对于个别丢失的报文段,发送方不会出现超时重传,也就不会误认为出现了拥塞(进而降低拥塞窗口 cwnd 为 1)。使用快重传可以使整个网络的吞吐量提高约 20%。

4. 快恢复(fast recovery)
发送方一旦 收到 3 个重复确认,就知道现在只是丢失了个别的报文段。于是不启动慢开始算法,而 执行快恢复算法;
- 发送方将慢开始门限 ssthresh 值和拥塞窗口 cwnd 值调整为当前窗口的一半;开始执行拥塞避免算法。
- 也有的快恢复实现是把快恢复开始时的拥塞窗口 cwnd 值再增大一些,即等于新的 ssthresh + 3。
- 既然发送方收到 3 个重复确认,就表明有 3 个数据报文段已经离开了网络;
- 这 3 个报文段不再消耗网络资源而是停留在接收方的接收缓存中;
- 可见现在网络中不是堆积了报文段而是减少了 3 个报文段。因此可以适当把拥塞窗口扩大些。

5. 习题
题一

查看答案

5.6 TCP 超时重传时间的选择*
1. 超时重传时间的选择是 TCP 最复杂的问题之一
若超时重传时间小于往返时间

若超时重传时间远大于往返时间


不能直接使用某次测量得到的 RTT 样本来计算超时重传时间 RTO。
利用每次测量得到的 RTT 样本,计算加权平均 往返时间 RTTs(又称为平滑的往返时间)。

用这种方法得出的加权平均往返时间 RTTs 就比测量出的 RTT 值更加平滑。
显然,超时重传时间 RTO 应略大于加权平均往返时间 RTTs。
2. RFC6298 建议使用下式计算超时重传时间 RTO:

3. 往返时间 RTT 的测量比较复杂

源主机若误将确认当做是对原报文段的确认:
所计算出的 RTTs 和 RTO 就会偏大,降低了传输效率;

源主机若误将确认当做是对重传报文段的确认:
所计算出的 RTTs 和 RTO 就会偏小,导致报文段没必要的重传,增大网络负荷;
针对 出现超时重传时无法测准往返时间 RTT 的问题,Karn 提出了一个 算法:在计算加权平均往返时间 RTTs 时,只要报文段重传了,就不采用其往返时间 RTT 样本。也就是出现重传时,不重新计算 RTTs,进而超时重传时间 RTO 也不会重新计算。
- 这又引起了新的问题。设想出现一种情况:报文段的时延突然增大了很多,并且之后很长一段时间都会保持这种时延。因此在原来得出的重传时间内,不会收到确认报文段。于是就重传报文段。但根据 Karn 算法,不考虑重传报文段的往返时间样本。这样,超时重传时间就无法更新。这会导致报文段反复被重传。
因此,要对 Karn 算法进行修正。方法是:报文段每重传一次,就把超时重传时间 RTO 增大一些。典型的做法是将新 RTO 的值取为旧 RTO 值的 2 倍。

5.7 TCP 可靠传输的实现
1. TCP 基于以字节为单位的滑动窗口来实现可靠传输
发送方

后沿的移动情况有两种可能:
- 不动(没有收到新的确认)
- 前移(收到了新的确认)
前沿的移动情况有三种可能:
- 通常是不断向前移动
- 不动
- 没有收到新的确认,对方通知的窗口大小也不变;
- 收到新确认但对方通知的窗口缩小,使发送窗口前沿正好不动;
- 向后收缩(对方通知的窗口缩小)(不推荐)
如何描述发送窗口的状态

使用三个指针 P1,P2,P3 分别指向相应的字节序号
- 小于 P1 的是已发送并已收到确认的部分;
- 大于等于 P3 的是不允许发送的部分;
- P3 – P1 = 发送窗口的尺寸;
- P2 – P1 = 已发送但未收到确认的字节数;
- P3 – P2 = 允许发送但当前尚未发送的字节数(又称为可用窗口或有效窗口);
接收方

补充
虽然发送方的发送窗口是根据接收方的接收窗口设置的,但在同一时刻,发送方的发送窗口并不总是和接收方的接收窗口 一样大
- 网络传送窗口值需要经历一定的时间滞后,并且这个时间还是不确定的。
- 发送方还可能根据网络当时的拥塞情况适当减小自己的发送窗口尺寸。
对于 不按序到达的数据应如何处理,TCP 并无明确规定。
- 如果接收方把不按序到达的数据一律丢弃,那么接收窗口的管理将会比较简单,但这样做对网络资源的利用不利,因为发送方会重复传送较多的数据。
- TCP 通常对不按序到达的数据是先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再 按序交付上层的应用进程。
TCP 要求接收方必须有 累计确认和捎带确认机制,这样可以减小传输开销。接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上。
- 接收方不应过分推迟发送确认,否则会导致发送方不必要的超时重传,这反而浪费了网络的资源。TCP 标准规定,确认推迟的时间不应超过 0.5 秒。若收到一连串具有最大长度的报文段,则必须每隔一个报文段就发送一个确认[RFC 1122]。
- 捎带确认实际上并不经常发生,因为大多数应用程序很少同时在两个方向上发送数据。
- TCP 的通信时全双工通信。通信中的每一方都在发送和接收报文段。因此,每一方都有自己的发送窗口和接收窗口。在谈到这些窗口时,一定要弄清楚是哪一方的窗口。
2. 习题
题一

查看解析

题二

查看解析

5.8.1 TCP 的运输连接管理 — TCP 的连接建立
TCP 是面向连接的协议,它基于运输连接来传送 TCP 报文段。
TCP 运输连接的建立和释放是每一次面向连接的通信中必不可少的过程。
TCP 运输连接有以下三个阶段:

TCP 的运输连接管理就是使运输连接的建立和释放都能正常地进行。
1. TCP 的连接建立要解决以下三个问题
- 使 TCP 双方都能够确知对方的存在;
- 使 TCP 双方能够协商一些参数(如最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等);
- 使 TCP 双方能够对运输实体资源(如缓存大小、连接表中的项目等)进行分配。
2. TCP 使用 “三报文握手” 建立连接
我们可以将 TCP 建立连接的过程比喻为 “握手”。“握手” 需要在 TCP 客户和服务器之间 交换三个 TCP 报文段。
最初,两端的 TCP 进程都处于 关闭状态。一开始,TCP 服务器进程首先创建 传输控制块,用来存储 TCP 连接中的一些重要信息。
之后,就准备接收客户进程的连接请求。
此时,TCP 服务器进程就进入 监听状态,等待 TCP 客户进程的连接请求。TCP 服务进程是被动等待来自 TCP 客户进程的连接请求而不是主动发起,因此称为被动打开连接。TCP 客户进程也是首先创建 传输控制块。然后,在打算建立 TCP 连接时,向 TCP 服务器进程发送 TCP 连接请求报文段,并进入 同步已发送状态。

TCP 连接请求报文段首部中的 同步位 SYN 被设置为 1,表明这是一个 TCP 连接请求报文段。序号字段 seq 被设置了一个初始值 x,作为 TCP 客户进程所选择的初始序号。(注意:TCP 规定 SYN 被设置为 1 的报文段不能携带数据,但要消耗掉一个序号。)由于 TCP 连接建立是由 TCP 客户主动发起的,因此称为主动打开连接。TCP 服务器进程收到 TCP 连接请求报文段后,如果同意建立连接,则向 TCP 客户进程发送 TCP 连接请求确认报文段,并进入 同步已接收状态。

该报文段首部中的 同步位 SYN 和 确认位 ACK 都设置为 1,表明这是一个 TCP 连接请求确认报文段。序号字段 seq 被设置了一个初始值 y,作为 TCP 服务进程所选择的初始序号。确认号字段 ack 的值被设置成了 x + 1,这是对 TCP 客户进程所选择的初始序号的确认。(注意:这个报文段也不能携带数据,因为它是 SYN 被设置为 1 的报文段,但同样要消耗一个序号。)TCP 客户进程收到 TCP 连接请求确认报文段后,还要向 TCP 服务器进程发送一个普通的 TCP 确认报文段,并进入 连接已建立状态。

该报文段首部中的 确认位 ACK 被设置为 1,表明这是一个普通的 TCP 确认报文段。序号字段 seq 被设置为 x + 1,这是因为 TCP 客户进程发送的第一个 TCP 报文段的序号为 x,并且不携带数据,因此第二个报文段的序号为 x + 1。(注意:TCP 规定普通的 TCP 确认报文段可以携带数据,但如果不携带数据,则不消耗序号。)在这种情况下,所发送的下一个数据报文段的序号仍是 x + 1。确认号字段 ack 被设置为 y + 1,这是对 TCP 服务器进程所选择的初始序号的确认。TCP 服务器进程收到该确认报文段后也进入 连接已建立状态。

现在,TCP 双方都进入了连接已建立状态。它们可以基于已建立好的 TCP 连接,进行可靠的数据传输了。
为什么 TCP 客户端进程最后还要发送一个普通的 TCP 确认报文段呢?能否使用 “两报文握手” 建立连接呢?

在 TCP 服务器第二次进入连接已建立状态时,它认为新的 TCP 连接已建立好了,并一直等待 TCP 客户进程发来数据,这将浪费 TCP 服务器进程所在主机的很多资源。
综上所述,第三次 “握手” 是为了防止已失效的连接请求报文段突然又传送到了 TCP 服务器,因而导致错误。
3. 习题
题一

查看解析

5.8.2 TCP 的运输连接管理 — TCP 的连接释放
1. TCP 通过 “四报文挥手” 来释放连接
现在 TCP 客户进程和 TCP 服务器进程都处于连接已建立状态。假设使用 TCP 客户进程的应用进程通知其主动关闭 TCP 连接,TCP 客户进程会发送 TCP 连接释放报文段并进入 终止等待 1 状态。
该报文段首部中的 终止位 FIN 和 确认位 ACK 的值都被设置为 1,表明这是一个 TCP 连接释放报文段,同时也对之前收到的报文段进行确认。序号 seq 字段的值设置为 u,它等于 TCP 客户进程之前已传送过的、数据的最后一个字节的序号加 1。(注意:TCP 规定终止位 FIN 等于 1 的报文段即使不携带数据,也要消耗掉一个序号。)确认号 ack 字段的值设置为 v,它等于 TCP 客户进程之前已收到的、数据的最后一个字节的序号加 1。TCP 服务器进程收到 TCP 连接释放报文段后,会发送一个普通的 TCP 确认报文段并进入 关闭等待状态。

该报文段首部中 确认位 ACK 的值被设置为 1,表明这是一个普通的 TCP 确认报文段。序号 seq 字段的值设置为 v,它等于 TCP 服务器进程之前已传送过的数据的最后一个字节的序号加 1。这也与之前收到的 TCP 连接释放报文段中的确认号匹配。确认号 ack 字段的值设置为 u+1,这是对 TCP 连接释放报文段的确认。TCP 服务器进程这时应通知高层应用进程:TCP 客户进程要断开与自己的 TCP 连接。此时,从 TCP 客户进程到 TCP 服务器进程这个方向的连接就释放了。这时的 TCP 连接属于 半关闭状态,也就是 TCP 客户进程已经没有数据要发送了,但 TCP 服务器进程如果还有数据要发送,TCP 客户进程仍要接收。也就是说,从 TCP 服务器进程到 TCP 客户进程这个方向的连接并未关闭。这个状态可能会持续一段时间。 TCP 客户进程收到 TCP 确认报文段后就进入 终止等待 2 状态。等待 TCP 服务器进程发出的 TCP 连接释放报文段。


若使用 TCP 服务器进程的应用进程已经没有数据要发送了,应用进程就通知其 TCP 服务器进程释放连接。由于 TCP 连接释放是由 TCP 客户进程主动发起的,因此 TCP 服务器进程对 TCP 连接的释放称为 被动关闭连接。TCP 服务器进程发送 TCP 连接释放报文段并进入 最后确认状态。

该报文段首部中的 终止位 FIN 和 确认位 ACK 的值都被设置为 1,表明这是一个 TCP 连接释放报文段,同时也对之前收到的报文段进行确认。现在假定 序号 seq 字段的值为 w。这是因为在半关闭状态下,TCP 服务器进程可能又发送了一些数据。确认号 ack 字段的值为 u+1,这是对之前收到的 TCP 连接释放报文段的重复确认。TCP 客户进程收到 TCP 连接释放报文段后,必须针对该报文段发送普通的 TCP 确认报文段,之后进入 时间等待状态。


该报文段首部中的 确认位 ACK 的值被设置为 1,表明这是一个普通的 TCP 确认报文段。序号 seq 字段的值设置为 u+1,这是因为 TCP 客户进程之前发送的 TCP 连接释放报文段虽然不携带数据,但要消耗掉一个序号。确认号 ack 字段的值设置为 w+1,这是对所收到的 TCP 连接释放报文段的确认。TCP 服务器进程收到该报文段后就进入 关闭状态。而 TCP 客户进程还要经过 2MSL 后才能进入关闭状态。

MSL 的意思是 最长报文段寿命,RFC793 文档建议为 2 分钟。也就是说 TCP 客户进程进入时间等待状态后。,还要进过 4 分钟才能进入关闭状态。对于现在的网络,MSL 取为 2 分钟可能太长了,因此 TCP 允许不同的实现可根据具体情况使用更小的 MSL 值。

那么,TCP 客户进程在发送完最后一个确认报文段后,为什么不直接进入关闭状态,而是要进入时间等待状态,2MSL 后才进入关闭状态?来看下面这种情况:
TCP 服务器进程发送 TCP 连接释放报文段后进入 最后确认状态。TCP 客户进程收到该报文段后,发送普通的 TCP 确认报文段,并进入关闭状态而不是时间等待状态。然而,该 TCP 确认报文段 丢失 了。这必然会造成 TCP 服务器进程对之前所发送的 TCP 连接释放报文段的 超时重传,并仍处于最后确认状态。重传的 TCP 连接释放报文段到达 TCP 客户进程。由于 TCP 客户进程属于关闭状态,因此不理睬该报文段,这必然会造成 TCP 服务器进程 反复重传 TCP 连接释放报文段,并一直处于最后确认状态而 无法进入关闭状态。

因此,时间等待状态以及处于该状态 2MSL 时长,可以确保 TCP 服务器进程可以收到最后一个 TCP 确认报文段而进入关闭状态。另外,TCP 客户进程在发送完最后一个 TCP 确认报文段后,再经过 2MSL 时长,就可以使本次连接持续时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的 TCP 连接中,不会出现旧连接中的报文段。
以上就是 TCP 通过 “四报文挥手” 释放连接的过程。
2. 保活计时器的作用

TCP 服务器进程每收到一次 TCP 客户进程的数据,就重新设置并启动 保活计时器(2小时定时)。
若保活计时器定时周期内未收到 TCP 客户进程发来的数据,则 当保活计时器到时候,TCP 服务器进程就向 TCP 客户进程发送一个探测报文段,以后则每隔 75 秒发送一次。若一连发送 10 个探测报文段后仍无 TCP 客户进程的响应,TCP 服务器进程就认为 TCP 客户进程所在主机出了故障,接着就关闭这个连接。
5.9 TCP 报文段的首部格式

为了实现可靠传输,TCP 采用了 面向字节流 的方式。
但 TCP 在发送数据时,是从发送缓存取出一部分或全部字节并给其添加一个首部使之成为 TCP报文段 后进行发送。
- 一个 TCP 报文段由 首部 和 数据载荷 两部分构成;
- TCP 的全部功能都体现在它首部中各字段的作用。
1. 源端口和目的端口

- 源端口:占 16 比特,写入源端口号,用来 标识发送该 TCP 报文段的应用进程。
- 目的端口:占 16 比特,写入目的端口号,用来 标识接收该 TCP 报文段的应用进程。

2. 序号、确认号、确认标志位 ACK

- 序号:占 32 比特,取值范围 [0, 232 – 1],序号增加到最后一个后,下一个序号就又回到 0。指出本 TCP 报文段数据载荷的第一个字节的序号。

- 确认号:占 32 比特,取值范围 [0, 232 – 1],序号增加到最后一个后,下一个序号就又回到 0。指出期望收到对方下一个 TCP 报文段的数据载荷的第一个字节的序号,同时也是对之前收到的所有数据的确认。若确认号 = n,则表明到序号 n-1 为止的所有数据都已正确接收,期望接收序号为 n 的数据。
- 确认标志位:取值为 1 时确认号字段才有效;取值为 0 时确认号字段无效。TCP 规定,在连接建立后所有传送的 TCP 报文段都必须把 ACK 置 1。
举例:
TCP 客户进程发送一个 TCP 报文段,该报文段首部中 序号字段 的取值为 201,这表示该 TCP 报文段数据载荷的第一个字节的序号为 201。假设 数据载荷 的长度为 100 字节。首部中 确认号 字段的取值为 800,这表示 TCP 客户进程收到了 TCP 服务器进程发来的序号到 799 为止的全部数据,现在期望收到序号从 800 开始的数据。为了使确认号字段有效,首部中的 确认标志位 ACK 的值必须设置为 1。
TCP 服务器进程收到该报文段后,也给 TCP 客户端进程发送 TCP 报文段。该报文段首部中 序号 字段的取值为 800,这表示该 TCP 报文段数据载荷的第一个字节的序号为 800,这正好与 TCP 客户进程的 确认号 相匹配。假设 数据载荷 的长度为 200 字节。首部中 确认号 字段的取值为 301,这表示 TCP 服务器进程收到了 TCP 客户进程发来的序号到 300 为止的全部数据,现在期望收到序号从 301 开始的数据。为了使确认号字段有效,首部中的 确认标志位 ACK 的值必须设置为 1。


3. 数据偏移

- 数据偏移:占 4 比特,并以 4 字节为单位。用来指出 TCP 报文段的数据载荷部分的起始处距离 TCP 报文段的起始处有多远。这个字段实际上是指出了 TCP 报文段的首部长度。首部固定长度为 20 字节,因此数据偏移字段的最小值为 (0101)2首部最大长度为 60 字节,因此数据偏移字段的最大值为 (1111)2
举例:
因为二进制 0101 的十进制值是 5,而该字段以 4 字节为单位,因此 5 乘以 4 字节等于 20 字节。
因为二进制 1111 的十进制值是 15,而该字段以 4 字节为单位,因此 15 乘以 4 字节等于 60 字节。

4. 保留字段

- 保留:占 6 比特,保留为今后使用,但目前应置为 0。
5. 窗口字段

- 窗口:占 16 比特,以字节为单位。指出发送本报文段的一方的接收窗口。窗口值作为接收方让发送方设置其发送窗口的依据。这是以接收方的接收能力来控制发送方的发送能力,称为流量控制。
6. 校验和

- 校验和:占 16 比特,检查范围包括 TCP 报文段的首部和数据载荷两部分。在计算校验和时,要在 TCP 报文段的前面加上 12 字节的伪首部。
7. 同步标志位 SYN

- 同步标志位 SYN:在 TCP 连接建立时用来同步序号。

8. 终止标志位 FIN

- 终止标志位 FIN:用来释放 TCP 连接。

9. 复位标志位 RST

- 复位标志位 RST:用来复位 TCP 连接。当 RST = 1 时,表明 TCP 连接出现了异常,必须释放连接,然后再重新建立连接。RST 置 1 还用来拒绝一个非法的报文段或拒绝打开一个 TCP 连接。
10. 推送标志位 PSH

- 推送标志位 PSH:接收方的 TCP 收到该标志位为 1 的报文段会 尽快上交应用进程,而不必等到接收缓存都填满后再向上交付。
11. 紧急标志位 URG 和紧急指针字段

- 紧急标志位 URG:取值为 1 时紧急指针字段有效;取值为 0 时紧急指针字段无效。
- 紧急指针:占 16 比特,以字节为单位,用来指明紧急数据的长度。
当发送方有紧急数据时,可将紧急数据插队到发送缓存的最前面,并立刻封装到一个 TCP 报文段中进行发送。紧急指针会指出本报文段数据载荷部分包含了多长的紧急数据,紧急数据之后是普通数据。
12. 选项字段

- 最大报文段长度 MSS 选项:TCP 报文段数据载荷部分的最大长度。
- 窗口扩大选项:为了扩大窗口(提高吞吐率)。
- 时间戳选项:
- 用来计算往返时间 RTT
- 用于处理序号超范围的情况,又称为防止序号绕回 PAWS。
- 选择确认选项:用来实现选择确认功能。
13. 填充字段

- 填充:由于选项的长度可变,因此使用填充来 确保报文段首部能被 4 整除(因为数据偏移字段,也就是首部长度字段,是以 4 字节为单位的)。