跳到主要内容

4. 传输协议 (TRANSPORT PROTOCOLS)

4.1 用户数据报协议 -- UDP (USER DATAGRAM PROTOCOL -- UDP)​

4.1.1 引言 (Introduction)​

用户数据报协议 UDP [UDP:1] 只提供一种最小化的传输服务 —— 不保证数据报的投递 —— 并让应用程序直接访问 IP 层的数据报服务。UDP 供那些不需要 TCP 服务级别的应用使用,或供那些希望使用 TCP 所不具备的通信服务(例如组播或广播投递)的应用使用。

UDP 几乎是一个"空"协议;它在 IP 之上提供的服务只有数据校验和与按端口号进行的多路复用。因此,运行在 UDP 之上的应用程序必须直接处理那些本应由面向连接的协议来处理的端到端通信问题 —— 例如,在需要可靠投递时的重传、打包与重组、流量控制、拥塞避免等。IP 与 TCP 之间相当复杂的耦合关系,将在 UDP 与许多使用 UDP 的应用之间的耦合中得到对应的体现。

4.1.2 协议逐步分析 (Protocol Walk-Through)​

在 UDP 的规范中没有发现已知的错误。

4.1.3 具体问题 (Specific Issues)​

4.1.3.1 端口 (Ports)​

UDP 熟知端口遵循与 TCP 熟知端口相同的规则;见下文第 4.2.2.1 节。

如果到达一个数据报,其目的 UDP 端口上没有处于等待状态的 LISTEN 调用,UDP 应当 (SHOULD) 发送一个 ICMP 端口不可达 (Port Unreachable) 消息。

4.1.3.2 IP 选项 (IP Options)​

UDP 必须 (MUST) 把它从 IP 层接收到的任何 IP 选项透明地传递给应用层。

应用程序 必须 (MUST) 能够指定要在其 UDP 数据报中发送的 IP 选项,UDP 必须 (MUST) 把这些选项传递给 IP 层。

DISCUSSION (讨论):

目前,需要经由 UDP 传递的选项只有源路由 (Source Route)、记录路由 (Record Route) 和时间戳 (Time Stamp)。但是,将来可能会定义新的选项,UDP 不需要也不应当对传递给应用层或来自应用层的选项的格式或内容做任何假设;一个可能的例外是 IP 层的安全选项。

基于 UDP 的应用程序需要从请求数据报中获得源路由,并在发送相应的应答时提供一条反转后的路由。

4.1.3.3 ICMP 消息 (ICMP Messages)​

UDP 必须 (MUST) 把它从 IP 层接收到的所有 ICMP 差错消息传递给应用层。至少在概念上,这可以通过对 ERROR_REPORT 例程的一次向上调用 (upcall) 来完成(见第 4.2.4.1 节)。

DISCUSSION (讨论):

注意,因发送 UDP 数据报而产生的 ICMP 差错消息是异步到达的。希望接收 ICMP 差错消息的基于 UDP 的应用程序,负责维护必要的状态,以便在这些消息到达时对它们进行多路分解;例如,应用程序可以为此保持一个未完成的接收操作。应用程序还负责避免因早先使用相同端口而迟到的 ICMP 差错消息所造成的混淆。

4.1.3.4 UDP 校验和 (UDP Checksums)​

主机 必须 (MUST) 实现生成和校验 UDP 校验和的功能。应用程序 可以 (MAY) 选择性地具备控制是否生成 UDP 校验和的能力,但默认 必须 (MUST) 开启校验和。

如果收到的 UDP 数据报带有非零且无效的校验和,UDP 必须 (MUST) 静默丢弃该数据报。应用程序 可以 (MAY) 选择性地具备控制是丢弃不带校验和的 UDP 数据报还是将其传递给应用层的能力。

DISCUSSION (讨论):

一些通常只在局域网上运行的应用程序为了效率而选择关闭 UDP 校验和。结果,已经报告了大量未检测到的错误案例。究竟是否应当关闭 UDP 校验和是极具争议的。

IMPLEMENTATION (实现说明):

UDP 校验和中存在一个常见的实现错误。与 TCP 校验和不同,UDP 校验和是可选的;UDP 头部中校验和字段的零值表示没有校验和。如果发送方真的计算出了数值为零的 UDP 校验和,它必须把校验和作为全 1(65535)来发送。接收方不需要任何特殊处理,因为在反码算术中零与 65535 是等价的。

4.1.3.5 UDP 多宿主 (UDP Multihoming)​

当收到一个 UDP 数据报时,其特定目的地址 (specific-destination address) 必须 (MUST) 被向上传递给应用层。

应用程序 必须 (MUST) 能够指定用于发送 UDP 数据报的 IP 源地址,或者将其留为未指定(此时由网络软件选择一个合适的源地址)。应当 (SHOULD) 提供一种把所选源地址向上传递给应用层的方法(例如,使应用程序此后只能从对应的接口接收应答数据报)。

DISCUSSION (讨论):

使用 UDP 的请求/应答类应用程序在发送应答时,应当使用与请求的特定目的地址相同的源地址。参见 [INTRO:1] 的 "General Issues" 一节。

4.1.3.6 非法地址 (Invalid Addresses)​

收到的 UDP 数据报若带有非法的 IP 源地址(例如广播或组播地址),必须由 UDP 或 IP 层丢弃(见第 3.2.1.3 节)。

当主机发送 UDP 数据报时,源地址 必须 (MUST) 是该主机的 IP 地址(之一)。

4.1.4 UDP/应用层接口 (UDP/APPLICATION LAYER INTERFACE)​

UDP 的应用接口 必须 (MUST) 提供本文第 3.4 节所述的 IP/传输层接口的全部服务。因此,使用 UDP 的应用程序需要第 3.4 节所述的 GET_SRCADDR()、GET_MAXSIZES()、ADVISE_DELIVPROB() 和 RECV_ICMP() 等调用所提供的功能。例如,GET_MAXSIZES() 可用于获知针对特定 {接口, 远程主机, TOS} 三元组的有效 UDP 最大数据报大小。

应用层程序 必须 (MUST) 能够在发送 UDP 数据报时设置 TTL 和 TOS 值以及 IP 选项,并且这些值必须被透明地传递给 IP 层。UDP 可以 (MAY) 把收到的 TOS 向上传递给应用层。

4.1.5 UDP 要求摘要 (UDP REQUIREMENTS SUMMARY)​

特性 (Feature)章节 (Section)MUSTSHOULDMAYSHOULD NOTMUST NOT
UDP
无监听时发送端口不可达 (Send Port Unreachable)4.1.3.1x
UDP 中的 IP 选项 (IP Options in UDP)
- 将收到的 IP 选项传递给应用层 (- Pass rcv'd IP options to applic layer)4.1.3.2x
- 应用层可在发送时指定 IP 选项 (- Applic layer can specify IP options in Send)4.1.3.2x
- UDP 将 IP 选项下传给 IP 层 (- UDP passes IP options down to IP layer)4.1.3.2x
将 ICMP 消息上传给应用层 (Pass ICMP msgs up to applic layer)4.1.3.3x
UDP 校验和 (UDP checksums)
- 能够生成/校验校验和 (- Able to generate/check checksum)4.1.3.4x
- 静默丢弃坏校验和 (- Silently discard bad checksum)4.1.3.4x
- 发送方可选不生成校验和 (- Sender Option to not generate checksum)4.1.3.4x
- 默认生成校验和 (- Default is to checksum)4.1.3.4x
- 接收方可选要求校验和 (- Receiver Option to require checksum)4.1.3.4x
UDP 多宿主 (UDP Multihoming)
- 将特定目的地址传递给应用 (- Pass spec-dest addr to application)4.1.3.5x
- 应用层可指定本地 IP 地址 (- Applic layer can specify Local IP addr)4.1.3.5x
- 应用层可指定通配本地 IP 地址 (- Applic layer specify wild Local IP addr)4.1.3.5x
- 通知应用层所用的本地 IP 地址 (- Applic layer notified of Local IP addr used)4.1.3.5x
UDP/IP 静默丢弃非法 IP 源地址 (Bad IP src addr silently discarded by UDP/IP)4.1.3.6x
只发送合法的 IP 源地址 (Only send valid IP source address)4.1.3.6x
UDP 应用接口服务 (UDP Application Interface Services)
- 为应用提供 3.4 节的完整 IP 接口 (- Full IP interface of 3.4 for application)4.1.4x
- 发送数据报时可指定 TTL、TOS、IP 选项 (- Able to spec TTL, TOS, IP opts when send dg)4.1.4x
- 将收到的 TOS 上传给应用层 (- Pass received TOS up to applic layer)4.1.4x

4.2 传输控制协议 -- TCP (TRANSMISSION CONTROL PROTOCOL -- TCP)​

4.2.1 引言 (Introduction)​

传输控制协议 TCP [TCP:1] 是互联网协议族中主要的虚电路 (virtual-circuit) 传输协议。TCP 提供全双工八位组 (octet, 8 位字节) 流的、按序的可靠投递。需要可靠的、面向连接的传输服务的应用都会使用 TCP, 例如邮件 (SMTP)、文件传输 (FTP) 和虚拟终端服务 (Telnet); 这些应用层协议的要求在 [INTRO:1] 中描述。

4.2.2 协议逐步分析 (Protocol Walk-Through)​

4.2.2.1 熟知端口 (Well-Known Ports): RFC-793 第 2.7 节​

讨论 (DISCUSSION):

TCP 把 0-255 范围内的端口号保留为 "熟知" (well-known) 端口, 用于访问在整个互联网上标准化的服务. 端口空间的其余部分可以自由分配给应用进程. 当前的熟知端口定义列在题为 "Assigned Numbers" 的 RFC [INTRO:6] 中. 定义一个新的熟知端口的先决条件, 是有一份 RFC 以足够实现新实现的细节记录所提议的服务.

一些系统扩展了这一概念, 增加了 TCP 端口空间的第三种划分: 保留端口 (reserved ports), 通常用于操作系统特定的服务. 例如, 保留端口可能落在 256 与某个依赖于系统的上限之间. 一些系统还选择保护熟知端口与保留端口, 只允许特权用户打开使用这些端口值的 TCP 连接. 只要主机不假定所有主机都以这种方式保护它们的低位端口, 这是完全合理的.

4.2.2.2 PUSH 的使用 (Use of Push): RFC-793 第 2.8 节​

当应用程序发出一系列未设置 PUSH 标志的 SEND 调用时, TCP 可以 (MAY) 在内部聚合数据而不发送. 类似地, 当收到一系列不带 PSH 位的段时, TCP 可以 (MAY) 在内部把数据排队而不传给接收应用.

PSH 位不是记录标记, 也与段的边界无关. 发送方在把数据打包成段时应当 (SHOULD) 合并相继的 PSH 位, 以发送尽可能大的段.

TCP 可以 (MAY) 在 SEND 调用上实现 PUSH 标志. 如果未实现 PUSH 标志, 那么发送方 TCP: (1) 不得无限期地缓冲数据, 并且 (2) 必须 (MUST) 在最后一个被缓冲的段中设置 PSH 位 (即, 当没有更多排队数据要发送时).

RFC-793 第 48, 50 和 74 页的讨论错误地暗示收到的 PSH 标志必须传递给应用层. 把收到的 PSH 标志传递给应用层现在是可选的 (OPTIONAL).

从逻辑上讲, 应用程序每当需要强制交付数据以避免通信死锁时, 就应当在 SEND 调用中设置 PUSH 标志. 然而, TCP 应当 (SHOULD) 尽可能发送最大长度的段, 以提高性能 (见第 4.2.3.4 节).

讨论 (DISCUSSION):

当 SEND 调用上未实现 PUSH 标志时, 也就是当应用/TCP 接口使用纯流模型时, 聚合微小的数据碎片以形成合理大小段的责任部分落在应用层身上.

一般而言, 交互式应用协议至少必须在每条命令或响应序列的最后一个 SEND 调用中设置 PUSH 标志. 像 FTP 这样的批量传输协议应当在文件的最后一个段上设置 PUSH 标志, 或者在需要防止缓冲死锁时设置.

在接收方, PSH 位强制把缓冲的数据交付给应用 (即使收到的数据不足一个完整缓冲). 反过来, 没有 PSH 位可以用来避免对应用进程的不必要唤醒; 对于大型分时主机而言, 这可能是一项重要的性能优化. 把 PSH 位传递给接收应用, 使得应用内部也可以进行类似的优化.

4.2.2.3 窗口大小 (Window Size): RFC-793 第 3.1 节​

窗口大小必须 (MUST) 被当作无符号数处理, 否则大窗口值会表现为负窗口, TCP 将无法工作. 建议 (RECOMMENDED) 实现在连接记录中为发送与接收窗口大小保留 32 位字段, 并全部以 32 位进行窗口计算.

讨论 (DISCUSSION):

众所周知, TCP 头部中的窗口字段对于高速, 长延迟路径来说太小了. 已经定义了扩展窗口大小的实验性 TCP 选项; 例如见 [TCP:11]. 为 anticipation 这样的扩展被采纳, TCP 实现者应当把窗口当作 32 位处理.

4.2.2.4 紧急指针 (Urgent Pointer): RFC-793 第 3.1 节​

第二句是错误的: 紧急指针指向紧急数据序列中最后一个八位组 (LAST, 而非 LAST+1) 的序列号. 第 56 页的描述 (最后一句) 是正确的.

TCP 必须 (MUST) 支持任意长度的紧急数据序列.

TCP 必须 (MUST) 在收到紧急指针且此前没有待处理紧急数据时, 或者紧急指针在数据流中前进时, 异步地通知应用层. 必须有办法让应用得知连接上还剩多少紧急数据待读, 或至少能够判断是否还有更多紧急数据待读.

讨论 (DISCUSSION):

虽然紧急机制可用于任何应用, 但它通常用于向 Telnet 程序发送 "中断" 类命令 (见 [INTRO:1] 的 "Using Telnet Synch Sequence" 一节).

异步或 "带外" 通知使应用得以进入 "紧急模式", 从 TCP 连接读取数据. 这使得控制命令能够发送给这样一个应用: 其常规输入缓冲已满是未处理数据.

实现 (IMPLEMENTATION):

第 4.2.4.1 节描述的通用 ERROR-REPORT() upcall 是把紧急数据到达通知给应用的一种可能机制.

4.2.2.5 TCP 选项 (TCP Options): RFC-793 第 3.1 节​

TCP 必须 (MUST) 能够在任意段中接收 TCP 选项. TCP 必须 (MUST) 无差错地忽略任何它未实现的 TCP 选项, 假定该选项带有长度字段 (未来定义的所有 TCP 选项都将带长度字段). TCP 必须 (MUST) 准备好处理非法的选项长度 (例如零) 而不崩溃; 建议的规程是重置连接并记录原因.

4.2.2.6 最大段大小选项 (Maximum Segment Size Option): RFC-793 第 3.1 节​

TCP 必须 (MUST) 同时实现最大段大小 (MSS) 选项的发送与接收 [TCP:4].

当其接收 MSS 不同于默认值 536 时, TCP 应当 (SHOULD) 在每个 SYN 段中发送 MSS 选项, 并且可以 (MAY) 总是发送.

如果在连接建立时未收到 MSS 选项, TCP 必须 (MUST) 假定默认发送 MSS 为 536 (576-40) [TCP:4].

TCP 实际发送的段的最大尺寸, 即 "有效发送 MSS" (effective send MSS), 必须是发送 MSS (反映远程主机可用的重组缓冲区大小) 与 IP 层允许的最大尺寸两者中较小者:

         Eff.snd.MSS =

min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize

其中:

  • SendMSS 是从远程主机收到的 MSS 值, 若未收到 MSS 选项则为默认值 536.

  • MMS_S 是 TCP 可以发送的传输层消息的最大尺寸.

  • TCPhdrsize 是 TCP 头部的大小; 通常为 20, 但如果要发送 TCP 选项则可能更大.

  • IPoptionsize 是 TCP 将随当前消息传给 IP 层的任何 IP 选项的大小.

在 MSS 选项中发送的 MSS 值必须小于或等于:

         MMS_R - 20

其中 MMS_R 是可以接收 (并重组) 的传输层消息的最大尺寸. TCP 从 IP 层获得 MMS_R 与 MMS_S; 见第 3.4 节的通用调用 GET_MAXSIZES.

讨论 (DISCUSSION):

TCP 段大小的选择对性能有强烈影响. 较大的段通过把头部大小和每数据报处理开销分摊到更多数据字节上来提高吞吐量; 然而, 如果分组大到引起 IP 分片, 那么一旦任何分片丢失, 效率会急剧下降 [IP:9].

一些 TCP 实现只在目的主机位于非直连网络时才发送 MSS 选项. 然而, 一般而言 TCP 层可能没有做出这一决定所需的信息, 因此更可取的做法是把为互联网路径确定合适 MTU 的任务留给 IP 层. 因此我们建议 TCP 总是发送该选项 (如果不是 536), 并由 IP 层按 3.3.3 与 3.4 节的规定确定 MMS_R. 一个拟议的 IP 层 MTU 测量机制随后可以在不改动 TCP 的情况下修改 IP 层.

4.2.2.7 TCP 校验和 (TCP Checksum): RFC-793 第 3.1 节​

与 UDP 校验和 (见第 4.1.3.4 节) 不同, TCP 校验和绝不是可选的. 发送方必须 (MUST) 生成它, 接收方必须 (MUST) 检查它.

4.2.2.8 TCP 连接状态图 (TCP Connection State Diagram): RFC-793 第 3.2 节, 第 23 页​

这张图有几个问题:

(a) 从 SYN-SENT 到 SYN-RCVD 的箭头应标注 "snd SYN,ACK", 以与第 68 页的正文及图 8 一致.

(b) 可以有一条从 SYN-RCVD 状态到 LISTEN 状态的箭头, 条件是在被动打开之后收到了 RST (见第 70 页正文).

(c) 可以从 FIN-WAIT-1 直接进入 TIME-WAIT 状态 (见规范第 75 页).

4.2.2.9 初始序列号选择 (Initial Sequence Number Selection): RFC-793 第 3.3 节, 第 27 页​

TCP 必须 (MUST) 使用规定的由时钟驱动的初始序列号选择方法.

4.2.2.10 同时打开尝试 (Simultaneous Open Attempts): RFC-793 第 3.4 节, 第 32 页​

图 8 有一个错误: 第 7 行的分组应当与第 5 行的分组相同.

TCP 必须 (MUST) 支持同时打开尝试.

讨论 (DISCUSSION):

有时会让实现者感到意外的是: 如果两个应用程序尝试同时相互连接, 只会产生一条连接而不是两条. 这是一个有意的设计决定; 不要试图去 "修复" 它.

4.2.2.11 从旧的重复 SYN 中恢复 (Recovery from Old Duplicate SYN): RFC-793 第 3.4 节, 第 33 页​

注意, TCP 实现 (MUST) 必须跟踪连接是作为被动 OPEN 还是主动 OPEN 的结果而进入 SYN_RCVD 状态的.

4.2.2.12 RST 段 (RST Segment): RFC-793 第 3.4 节​

TCP 应当 (SHOULD) 允许收到的 RST 段携带数据.

讨论 (DISCUSSION):

有人建议 RST 段可以包含解释 RST 原因的 ASCII 文本. 目前尚未为此类数据建立任何标准.

4.2.2.13 关闭连接 (Closing a Connection): RFC-793 第 3.5 节​

TCP 连接可以以两种方式终止: (1) 使用 FIN 握手的正常 TCP 关闭序列, 以及 (2) "中止" (abort), 即发送一个或多个 RST 段并立即丢弃连接状态. 如果 TCP 连接被远端关闭, 本地应用必须 (MUST) 被告知它是正常关闭还是被中止.

正常的 TCP 关闭序列在两个方向上都可靠地交付缓冲数据. 由于 TCP 连接的两个方向是独立关闭的, 连接可能处于 "半关闭" (half closed) 状态, 即只关闭了一个方向, 并且允许主机在半关闭连接的开放方向上继续发送数据.

主机可以 (MAY) 实现 "半双工" TCP 关闭序列, 使得调用了 CLOSE 的应用无法继续从连接读取数据. 如果这样的主机在收到的数据仍在 TCP 中待处理时发出 CLOSE 调用, 或者在 CLOSE 调用之后收到新数据, 其 TCP 应当 (SHOULD) 发送 RST 以表明数据已丢失.

当连接被主动关闭时, 它必须 (MUST) 在 TIME-WAIT 状态逗留 2xMSL (最大段寿命) 的时间. 然而, 它可以 (MAY) 从远端 TCP 接受一个新的 SYN, 直接从 TIME-WAIT 状态重新打开该连接, 只要它:

(1) 为新连接分配的初始序列号大于它在上一代连接上使用过的最大序列号, 并且

(2) 如果该 SYN 结果是一个旧的重复分组, 则返回 TIME-WAIT 状态.

讨论 (DISCUSSION):

TCP 的全双工保数据关闭是类似的 ISO 传输协议 TP4 中没有的特性.

一些系统没有实现半关闭连接, 大概是因为它们不符合其特定操作系统的 I/O 模型. 在这些系统上, 应用一旦调用 CLOSE, 就再也不能从连接读取输入数据; 这被称为 "半双工" TCP 关闭序列.

TCP 的优雅关闭算法要求连接状态在 (至少) 连接的一端保持有定义, 超时周期为 2xMSL, 即 4 分钟. 在此期间, 定义该连接的 (远端套接字, 本地套接字) 对被占用, 不能重用. 为了缩短给定端口对被占用的时间, 一些 TCP 允许在 TIME-WAIT 状态下接受新的 SYN.

4.2.2.14 数据通信 (Data Communication): RFC-793 第 3.7 节, 第 40 页​

自 RFC-793 撰写以来, 在实现高效数据通信的 TCP 算法方面已有大量工作. 本文档后面的各节描述了确定何时发送数据 (第 4.2.3.4 节), 何时发送确认 (第 4.2.3.2 节) 以及何时更新窗口 (第 4.2.3.3 节) 的必需与推荐 TCP 算法.

讨论 (DISCUSSION):

一个重要的性能问题是 "糊涂窗口综合症" (Silly Window Syndrome, SWS) [TCP:5], 即小的增量窗口移动形成稳定模式, 导致极差的 TCP 性能. 避免SWS 的算法在下文针对发送方 (第 4.2.3.4 节) 与接收方 (第 4.2.3.3 节) 分别描述.

简而言之, SWS 的成因是: 接收方只要有任何新的可用缓冲空间就推进右窗口边缘, 而发送方利用任何增量窗口 (无论多小) 发送更多数据 [TCP:5]. 结果可能是一种发送微小数据段的稳定模式, 即便发送方与接收方为该连接准备了很大的总缓冲空间. SWS 只能在大数据量传输期间发生; 如果连接转入静默, 问题就会消失. 它由窗口管理的典型直白实现造成, 但下文给出的发送方与接收方算法可以避免它.

另一个重要的 TCP 性能问题是: 一些应用, 尤其是对按字符交付主机的远程登录, 倾向于发送单八位组数据段的流. 为避免死锁, 这类应用的每个 TCP SEND 调用都必须被 "推入" (pushed), 要么由应用显式进行, 要么由 TCP 隐式进行. 结果可能是每个 TCP 段只含一个数据八位组的流, 这对互联网的利用非常低效, 并加剧互联网拥塞. 第 4.2.3.4 节描述的 Nagle 算法为这一问题提供了简单有效的解决方案. 它确实会把 Telnet 连接上的字符聚成一团; 这可能最初会让习惯单字符回显的用户感到意外, 但用户接受度一直不是问题.

注意, Nagle 算法与发送方 SWS 避免算法在提升性能方面扮演互补的角色. Nagle 算法抑制在待发数据以小增量增长时发送微小段, 而 SWS 避免算法抑制因右窗口边缘小增量推进而产生的小段.

粗心的实现可能对收到的每个数据段发送两个或更多确认段. 例如, 假设接收方对每个数据段立即确认. 当应用程序随后消费了数据并再次增加可用接收缓冲空间时, 接收方可能发送第二个确认段以更新发送方的窗口. 极端情况出现在使用 Telnet 协议提供远程登录服务的 TCP 连接上的单字符段. 曾观察到一些实现: 每个到达的 1 字符段产生三个返回段: (1) 确认, (2) 一字节的窗口增加, 以及 (3) 回显字符.

4.2.2.15 重传超时 (Retransmission Timeout): RFC-793 第 3.7 节, 第 41 页​

RFC-793 建议的计算重传超时的算法如今已知是不充分的; 见下文第 4.2.3.1 节.

Jacobson [TCP:7] 关于互联网拥塞与 TCP 重传稳定性的近期工作, 产生了一种把 "慢启动" (slow start) 与 "拥塞避免" (congestion avoidance) 相结合的传输算法. TCP 必须 (MUST) 实现该算法.

如果重传的分组与原始分组完全相同 (这不仅意味着数据边界没有变化, 还意味着头部的窗口与确认字段没有变化), 则可以 (MAY) 使用相同的 IP Identification 字段 (见第 3.2.1.5 节).

实现 (IMPLEMENTATION):

一些 TCP 实现者选择对数据流 "打包" (packetize), 即在段最初发送时就选定段边界, 并把这些段排入 "重传队列" 直到被确认. 另一种设计 (可能更简单) 是把打包推迟到每次发送或重传数据时进行, 这样就没有段重传队列.

在带有段重传队列的实现中, 当第一次重传超时发生时, 通过对等待确认的段重新打包, 可以提升 TCP 性能. 也就是说, 把能够装下的未完成段合并为一个最大尺寸的段, 使用新的 IP Identification 值. TCP 随后把这个合并段保留在重传队列中直到被确认. 然而, 如果重传队列中的前两个段合计超过一个最大尺寸段, TCP 将只重传第一个段, 并使用原来的 IP Identification 字段.

4.2.2.16 窗口管理 (Managing the Window): RFC-793 第 3.7 节, 第 41 页​

TCP 接收方不应当 (SHOULD NOT) 收缩窗口, 即把右窗口边缘向左移动. 然而, 发送方 TCP 必须 (MUST) 对窗口收缩保持健壮, 窗口收缩可能使 "可用窗口" (useable window, 见第 4.2.3.4 节) 变为负值.

如果发生这种情况, 发送方不应当 (SHOULD NOT) 发送新数据, 但应当 (SHOULD) 正常重传 SND.UNA 与 SND.UNA+SND.WND 之间的旧未确认数据. 发送方还可以 (MAY) 重传超出 SND.UNA+SND.WND 的旧数据, 但不应当 (SHOULD NOT) 在右窗口边缘之外的数据未获确认时使连接超时. 如果窗口收缩为零, TCP 必须 (MUST) 以标准方式探测它 (见下一节).

讨论 (DISCUSSION):

许多 TCP 实现在数据已按更大窗口发送之后窗口从右侧收缩时会陷入混乱. 注意, TCP 有一个启发式规则, 尽管数据报可能乱序, 仍选取最新的窗口更新; 结果, 如果序列号与确认号都没有增加, 它可能忽略一个窗口比先前更小的窗口更新.

4.2.2.17 零窗口探测 (Probing Zero Windows): RFC-793 第 3.7 节, 第 42 页​

必须 (MUST) 支持对零 (提供的) 窗口的探测.

TCP 可以 (MAY) 无限期地保持其提供的接收窗口为关闭状态. 只要接收方 TCP 持续对探测段发送确认, 发送方 TCP 就必须 (MUST) 允许连接保持打开.

讨论 (DISCUSSION):

极其重要的一点是: 不含数据的 ACK (确认) 段并不是由 TCP 可靠传输的. 如果不支持零窗口探测, 当一个重新打开窗口的 ACK 段丢失时, 连接可能永远挂起.

零窗口打开的延迟通常发生在接收应用停止从其 TCP 取数据时. 例如, 考虑一个因为打印机缺纸而停止的打印守护进程应用.

发送主机应当 (SHOULD) 在零窗口存在达到重传超时周期 (见第 4.2.2.15 节) 时发送第一个零窗口探测, 并且应当 (SHOULD) 指数式地增大相继探测之间的间隔.

讨论 (DISCUSSION):

如果零窗口状态是由含窗口打开更新的 ACK 段丢失造成的, 这一规程能把延迟降到最小. 建议采用指数退避, 可带有某个本文未规定的最大间隔. 这一规程与重传算法类似, 在实现中或许可以把两者结合起来.

4.2.2.18 被动 OPEN 调用 (Passive OPEN Calls): RFC-793 第 3.8 节​

每个被动 OPEN 调用要么在 LISTEN 状态创建一个新的连接记录, 要么返回一个错误; 它禁止 (MUST NOT) 影响任何先前创建的连接记录.

支持多个并发用户的 TCP 必须 (MUST) 提供一种 OPEN 调用, 使得在同一本地端口的连接块处于 SYN-SENT 或 SYN-RECEIVED 状态时, 应用程序仍能在该端口上 LISTEN.

讨论 (DISCUSSION):

一些应用 (例如 SMTP 服务器) 可能需要在大约同一时间处理多个连接尝试. 通过给应用提供某种手段, 在较早的连接尝试正在进行三次握手的同时监听新连接, 可以降低连接尝试失败的概率.

实现 (IMPLEMENTATION):

可接受的并发打开实现可以允许多个被动 OPEN 调用, 也可以允许从单个被动 OPEN 调用 "克隆" LISTEN 状态的连接.

4.2.2.19 生存时间 (Time to Live): RFC-793 第 3.9 节, 第 52 页​

RFC-793 规定 TCP 请求 IP 层以 TTL = 60 发送 TCP 段. 这已过时; 用于发送 TCP 段的 TTL 值必须 (MUST) 可配置. 讨论见第 3.2.1.7 节.

4.2.2.20 事件处理 (Event Processing): RFC-793 第 3.9 节​

虽然不是严格必需的, TCP 应当 (SHOULD) 具备为乱序 TCP 段排队的能力. 把第 70 页第一段最后一句中的 "may" 改为 "should".

讨论 (DISCUSSION):

一些小型主机实现由于缓冲空间有限而省略了段排队. 这一省略预计会对 TCP 吞吐量产生不利影响, 因为单个段的丢失会使所有后续段都表现为 "乱序".

一般而言, 收到段的处理必须 (MUST) 实现为: 只要有可能就把 ACK 段聚合起来. 例如, 如果 TCP 正在处理一列排队的段, 它必须在发送任何 ACK 段之前把所有段处理完.

以下是对 RFC-793 事件处理一节的一些详细勘误与注记.

(a) CLOSE 调用, CLOSE-WAIT 状态, 第 61 页: 进入 LAST-ACK 状态, 而不是 CLOSING.

(b) LISTEN 状态, 检查 SYN (第 65, 66 页): 若段带有 SYN 位, 且 security/compartment 或 precedence 对该段而言是错误的, 则发送一个 reset. 正文中给出的 reset 形式有误; 应为:

             <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>

(c) SYN-SENT 状态, 检查 SYN, 第 68 页: 当连接进入 ESTABLISHED 状态时, 必须设置以下变量:

              SND.WND <- SEG.WND
SND.WL1 <- SEG.SEQ
SND.WL2 <- SEG.ACK

(d) 检查安全与优先级, 第 71 页: 第一个标题 "ESTABLISHED STATE" 实际上应当是除 SYN-RECEIVED 之外所有状态的列表: ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK 与 TIME-WAIT.

(e) 检查 SYN 位, 第 71 页: "在 SYN-RECEIVED 状态下, 如果连接是以被动 OPEN 发起的, 则把该连接返回 LISTEN 状态并返回. 否则...".

(f) 检查 ACK 字段, SYN-RECEIVED 状态, 第 72 页: 当连接进入 ESTABLISHED 状态时, 必须设置 (c) 中列出的变量.

(g) 检查 ACK 字段, ESTABLISHED 状态, 第 72 页: 当 SEG.ACK <= SND.UNA 时, 该 ACK 是重复的 (原文遗漏了 =). 类似地, 当 SND.UNA <= SEG.ACK <= SND.NXT 时才应更新窗口.

(h) USER TIMEOUT, 第 77 页:

更好的做法是把超时通知应用, 而不是让 TCP 强制关闭连接. 不过, 亦见第 4.2.3.5 节.

4.2.2.21 确认排队的段 (Acknowledging Queued Segments): RFC-793 第 3.9 节​

当一个有效段到达, 它在窗口之内但不在左窗口边缘时, TCP 可以 (MAY) 发送一个确认 RCV.NXT 的 ACK 段.

讨论 (DISCUSSION):

RFC-793 (见第 74 页) 对收到乱序段 (即 SEG.SEQ 不等于 RCV.NXT) 时是否应发送 ACK 段含糊其辞.

对乱序段进行确认的原因之一, 可能是支持一种称为 "快速重传" (fast retransmit) 的实验性算法. 使用该算法时, 发送方利用 "冗余" ACK 在重传计时器到期之前推断某个段已丢失. 它统计收到相同 SEG.ACK 值且相同右窗口边缘的 ACK 的次数. 如果收到的此类 ACK 超过阈值次数, 则假定包含从 SEG.ACK 开始的八位组的段已丢失并重传之, 而不必等待超时. 阈值的选取需补偿互联网中可能的最大段重排. 目前还没有足够的快速重传算法经验来确定其效用.

4.2.3 具体问题 (Specific Issues)​

4.2.3.1 重传超时的计算 (Retransmission Timeout Calculation)​

主机 TCP 必须 (MUST) 实现 Karn 算法与 Jacobson 算法来计算重传超时 ("RTO").

  • Jacobson 计算平滑往返时间 ("RTT") 的算法纳入了对方差的一个简单度量 [TCP:7].

  • Karn 选择 RTT 测量值的算法确保含糊的往返时间不会破坏平滑往返时间的计算 [TCP:6].

这一实现还必须 (MUST) 对同一段的相继 RTO 值包含 "指数退避" (exponential backoff). SYN 段的重传应当 (SHOULD) 使用与数据段相同的算法.

讨论 (DISCUSSION):

RFC-793 规定的 RTO 计算存在两个已知问题. 第一, 存在重传时难以准确测量 RTT. 第二, 计算平滑往返时间的算法不充分 [TCP:7], 因为它错误地假定 RTT 值的方差会很小且恒定. 这两个问题分别由 Karn 算法与 Jacobson 算法解决.

使用这些改进带来的性能提升从可察觉到戏剧性不等. Jacobson 纳入实测 RTT 方差的算法在低速链路上尤为重要, 因为分组大小的自然变化会引起 RTT 的大幅变化. 一家厂商发现, 在 TCP 中实现 Jacobson 方差算法之后, 9.6kb 线路的利用率从 10% 提升到了 90%.

以下数值应当 (SHOULD) 用于为新连接初始化估计参数:

(a) RTT = 0 秒.

(b) RTO = 3 秒. (平滑方差应初始化为能产生该 RTO 的值.)

众所周知, 对 RTO 推荐的上下界在大型互联网上是不充分的. 下界应当 (SHOULD) 以秒的分数来度量 (以适应高速 LAN), 上界应为 2*MSL, 即 240 秒.

讨论 (DISCUSSION):

经验表明这些初始化值是合理的, 并且无论如何, Karn 与 Jacobson 算法使 TCP 行为对初始参数的选择相当不敏感.

4.2.3.2 何时发送 ACK 段 (When to Send an ACK Segment)​

正在接收 TCP 数据段流的主机, 可以通过每收到多个数据段才发送少于对应数量的 ACK (确认) 段来同时提高互联网与主机两方面的效率; 这就是所谓的 "延迟确认" (delayed ACK) [TCP:5].

TCP 应当 (SHOULD) 实现延迟确认, 但 ACK 不应被过度延迟; 特别是, 延迟必须 (MUST) 小于 0.5 秒, 并且在满尺寸段的流中, 应当 (SHOULD) 至少每两个段有一个 ACK.

讨论 (DISCUSSION):

延迟确认给了应用更新窗口并可能立即发送响应的机会. 特别是, 在字符模式远程登录的情形下, 延迟确认可以把服务器发送的段数减少为原来的 1/3 (ACK, 窗口更新与回显字符合并在一个段中).

此外, 在一些大型多用户主机上, 延迟确认可以通过减少需要处理的分组总数来显著降低协议处理开销 [TCP:5]. 然而, ACK 的过度延迟会干扰往返时间测量与分组 "时钟" 算法 [TCP:7].

4.2.3.3 何时发送窗口更新 (When to Send a Window Update)​

TCP 必须 (MUST) 在接收方包含 SWS 避免算法 [TCP:5].

实现 (IMPLEMENTATION):

接收方的 SWS 避免算法决定右窗口边缘何时可以推进; 这通常称为 "更新窗口". 该算法与延迟确认算法 (见第 4.2.3.2 节) 结合, 共同决定含当前窗口的 ACK 段何时真正发送给接收方. 我们使用 RFC-793 的记法; 见该文档的图 4 与图 5.

接收方 SWS 的解决方案是: 避免以小增量推进右窗口边缘 RCV.NXT+RCV.WND, 即使数据是以小段从网络收到的.

假设总接收缓冲空间为 RCV.BUFF. 在任一时刻, 该总量中可能有 RCV.USER 个八位组被已被接收并确认但用户进程尚未消费的数据占用. 当连接静默时, RCV.WND = RCV.BUFF 且 RCV.USER = 0.

在数据到达并被确认时保持右窗口边缘固定, 要求接收方提供的空间小于其全部缓冲空间, 即接收方必须规定一个 RCV.WND, 使得当 RCV.NXT 增加时 RCV.NXT+RCV.WND 保持恒定. 因此, 总缓冲空间 RCV.BUFF 一般划分为三个部分:

           |<------- RCV.BUFF ---------------->|
1 2 3
----|---------|------------------|------|----
RCV.NXT ^
(Fixed)

1 - RCV.USER = data received but not yet consumed;
2 - RCV.WND = space advertised to sender;
3 - Reduction = space available but not yet
advertised.

针对接收方建议的 SWS 避免算法是: 保持 RCV.NXT+RCV.WND 固定, 直到缩减量满足:

                RCV.BUFF - RCV.USER - RCV.WND  >=

min( Fr * RCV.BUFF, Eff.snd.MSS )

其中 Fr 是一个分数, 推荐值为 1/2, Eff.snd.MSS 是该连接的有效发送 MSS (见第 4.2.2.6 节). 当不等式满足时, 把 RCV.WND 置为 RCV.BUFF-RCV.USER.

注意, 该算法的总体效果是以 Eff.snd.MSS 为增量推进 RCV.WND (对于现实的接收缓冲: Eff.snd.MSS < RCV.BUFF/2). 另注意, 接收方必须使用它自己的 Eff.snd.MSS, 并假定它与发送方的相同.

4.2.3.4 何时发送数据 (When to Send Data)​

TCP 必须 (MUST) 在发送方包含 SWS 避免算法.

TCP 应当 (SHOULD) 实现 Nagle 算法 [TCP:9] 以合并短段. 然而, 必须有 (MUST) 办法让应用在单个连接上停用 Nagle 算法. 在所有情况下, 发送数据还受慢启动算法 (第 4.2.2.15 节)施加的限制约束.

讨论 (DISCUSSION):

Nagle 算法大致如下:

                如果存在未确认的数据 (即 SND.NXT >
SND.UNA), 那么发送方 TCP 缓冲所有用户
数据 (无论 PSH 位如何), 直到未完成数据
被确认, 或者直到 TCP 能够发送一个满尺寸
段 (Eff.snd.MSS 字节; 见第 4.2.2.6 节)。

一些应用 (例如实时显示窗口更新) 要求关闭 Nagle 算法, 以便小数据段能以最大速率流出.

实现 (IMPLEMENTATION):

发送方的 SWS 避免算法比接收方的更难, 因为发送方 (直接) 不知道接收方的总缓冲空间 RCV.BUFF. 一种被发现行之有效的方法是: 发送方计算 Max(SND.WND), 即它在该连接上迄今见过的最大发送窗口, 并把该值用作 RCV.BUFF 的估计. 遗憾的是, 这只能是一个估计; 接收方可能随时缩小 RCV.BUFF. 为避免由此造成的死锁, 必须有一个超时来强制发送数据, 覆盖 SWS 避免算法. 实践中, 这一超时应很少触发.

"可用窗口" [TCP:5] 为:

                U = SND.UNA + SND.WND - SND.NXT

即提供的窗口减去已发送但未确认的数据量. 设 D 为发送方 TCP 中已排队但尚未发送的数据量, 推荐使用如下规则集发送数据:

(1) 如果可以发送一个最大尺寸的段, 即:

                     min(D,U) >= Eff.snd.MSS;

(2) 或者数据被推入 (pushed) 且所有排队数据现在都能发送, 即:

                    [SND.NXT = SND.UNA and] PUSHED and D <= U

(方括号中的条件由 Nagle 算法施加);

(3) 或者至少可以发送最大窗口的 Fs 分数, 即:

                    [SND.NXT = SND.UNA and]

min(D.U) >= Fs * Max(SND.WND);

(4) 或者数据被 PUSH 且覆盖超时 (override timeout) 到期.

这里 Fs 是一个分数, 推荐值为 1/2. 覆盖超时应取 0.1 至 1.0 秒之间. 把该计时器与用于探测零窗口的计时器 (第 4.2.2.17 节) 合并可能会很方便.

最后, 注意应当使用上文规定的 SWS 避免算法, 而不是 [TCP:5] 中包含的发送方算法.

4.2.3.5 TCP 连接失败 (TCP Connection Failures)​

TCP 对同一段的过度重传表明远端主机或互联网路径发生了某种故障. 这种故障的持续时间可长可短. 必须使用 (MUST) 下述规程来处理数据段的过度重传 [IP:11]:

(a) 有两个阈值 R1 与 R2, 度量同一段已发生的重传量. R1 与 R2 可以用时间单位度量, 也可以用重传次数度量.

(b) 当同一段的传输次数达到或超过阈值 R1 时, 向 IP 层传递否定建议 (negative advice, 见第 3.3.1.4 节), 以触发死网关诊断.

(c) 当同一段的传输次数达到大于 R1 的阈值 R2 时, 关闭连接.

(d) 应用必须 (MUST) 能够为特定连接设置 R2 的值. 例如, 交互式应用可能把 R2 设为 "无穷大", 把断开时机交给用户控制.

(d) TCP 应当 (SHOULD) 在达到 R1 而未到 R2 时把交付问题告知应用 (除非应用已停用此类信息; 见第 4.2.4.1 节). 例如, 这可以让远程登录 (User Telnet) 应用程序通知用户.

R1 的值应当 (SHOULD) 对应当前 RTO 下至少 3 次重传. R2 的值应当 (SHOULD) 对应至少 100 秒.

打开 TCP 连接的尝试可能因 SYN 段的过度重传而失败, 或因收到 RST 段或 ICMP Port Unreachable 而失败. SYN 重传必须 (MUST) 按刚才针对数据重传描述的一般方式处理, 包括通知应用层.

然而, SYN 与数据段的 R1 和 R2 值可以不同. 特别是, SYN 段的 R2 必须 (MUST) 设置得足够大, 以便该段的重传至少持续 3 分钟. 当然, 应用可以更早关闭连接 (即放弃打开尝试).

讨论 (DISCUSSION):

一些互联网路径有显著的建立时间, 且这类路径的数量未来可能增加.

4.2.3.6 TCP 保活 (TCP Keep-Alives)​

实现者可以 (MAY) 在其 TCP 实现中包含 "保活" (keep-alives), 尽管这一做法并未被普遍接受. 如果包含保活, 应用必须 (MUST) 能够针对每条 TCP 连接打开或关闭它们, 并且它们必须 (MUST) 默认为关闭.

保活分组必须 (MUST) 只在一个间隔内该连接没有收到任何数据或确认分组时才发送. 该间隔必须 (MUST) 可配置, 并且必须 (MUST) 默认不小于两小时.

极其重要的一点是: 不含数据的 ACK 段并不是由 TCP 可靠传输的. 因此, 如果实现了保活机制, 它禁止 (MUST NOT) 把对任何特定探测的未响应解释为连接已死.

实现应当 (SHOULD) 发送不含数据的保活段; 然而, 它可以 (MAY) 被配置为发送含一个垃圾八位组的保活段, 以兼容有缺陷的 TCP 实现.

讨论 (DISCUSSION):

"保活" 机制在连接本来空闲时周期性地探测连接的另一端, 即使没有数据要发送. TCP 规范不包含保活机制, 因为它可能: (1) 在互联网瞬时故障期间弄断完全良好的连接; (2) 消耗不必要的带宽 ("如果没人在用这条连接, 谁在乎它是否还活着?"); (3) 对按分组计费的互联网路径产生费用.

然而, 一些 TCP 实现包含了保活机制. 为确认一条空闲连接仍然活跃, 这些实现发送一个旨在引发对端 TCP 响应的探测段. 这样的段通常含有 SEG.SEQ = SND.NXT-1, 可能包含也可能不包含一个垃圾数据八位组. 注意, 在静默连接上 SND.NXT = RCV.NXT, 因此该 SEG.SEQ 将落在窗口之外. 于是, 探测使接收方返回一个确认段, 证明连接仍然存活. 如果对端因网络分区或崩溃而丢弃了连接, 它会以 RST 而不是确认段作为响应.

遗憾的是, 一些行为不当的 TCP 实现在段不含数据时, 对 SEG.SEQ = SND.NXT-1 的段不予响应. 作为替代, 实现也可以先确定对端能否对不含垃圾数据八位组的保活分组正确响应.

TCP 保活机制只应当被那些可能因客户端在网络故障期间崩溃或中止连接而无限期挂起并无谓消耗资源的服务器应用调用.

4.2.3.7 TCP 多宿主 (TCP Multihoming)​

如果多宿主主机上的应用在主动打开 TCP 连接时未指定本地 IP 地址, 那么 TCP 在发送 (第一个) SYN 之前必须 (MUST) 请求 IP 层选择一个本地 IP 地址. 见第 3.4 节的函数 GET_SRCADDR().

在所有其他时刻, 该连接上此前已有段被发送或接收, TCP 必须 (MUST) 使用与那些先前段相同的本地地址.

4.2.3.8 IP 选项 (IP Options)​

当收到的选项由 IP 层上交 TCP 时, TCP 必须 (MUST) 忽略它不理解的选项.

TCP 可以 (MAY) 支持 Time Stamp 与 Record Route 选项.

应用在主动打开 TCP 连接时必须 (MUST) 能够指定源路由, 并且它必须 (MUST) 优先于在数据报中收到的源路由.

当 TCP 连接以被动方式 OPEN, 且一个带有完整 IP Source Route 选项 (含返回路由) 的分组到达时, TCP 必须 (MUST) 保存该返回路由, 并把它用于在该连接上发送的所有段. 如果后续段中到达了不同的源路由, 后到的定义应当 (SHOULD) 覆盖先前的.

4.2.3.9 ICMP 消息 (ICMP Messages)​

TCP 必须 (MUST) 对 IP 层上交的 ICMP 错误消息采取行动, 并把它定向到引发该错误的连接. 必要的多路分解信息可在 ICMP 消息所含的 IP 头部中找到.

  • Source Quench (源抑制)

    TCP 必须 (MUST) 对 Source Quench 做出反应, 减慢该连接上的传输. 推荐的 (RECOMMENDED) 规程是让 Source Quench 触发一次 "慢启动", 就像发生了一次重传超时那样.

  • Destination Unreachable -- 代码 0, 1, 5

    由于这些 Unreachable 消息指示软错误状态, TCP 禁止 (MUST NOT) 中止连接, 并且应当 (SHOULD) 把该信息提供给应用.

    讨论 (DISCUSSION):

    TCP 可以通过调用 ERROR_REPORT 例程的 upcall 把软错误状态直接报告给应用层, 也可以仅记录该消息, 只在 TCP 连接超时时才向应用报告.

  • Destination Unreachable -- 代码 2-4

    这些是硬错误状态, 因此 TCP 应当 (SHOULD) 中止连接.

  • Time Exceeded -- 代码 0, 1

    应按 Destination Unreachable 代码 0, 1, 5 的方式处理 (见上).

  • Parameter Problem (参数问题)

    应按 Destination Unreachable 代码 0, 1, 5 的方式处理 (见上).

4.2.3.10 远程地址验证 (Remote Address Validation)​

TCP 实现必须 (MUST) 把针对无效远程 IP 地址 (例如广播或组播地址) 的本地 OPEN 调用作为错误拒绝.

带有无效源地址的入 SYN 必须由 TCP 或 IP 层忽略 (见第 3.2.1.3 节).

TCP 实现必须 (MUST) 静默丢弃寻址到广播或组播地址的入 SYN 段.

4.2.3.11 TCP 流量模式 (TCP Traffic Patterns)​

实现 (IMPLEMENTATION):

TCP 协议规范 [TCP:1] 给了实现者很大的自由度来设计控制连接上消息流的算法 -- 打包, 窗口管理, 发送确认等. 这些设计决策之所以困难, 是因为 TCP 必须适应大范围的流量模式. 经验表明, TCP 实现者需要在两种极端流量模式下验证设计:

  • 单字符段 (Single-character Segments)

    即使发送方使用了 Nagle 算法, 当 TCP 连接跨越低时延 LAN 携带远程登录流量时, 接收方一般会收到单字符段的流. 如果远程终端回显模式生效, 接收方系统通常会随每个字符的到达而回显.

  • 批量传输 (Bulk Transfer)

    当 TCP 用于批量传输时, 数据流应当 (几乎) 完全由有效 MSS 大小的段组成. 尽管 TCP 使用具有字节 (八位组) 粒度的序列号空间, 在批量传输模式下, 其运行应当表现得如同 TCP 使用了只按段计数的序列号空间.

经验进一步表明, 单个 TCP 能够有效且高效地处理这两个极端.

验证新 TCP 实现最重要的工具是分组跟踪程序. 大量经验表明, 用其他 TCP 实现跟踪各种流量模式并仔细研究结果非常重要.

4.2.3.12 效率 (Efficiency)​

实现 (IMPLEMENTATION):

大量经验引出了以下高效实现 TCP 的建议:

(a) 不要复制数据 (Don't Copy Data)

在批量数据传输中, 最耗 CPU 的任务是数据复制与数据校验和计算. 把 TCP 数据的复制次数降到最少至关重要. 由于最终的速度限制可能来自跨内存总线取数, 把复制与校验和计算结合起来, 用一次内存读取同时完成两者, 可能是有益的.

(b) 手工打造校验和例程 (Hand-Craft the Checksum Routine)

一个好的 TCP 校验和例程通常比定义的简单直接实现快两到五倍. 要让校验和代码 "快如闪电", 通常需要非常细心与巧妙的编码, 而且这样做是值得的. 见 [TCP:10].

(c) 为常见情形编码 (Code for the Common Case)

TCP 协议处理可以很复杂, 但对大多数段而言, 只需做出少数几个简单决定. 通过把主干代码编写为在最常见情形下最小化判定数量, 可以大大加速每段处理.

4.2.4 TCP/应用层接口 (TCP/APPLICATION LAYER INTERFACE)​

4.2.4.1 异步报告 (Asynchronous Reports)​

必须有 (MUST) 一种机制把 TCP 的软错误状态报告给应用. 一般地, 我们假定它采取应用提供的 ERROR_REPORT 例程的形式, 可以由传输层异步地 upcall [INTRO:7]:

         ERROR_REPORT(local connection name, reason, subreason)

reason 与 subreason 参数的精确编码此处不做规定. 但是, 异步报告给应用的条件必须 (MUST) 包括:

  • 到达了 ICMP 错误消息 (见 4.2.3.9)

  • 过度重传 (见 4.2.3.5)

  • 紧急指针前进 (见 4.2.2.4).

然而, 不希望接收此类 ERROR_REPORT 调用的应用程序, 应当 (SHOULD) 能够有效地停用这些调用.

讨论 (DISCUSSION):

这些错误报告通常反映的是软错误, 许多应用可以不加理会而无害. 有人建议这些错误报告调用应默认为 "停用", 但这并非要求.

4.2.4.2 服务类型 (Type-of-Service)​

应用层必须 (MUST) 能够为连接上发送的段指定服务类型 (TOS). 不作强制要求, 但应用应当 (SHOULD) 能够在连接存续期间改变 TOS. TCP 在该连接上发送段时, 应当 (SHOULD) 把当前 TOS 值不加改变地传给 IP 层.

TOS 在连接的两个方向上独立指定, 从而接收方应用将指定用于 ACK 段的 TOS.

TCP 可以 (MAY) 把最近收到的 TOS 上交应用.

讨论 (DISCUSSION):

一些应用 (例如 SMTP) 在连接存续期间改变其通信的性质, 因此希望改变 TOS 规定.

另请注意, RFC-793 规定的 OPEN 调用包含一个参数 ("options"), 调用者可以在其中指定 IP 选项, 例如源路由, 记录路由或时间戳.

4.2.4.3 FLUSH 调用 (Flush Call)​

一些 TCP 实现包含了 FLUSH 调用, 它会清空 TCP 发送队列中用户已发出 SEND 调用但仍在当前发送窗口右侧的全部数据. 也就是说, 它在不丢失序列号同步的前提下, 尽可能多地冲刷排队的发送数据. 这对实现 Telnet 的 "中止输出" (abort output) 功能很有用.

4.2.4.4 多宿主 (Multihoming)​

RFC-793 第 2.7 和 3.8 节概述的用户接口需要为多宿主进行扩展. OPEN 调用必须 (MUST) 有一个可选参数:

          OPEN( ... [local IP address,] ... )

以允许指定本地 IP 地址.

讨论 (DISCUSSION):

一些基于 TCP 的应用需要指定用于打开特定连接的本地 IP 地址; FTP 就是一个例子.

实现 (IMPLEMENTATION):

带有指定 "local IP address" 参数的被动 OPEN 调用将等待指向该地址的入连接请求. 如果未指定该参数, 被动 OPEN 将等待指向任何本地 IP 地址的入连接请求, 然后把连接的本地 IP 地址绑定为实际使用的那个特定地址.

对于主动 OPEN 调用, 指定的 "local IP address" 参数将用于打开该连接. 如果未指定该参数, 网络软件将为该连接选择一个合适的本地 IP 地址 (见第 3.3.4.2 节).

4.2.5 TCP 要求摘要 (TCP REQUIREMENT SUMMARY)​

                                           |        | | | |S| |
                                           |        | | | |H| |F
                                           |        | | | |O|M|o
                                           |        | | | |U|U|o
                                           |        | |S| |L|S|t
                                           |        | |H| |D|T|n
                                           |        |M|O| | | |o
                                           |        |U|U|M|N|N|t
                                           |        |S|L|A|O|O|t
FEATURE                                          |SECTION | | | |T|T|e
-------------------------------------------------|--------|-|-|-|-|-|--
                                           |        | | | | | |
Push flag                                        |        | | | | | |
  Aggregate or queue un-pushed data              |4.2.2.2 | | |x| | |
  Sender collapse successive PSH flags           |4.2.2.2 | |x| | | |
  SEND call can specify PUSH                     |4.2.2.2 | | |x| | |
    If cannot: sender buffer indefinitely        |4.2.2.2 | | | | |x|
    If cannot: PSH last segment                  |4.2.2.2 |x| | | | |
  Notify receiving ALP of PSH                    |4.2.2.2 | | |x| | |1
  Send max size segment when possible            |4.2.2.2 | |x| | | |
                                           |        | | | | | |
Window                                           |        | | | | | |
  Treat as unsigned number                       |4.2.2.3 |x| | | | |
  Handle as 32-bit number                        |4.2.2.3 | |x| | | |
  Shrink window from right                       |4.2.2.16| | | |x| |
  Robust against shrinking window                |4.2.2.16|x| | | | |
  Receiver's window closed indefinitely          |4.2.2.17| | |x| | |
  Sender probe zero window                       |4.2.2.17|x| | | | |
    First probe after RTO                        |4.2.2.17| |x| | | |
    Exponential backoff                          |4.2.2.17| |x| | | |
  Allow window stay zero indefinitely            |4.2.2.17|x| | | | |
  Sender timeout OK conn with zero wind          |4.2.2.17| | | | |x|
                                           |        | | | | | |
Urgent Data                                      |        | | | | | |
  Pointer points to last octet                   |4.2.2.4 |x| | | | |
  Arbitrary length urgent data sequence          |4.2.2.4 |x| | | | |
  Inform ALP asynchronously of urgent data       |4.2.2.4 |x| | | | |1
  ALP can learn if/how much urgent data Q'd      |4.2.2.4 |x| | | | |1
                                           |        | | | | | |
TCP Options                                      |        | | | | | |
  Receive TCP option in any segment              |4.2.2.5 |x| | | | |
  Ignore unsupported options                     |4.2.2.5 |x| | | | |
  Cope with illegal option length                |4.2.2.5 |x| | | | |
  Implement sending & receiving MSS option       |4.2.2.6 |x| | | | |
  Send MSS option unless 536                     |4.2.2.6 | |x| | | |
  Send MSS option always                         |4.2.2.6 | | |x| | |
  Send-MSS default is 536                        |4.2.2.6 |x| | | | |
  Calculate effective send seg size              |4.2.2.6 |x| | | | |
                                           |        | | | | | |
TCP Checksums                                    |        | | | | | |
  Sender compute checksum                        |4.2.2.7 |x| | | | |
  Receiver check checksum                        |4.2.2.7 |x| | | | |
                                           |        | | | | | |
Use clock-driven ISN selection                   |4.2.2.9 |x| | | | |
                                           |        | | | | | |
Opening Connections                              |        | | | | | |
  Support simultaneous open attempts             |4.2.2.10|x| | | | |
  SYN-RCVD remembers last state                  |4.2.2.11|x| | | | |
  Passive Open call interfere with others        |4.2.2.18| | | | |x|
  Function: simultan. LISTENs for same port      |4.2.2.18|x| | | | |
  Ask IP for src address for SYN if necc.        |4.2.3.7 |x| | | | |
    Otherwise, use local addr of conn.           |4.2.3.7 |x| | | | |
  OPEN to broadcast/multicast IP Address         |4.2.3.14| | | | |x|
  Silently discard seg to bcast/mcast addr       |4.2.3.14|x| | | | |
                                           |        | | | | | |
Closing Connections                              |        | | | | | |
  RST can contain data                           |4.2.2.12| |x| | | |
  Inform application of aborted conn             |4.2.2.13|x| | | | |
  Half-duplex close connections                  |4.2.2.13| | |x| | |
    Send RST to indicate data lost               |4.2.2.13| |x| | | |
  In TIME-WAIT state for 2xMSL seconds           |4.2.2.13|x| | | | |
    Accept SYN from TIME-WAIT state              |4.2.2.13| | |x| | |
                                           |        | | | | | |
Retransmissions                                  |        | | | | | |
  Jacobson Slow Start algorithm                  |4.2.2.15|x| | | | |
  Jacobson Congestion-Avoidance algorithm        |4.2.2.15|x| | | | |
  Retransmit with same IP ident                  |4.2.2.15| | |x| | |
  Karn's algorithm                               |4.2.3.1 |x| | | | |
  Jacobson's RTO estimation alg.                 |4.2.3.1 |x| | | | |
  Exponential backoff                            |4.2.3.1 |x| | | | |
  SYN RTO calc same as data                      |4.2.3.1 | |x| | | |
  Recommended initial values and bounds          |4.2.3.1 | |x| | | |
                                           |        | | | | | |
Generating ACK's:                                |        | | | | | |
  Queue out-of-order segments                    |4.2.2.20| |x| | | |
  Process all Q'd before send ACK                |4.2.2.20|x| | | | |
  Send ACK for out-of-order segment              |4.2.2.21| | |x| | |
  Delayed ACK's                                  |4.2.3.2 | |x| | | |
    Delay < 0.5 seconds                          |4.2.3.2 |x| | | | |
    Every 2nd full-sized segment ACK'd           |4.2.3.2 |x| | | | |
  Receiver SWS-Avoidance Algorithm               |4.2.3.3 |x| | | | |
                                           |        | | | | | |
Sending data                                     |        | | | | | |
  Configurable TTL                               |4.2.2.19|x| | | | |
  Sender SWS-Avoidance Algorithm                 |4.2.3.4 |x| | | | |
  Nagle algorithm                                |4.2.3.4 | |x| | | |
    Application can disable Nagle algorithm      |4.2.3.4 |x| | | | |
                                           |        | | | | | |
Connection Failures:                             |        | | | | | |
  Negative advice to IP on R1 retxs              |4.2.3.5 |x| | | | |
  Close connection on R2 retxs                   |4.2.3.5 |x| | | | |
  ALP can set R2                                 |4.2.3.5 |x| | | | |1
  Inform ALP of  R1<=retxs<R2                    |4.2.3.5 | |x| | | |1
  Recommended values for R1, R2                  |4.2.3.5 | |x| | | |
  Same mechanism for SYNs                        |4.2.3.5 |x| | | | |
    R2 at least 3 minutes for SYN                |4.2.3.5 |x| | | | |
                                           |        | | | | | |
Send Keep-alive Packets:                         |4.2.3.6 | | |x| | |
  - Application can request                     |4.2.3.6 |x| | | | |
  - Default is "off"                            |4.2.3.6 |x| | | | |
  - Only send if idle for interval              |4.2.3.6 |x| | | | |
  - Interval configurable                       |4.2.3.6 |x| | | | |
  - Default at least 2 hrs.                     |4.2.3.6 |x| | | | |
  - Tolerant of lost ACK's                      |4.2.3.6 |x| | | | |
                                           |        | | | | | |
IP Options                                       |        | | | | | |
  Ignore options TCP doesn't understand          |4.2.3.8 |x| | | | |
  Time Stamp support                             |4.2.3.8 | | |x| | |
  Record Route support                           |4.2.3.8 | | |x| | |
  Source Route:                                  |        | | | | | |
    ALP can specify                              |4.2.3.8 |x| | | | |1
    Overrides src rt in datagram                 |4.2.3.8 |x| | | | |
    Build return route from src rt               |4.2.3.8 |x| | | | |
    Later src route overrides                    |4.2.3.8 | |x| | | |
                                           |        | | | | | |
Receiving ICMP Messages from IP                  |4.2.3.9 |x| | | | |
  Dest. Unreach (0,1,5) => inform ALP            |4.2.3.9 | |x| | | |
  Dest. Unreach (0,1,5) => abort conn            |4.2.3.9 | | | | |x|
  Dest. Unreach (2-4) => abort conn              |4.2.3.9 | |x| | | |
  Source Quench => slow start                    |4.2.3.9 | |x| | | |
  Time Exceeded => tell ALP, don't abort         |4.2.3.9 | |x| | | |
  Param Problem => tell ALP, don't abort         |4.2.3.9 | |x| | | |
                                           |        | | | | | |
Address Validation                               |        | | | | | |
  Reject OPEN call to invalid IP address         |4.2.3.10|x| | | | |
  Reject SYN from invalid IP address             |4.2.3.10|x| | | | |
  Silently discard SYN to bcast/mcast addr       |4.2.3.10|x| | | | |
                                           |        | | | | | |
TCP/ALP Interface Services                       |        | | | | | |
  Error Report mechanism                         |4.2.4.1 |x| | | | |
  ALP can disable Error Report Routine           |4.2.4.1 | |x| | | |
  ALP can specify TOS for sending                |4.2.4.2 |x| | | | |
    Passed unchanged to IP                       |4.2.4.2 | |x| | | |
  ALP can change TOS during connection           |4.2.4.2 | |x| | | |
  Pass received TOS up to ALP                    |4.2.4.2 | | |x| | |
  FLUSH call                                     |4.2.4.3 | | |x| | |
  Optional local IP addr parm. in OPEN           |4.2.4.4 |x| | | | |
-------------------------------------------------|--------|-|-|-|-|-|--
-------------------------------------------------|--------|-|-|-|-|-|--

FOOTNOTES:

(1)  "ALP" means Application-Layer program.

注: 以上要求摘要表为英文原文原样保留 (列含义: MUST / SHOULD / MAY / SHOULD NOT / MUST NOT / FOOTNOTE; "ALP" 指应用层程序).