1、第二章 TCP 协议和 UDP 协议2.1 概述本章从网络程序设计角度提供足够的细节以理解如何使用 TCP 协议和 UDP 协议。同时提供这些协议的实际设计、具体实现和相关的注意事项。本章的焦点是计算机网络传输层服务,即面向连接服务和面向无连接服务,它们所使用的相关协议分别是 TCP 协议和 UDP 协议。目前绝大多数的客户服务器应用程序都使用TCP 协议或 UDP 协议。这两个协议使用网络层协议 IP: IPv4 或 IPv6。尽管应用程序可以绕过传输层直接使用 IPv4 或 IPv6,但这种方法(称为原始套接口)使用较少。UDP 是一个简单的传输层协议,应用程序写一个数据报到 UDP 套接
2、口,由它封装成IPv4 或 IPv6 数据报,然后发送到目的地址。但是,UDP 并不能保证 UDP 数据报最终能够到达目的地。使用 UDP 进行程序设计所遇到的问题是缺乏可靠性。如果要确保一个数据报能够到达目的地,必须在应用程序中建立相应的特性,主要包括:来自另一端的确认、超时、重传等等。每个 UDP 数据报都有一定的长度,可以把一个数据报看作一个记录。如果数据报最终正确地到达目的地(即分组到达目的地且校验和正确) ,那么该数据报的长度将传递给接收方的应用进程。而 TCP 是一个字节流协议,无记录边界。向应用程序提供的 TCP 服务与 UDP 服务不同。首先,TCP 提供客户与服务器的连接;其
3、次,TCP 提供可靠性;第三,TCP 通过给所发送数据的每一个字节关联一个序列号进行排序;第四,TCP 提供流量控制。总之,UDP 协议是一种简单的、不可靠的数据报协议,而 TCP 协议是一种复杂的、可靠的字节流协议。只有正确理解这两个协议提供给应用程序的服务,才能清楚这些协议能够处理什么,应用程序又需要处理什么。只有深入理解 TCP 协议和 UDP 协议的某些特征,才能更容易编写健壮的、高效的客户服务器程序。2.2 UDP:用户数据报协议UDP 是一个简单的面向数据报的传输层协议:进程的每个输出操作刚好产生一个 UDP数据报,该数据报导致一个 IP 数据报的发送。图 2-1 显示了作为 IP
4、 数据报的 UDP 数据报的封装。IP 数据报UDP 数据报IP 报头 UDP 报头 UDP 数据20 字节 8 字节图 2-1 UDP 封装RFC 768Postel 1980是 UDP 的官方描述。UDP 不提供可靠性:它发送应用程序数据到 IP 层的数据报,但不保证这些数据报到达其目的地。鉴于这种不可靠性,我们或许认为应避免 UDP 而总使用一个可靠的协议。 应用程序应注意所产生 IP 数据报的大小。若超出网络的 MTU,该 IP 报会被分段。 这适用于数据报从源到目的所跨越的每个网络,不只是适用于发送主机的第一个网络。2.2.1 UDP 报头图 2-2 列出了 UDP 报头的各个域。图
5、 2-2 UDP 报头端口号标识出发送进程和接收进程。由于 IP 已将到来的 IP 数据报分解复用为 TCP 和UDP,这意味着 TCP 端口号由 TCP 查看,UDP 端口号由 UDP 查看。TCP 端口号与 UCP 端口号无关。尽管二者无关,但若一个众所周和的服务 TCP 和 UDP 都提供,端口号通常取同一个值。UDP 长度域是以字节为单位的 UDP 数据和 UDP 报头之长,其最小值为。该 UDP 长度是冗余的,IP 报含有其总长度,故 UDP 报长为该总长度减去 IP 报头长度。2.2.2 UDP 校验和UDP 校验和覆盖 UDP 和 UDP 数据。而 IP 报头中的校验和仅覆盖该
6、IP 报头,它不涉及IP 数据报中的任何数据。UDP 和 TCP 均在其报头中有覆盖其报头和数据的校验和。对 UDP而言,校验和是可选的,而 TCP 则是必需的。首先,UDP 数据报的长度可以是奇数个字节,而校验和算法是加 16 位字。解决办法是在尾部追加 0 的填充字节, 而这填充字节仅为计算校验和所需。另外,UDP 和 TCP 均在 UDP 报中包含一个 12 字节的伪报头以计算校验和。该伪报头包含 IP 报头的某些域,目的是让 UDP 检测数据确已到达正确的目的端。如果发送者的确计算了校验和并且接收者检测出校验和错误,则该 UDP 数据报会被简单地扔弃,不产生错误信息。UDP 校验和是端
7、对端校验和。它由发送者计算,然后由接收者验证。这用于捕捉在发送者与接收者之间任何地方的 UDP 报头或数据所发生的任何改动。尽管 UCP 校验和是可选的,但他们应该总是能打开的。尽管这在单一的 LAN 上可能是可接受的, 因为在数据链路帧上的循环冗余检查能够检测到该帧的大多数错误,当这些数据报穿越路由器时,所有位都关闭。不管相信与否,某些带有软硬件缺陷的路由器会修改所转发的数据报中的某些位。如果端到端 UDP 校验和被关闭,那么这些错误是不可检测的。同时也应看到某些路据链路协议没有任何形式的数据链路校验和。16 位源端口号 16 位目的端口号16 位 UDP 长度 16 位 UDP 检查和数据
8、(如果有)0 15 16 318 字节TCP 校验错误率比 UDP 要高,这可能是因为系统的 TCP 连接倾向于“长距离” ,而 UDP主要用于本地。2.3 TCP:传输控制协议TCP 提供了一种可靠的面向连接的字节流传输层服务,TCP 将用户数据打包形成报文段;它发送数据后启动一个定时器;通信的另一端对收到的数据进行确认,对乱序的数据重新排序,丢弃重复数据;TCP 提供端到端的流量控制,并计算和验证一个强制性的端到端检查和。目前,许多流行的网络应用程序如 Telnet、FTP、Rlogin 和 SMTP 都使用 TCP。下面主要介绍 TCP 为应用层提供的服务以及 TCP 首部中各个字段的含
9、义。2.3.1 TCP 提供的服务尽管 TCP 和 UDP 都使用相同的网络层(IP) ,TCP 却向应用层提供与 UDP 完全不同的服务。TCP 提供一种面向连接的、可靠的字节流服务。面向连接是指两个使用 TCP 的应用(典型的情况是顾客/服务员模型)在彼此交换数据之前必须先建立一个 TCP 连接。这个过程与打电话类似。在一个 TCP 连接中,只能是双方进行通信,而广播和多播不能用于 TCP。TCP 通过下列方式来提供可靠性: 应用数据被分割成 TCP 认为最适合发送的数据块。这和 UDP 完全不同,应用程序产生的数据报长度保持不变。由 TCP 传递给 IP 的信息单位称为报文段或段(seg
10、ment) 。 当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。这里要求发送端在收到确认信息前必须保留该报文段的副本,一旦不能及时收到确认信息,才能重发该报文段。 当 TCP 收到发自 TCP 连接另一端的数据,它将发送一个确认。通常这个确认不是立即发送,将延迟几分之一秒。 TCP 将保持它首部和数据的检查和。这是一个端到端的检查和,目的是检查数据在传输过程中的变化。如果收到一个段的检查和有差错,TCP 将丢弃这个报文段,同时向该报文段的发送方发送否定的信息,表示收到的报文段有错误,希望对方重发该报文段。 TCP 报文段作为
11、 IP 数据报中的数据来传送,而 IP 数据报的到达可能乱序,因此 TCP 报文段的到达也可能乱序。如果必要,TCP 将对收到的数据进行重新排序,将收到的数据以正确的顺序提交给应用层。 IP 数据报会发生重复,因此 TCP 的接收端必须丢弃重复的数据。 TCP 提供流量控制。TCP 连接的每一方都有固定大小的缓冲空间。TCP 的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这种控制将防止较快主机致使较慢主机的缓冲区溢出。两个应用程序通过 TCP 连接交换 8bit 字节构成的字节流。TCP 不在字节流中插入标识符,通常称为字节流服务(byte stream service) 。如果一方的应
12、用程序先传 10 个字节,又传 30 字节,再传 40 字节;连接的另一方将无法了解发送方每次发送了多少字节。收方可以分 4 次接收这 80 字节,每次接收 20 字节。通信双方一方将字节流放到 TCP 连接上,同样的字节流将出现在 TCP 连接的另一端。另外,TCP 对字节流的内容不作任何解释。TCP 不知道传输的数据字节流是二进制数据、还是 ASCII 字符、EBCDIC 字符或其它类型数据。对字节流的解释由 TCP 连接双方的应用层解释。这种对字节流的处理方式与 Unix 操作系统对文件的处理方式相似。Unix 的内核对一个应用读或写的内容不作任何解释,而是交给应用程序处理。2.3.2
13、TCP 的首部TCP 数据被封装在一个 IP 数据报中,如图 2-3 所示。图 2-4 显示了 TCP 首部的数据格式。如果不计任选字段,它通常是 20 个字节。每个 TCP 段都包含源端和目的端的端口号,用于定位接收和发送应用进程,端口号是由本地操作系统分配的,在单机内部是唯一的;而一个主机的 IP 地址唯一地标识了网络中的主机。因此,这两个值和 IP 首部中的源端 IP 地址和目的端 IP 地址唯一确定一个 TCP 连接。IP 首部 TCP 首部 TCP 数据20 字节 20 字节TCP 报文段IP 数据报图 2-3 TCP 数据在 IP 数据报中的封装16 位源端口号 16 位目的端口号
14、32 位序号32 位确认序号首部长度(4 位)保留6 位U A P R S FR C S S Y IG K H T N N窗口大小(16 位)检查和(16 位) 紧急指针(16 位)选项数据图 2-4 TCP 包首部0 15 16 31一个 IP 地址和一个端口号也称为一个插座(socket) 。这个术语最早出现在 TCP 规范(RFC793)中,后来它成为表示伯克利版的网络编程接口。一个插座对(socket pair)(包括客户 IP 地址、客户端口号、服务器 IP 地址和服务器端口号的四元组)可以唯一确定互连网络中每个 TCP 连接的双方。序号用来标识从 TCP 发送端向 TCP 接收端发
15、送的数据字节流,它表示在这个报文段中的第一个数据字节。如果将字节流看作在两个应用程序之间的单向流动,则 TCP 用序号对每个字节进行计数。序号是 32 位的无符号数,序号到达 232-1 后又从 0 开始。当建立一个 TCP 连接时,SYN 标志置 1。序号字段包含由这个主机选择的该连接的初始序号 ISN(Initial Sequence Number) 。由于 SYN 标志消耗了一个序号,该主机要发送数据的第一个字节序号为 ISN 加 1。在 TCP 连接中每个传输的字节都被计数,确认序号包含发送确认的一端所期望收到的下一个序号。因此,确认序号应当是上次已经成功收到数据字节序号加 1。只有
16、ACK 标志为 1 时,确认序号字段才有效。发送 ACK 无需任何代价,因为 32bit 的确认序号字段和 ACK 标志一样,总是 TCP 首部的一部分。因此,一旦一个 TCP 连接建立起来,这个字段总是被设置,ACK 标志也总是被设置为 1。TCP 连接为应用层提供全双工服务,数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据序号。TCP 可以描述为一个没有选择确认或否定的滑动窗口协议。TCP 缺少选择确认是因为TCP 首部中的确认序号表示发方已成功收到字节,但还不包含确认序号所指的字节。当前还无法对数据流中选定的部分进行确认。例如,如果 11024 字节已经成
17、功收到,下一个报文段中包含序号从 20493072 的字节,收端并不能确认这个新的报文段,它所能做的就是发回一个确认序号为 1025 的 ACK。它也无法对一个报文段进行否认。例如,如果收到包含10252048 字节的报文段,但它的检查和有错误,TCP 接收端所能做的就是发回一个确认序号为 1025 的 ACK。首部长度给出首部中 32bit 字的数目。使用这个字段是因为任选字段的长度是可变的。这个字段占 4 个 bit,因此 TCP 最多有 60 字节的首部。如果没有任选字段,正常的首部长度是 20 字节。在 TCP 首部中有 6 个标志比特,它们中的多个可以被同时置 1。表 2-1 说明了
18、每个比特的具体含义:表 2-1:TCP 首部中 6 个标志比特的含义标志比特 含义URG 紧急指针(urgent pointer )有效ACK 确认序号有效PSH 接收方应该尽快将这个报文段交给应用层RST 重建 TCP 连接SYN 同步序号用来发起一个连接FIN 发送端完成发送任务TCP 的流量控制由连接的每一端通过声明的窗口大小来提供。窗口大小为字节数,起始于确认序号字段指明的值,这个值是接收端正期望接收的字节。窗口大小是一个 16bit字段,因此窗口大小最大为 65535 字节。检查和覆盖了整个的 TCP 报文段,包括 TCP 首部和 TCP 数据。这是一个强制性的字段,一定是由发端计算
19、和存储,并由收端进行验证。TCP 检查和的计算和 UDP 检查和的计算相似。只有当 URG 标志置 1 时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。TCP 的紧急方式是发送端向接收端发送紧急数据的一种方式。最常见的可选字段是最长报文大小,即 MSS(Maximum Segment Size) 。每个连接方通常都在通信的第一个报文段(为建立 TCP 连接而设置 SYN 标志的那个报文段)中指明这个选项。它指明本端所能接收的最大长度的报文段。TCP 报文段中的数据部分是可选的。有些 TCP 报文段不包含数据,例如,在一个连接建立或一个连接终止时,
20、通信双方交换的报文仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况下,也会发送不带任何数据的报文段。2.3.3 TCP 连接的建立和释放2.3.3.1 TCP 连接的建立TCP 是一个面向连接的协议,通信双方在发送数据之前都必须建立一个 TCP 连接。为了建立一个 TCP 连接,通常需要以下一些操作:1 请求端发送一个 SYN 段指明客户想要连接的服务器的端口,以及初始序号(ISN) 。这个 SYN 段为报文段 1。2 服务器发回包含服务器的初始序号的 SYN 报文段(报文段 2)作为应答。同时,将确认序号设置为客户的 ISN 加 1
21、以对客户的 SYN 报文段进行确认。一个 SYN 占用一个序号。3 客户必须将确认序号设置为服务器的 ISN 加 1 以对服务器的 SYN 报文段进行确认(报文段 3) 。这三个报文段的传递完成了一个 TCP 连接的建立,如图 2-5 所示,这个过程也称为三次握手(three-way handshake) 。发送第一个 SYN 的一端执行主动打开(active open) 。接收这个 SYN 并发回下一个SYN 的另一端执行被动打开(passive open) 。当一端为建立 TCP 连接而发送它的 SYN 时,它为该连接选择一个初始序号 ISN。ISN随时间变化,因此每个 TCP 连接都将具
22、有不同的 ISN。RFC793 指出 ISN 可看作是一个 32 比特的计数器,每 4ms 加 1。这样选择序号的目的在于防止在网络中被延迟的分组在以后又报文段 1报文段 2报文段 3SYNSYN ACKACK图 2-5 连接建立的报文序列被传送,而导致某个 TCP 连接的一方对它作出错误的解释。不同的操作系统对序号的选择不同。在 BSD4.4 中,系统初始化时初始的发送序号被初始化为 1。这种方法违背了 Host Requirements RFC(在这个代码中的一个注释确认这是一个错误) 。2.3.3.2 TCP 连接的释放建立一个 TCP 连接需要三次握手,而释放一个 TCP 连接需要经过
23、 4 次握手,这是由于TCP 的半关闭(half-close)造成的。一个 TCP 连接是全双工的,因此每个方向必须单独地进行关闭。即当 TCP 连接的一方完成它的数据发送后就能发送一个 FIN 来终止这个方向的连接;当 TCP 连接的另一端收到一个 FIN,它必须通知应用层对方已经终止了那个方向上的数据传送。发送 FIN 通常是应用层进行关闭的结果。收到一个 FIN 只意味着在这个方向上没有数据流动。一个 TCP 连接在收到一个 FIN 后仍能发送数据。这种情况对于利用半关闭的应用是可以的,尽管在实际的应用中只有很少的 TCP 应用程序这样做。正常的关闭过程如图 12-5 所示。首先进行关闭
24、的一方(发送第一个 FIN 报文的一方)将执行主动关闭,而 TCP 连接的另一方(收到这个 FIN 报文的一方)执行被动关闭。通常情况下,一方完成主动关闭而另一方完成被动关闭。图 2-6 显示了释放一个 TCP 连接的典型握手顺序。在这个图中,发送 FIN 将导致应用程序关闭它们的连接,这些 FIN 的 ACK 是由 TCP 软件自动产生的。一个 TCP 连接通常是由客户端发起的,这样第一个 SYN 从客户传到服务器。每一端都能主动关闭这个连接(即首先发送 FIN 报文) 。然而,一般由客户端决定何时终止连接,因为客户进程通常由用户交互控制。2.3.4 最大报文段长度最大报文长度(MSS)表示
25、 TCP 传往另一端的最大块数据的长度。当一个 TCP 连接建立时,连接的双方都要通告各自的 MSS。在有些相关资料中,将最大报文长度看作可“协商”选项。但它并不是在任何条件下都可以协商。当建立一个 TCP 连接时,每一方都有用于通告它期望接收的 MSS 选项(MSS选项只能出现在 SYN 报文段中) 。如果一方不接受来自另一方的 MSS 值,则 MSS 就定为默认值 536 字节(这个默认值允许 20 字节的 IP 首部和 20 字节的 TCP 首部以适合 576 字节的IP 数据报) 。客户 服务员应用程序关闭向应用程序交付 EOF应用程序关闭FINFIN 的 ackFINFIN 的 ac
26、k图 2-6 TCP 连接释放期间正常的报文交换通常,如果没有分段发生,MSS 值越大性能越好。报文段越大允许每个报文段传送的数据就越多,相对 IP 和 TCP 首部有更高的网络利用率。当 TCP 发送一个 SYN 报文段时,或者由于一个本地应用进程想发起一个 TCP 连接,或者是由于另一端的主机收到了一个 TCP连接请求,它能将 MSS 值设置为外出接口上的 MTU 长度减去固定的 IP 首部和 TCP 首部长度。对于以太网来说,MSS 值可以达到 1460 字节。对于使用 IEEE 802.3 的封装,它的 MSS 值可以达到 1452 字节。由于许多 BSD 的实现版本需要 MSS 的值
27、是 512 的倍数,对于 BSD/386 和 SVR4 的 MSS值为 1024。许多其它的系统,如 SunOS4.1.3、Solaris2.2 和 AIX3.2.2 等,当 TCP 连接都在一个本地以太网上时都规定 MSS 值为 1460。许多测试结果表明在以太网上 1460 的 MSS在性能上比 1024 的 MSS 更好。通常,如果目的 IP 地址为“非本地的(nonlocal) ”,MSS 的默认值是 536。而区分地址是本地的还是非本地的是很简单的,如果目的 IP 地址的网络号和子网号都和源 IP 地址的网络号和子网号相同,则是本地的;否则是非本地的。如果目的 IP 地址的网络号与源
28、IP 地址的网络相同,但子网号不同,则可能是本地的,也可能是非本地的。大多数 TCP 实现版都提供了一个配置选项,让网络系统管理员说明不同的子网是本地的还是非本地的,这个选项的设置将确定 MSS 可以选择尽可能的大(达到外出接口的 MTU 长度)或者是默认值 536。MSS 值让主机限制连接的另一端发送数据报的长度,同时主机也能控制它发送数据报的长度,这样可以使以较小 MTU 连接到一个网络上的主机避免分段,从而提高了网络的通信性能。2.3.5 TCP 的半关闭TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。这就是所说的半关闭。这种情况只有很少的应用程序使用它。为了使用这
29、个特性,编程接口必须为应用程序提供一种方式来说明“一方已经完成了数据传送,因此发送一个结束报文(FIN 报文段)给另一端,但该方还想接收来自另一端的数据,直到接收了结束报文(FIN 报文段) ”。图 2-7 描述了一个半关闭的典型例子。客户 服务员应用进程shutdown应用进程read向应用进程交付 EOFFIN向应用进程交付 EOFFIN 的 ack数据数据的 ack应用进程write应用进程closeFINFIN 的 ack图中,客户端开始半关闭,当然也可以服务员端开始半关闭。初始端发出 FIN 报文段,接着是另一端对这个 FIN 的 ACK 报文段。这里客户端虽然发送了 FIN 报文段
30、,但它仍能够接收来自对方的数据,在图中只显示了一个数据报文段和一个 ACK 报文段,但实际可能发送了许多数据报文段。当收到半关闭的一端在完成它的数据传送后,将发送一个 FIN 报文段以关闭这个方向的连接。当对第二个 FIN 报文段进行确认后,这个连接就彻底释放了。2.3.6 同时关闭通常情况下,TCP 连接的一方发送第一个 FIN 报文段执行主动关闭,但 TCP 双方都执行主动关闭也是可能的,TCP 协议也允许这样的同时关闭(simultaneous close) 。如图 2-8 所示。当应用层发出关闭命令时,两端均从 ESTABLISHED 变为 FIN_WAIT_1。这种状态的变化将导致
31、TCP 双方各发送一个 FIN 报文段,两个 FIN 报文段经过网络传输后分别到达另一端。TCP 的每一端收到 FIN 报文段后,状态由 FIN_WAIT_1 变化为 CLOSING,并发送最后的 ACK报文段。当收到最后的 ACK 报文段时,状态变化为 TIME_WAIT。同时关闭与正常关闭使用的报文段交换数目相同。2.3.7 TCP 选项TCP 首部可以包含选项部分,在最初的 TCP 规范中定义的选项是选项表结束、无操作和最大报文段长度。新的 RFC,例如 RFC1323 中定义了新的 TCP 选项,这些选项的大多数只在最新的 TCP 实现中提供。图 2-9 显示了当前 TCP 选项的格式
32、,这些选项的定义来自于RFC793 和 RFC1323。每个 TCP 选项的开始是 1 字节的 kind 字段,该字段说明选项的类型。kind 字段为 0和 1 的选项仅占一个字节。其它的选项在 kind 字节后还有 len 字节,它说明的长度是指总长度,包括 kind 字节和 len 字节。设置无操作选项的原因是在于允许发方填充字段为 4 字节的倍数。其它 kind 值为4、5、6 和 7 的四个选项称为 ACK 及回显选项。由于回显选项已经被时间戳选项取代,而图 2-7 TCP 的半关闭例子(主动关闭)FIN_WAIT_1 (主动关闭)FIN_WAIT_1CLOSINGTIME_WAITC
33、LOSINGTIME_WAITFIN J FIN Kack K+1 ack J+1图 2-8 同时关闭时的报文段交换目前定义的选择 ACK 选项仍未定论,而且并未包括在 RFC1323 中。2.3.8 TCP 的性能TCP 已经在从 1200b/s 的拨号 SLIP 链路到以太数据链路上运行了许多年。在 80 年代和 90 年代初期,以太网是运行 TCP/IP 最主要的数据链路方式。尽管 TCP 在比以太网速率高的环境(如 T2 电话线、FDDI 等)下也能够正常运行,但在这些高速网络环境下,TCP 的某些限制就会暴露出来。下面介绍关于 TCP 的一些修改建议,这些建议可以使 TCP 在高速率
34、网络环境下获得最大的吞吐量。2.3.8.1 路径 MTU 发现每个网络中的数据链路层对数据单元的长度都有一个限制,例如对于以太网,它的最大值是 1518 字节。链路层的这个特性称为最大传输单元(MTU) ,不同类型的网络大多数都有一个上限。当在同一个网络上的两台主机互相进行通信时,该网络的 MTU 是十分重要的。如果两台主机的通信要通过多个网络,那么每个网络的链路层可能有不同的 MTU。在这里,重要的不是两台主机所在网络的 MTU 值,而是两台通信主机路径中的最小 MTU,它被称为路径MTU。路径 MTU 是决定主机之间通信性能的重要因素。两台主机之间的路径 MTU 不一定是一个常数,它取决于
35、当时路由算法所选择的路由。而选路不一定是对称的(从节点 A 到节点 B 的路由可能与节点 B 到节点 A 的路由不同) ,因此路径 MTU 在通信两个方向上不一定是相同的。RFC1191 文件描述了路径 MTU 的发现机制,即在任何时候确定路径 MTU 的方法。这是在两个主机之间的路径上任何网络上的最小 MTU。路径 MTU 发现在 IP 首部中继承并设置“不要分片(DF) ”比特,来发现当前路径上的路由器是否需要对正在发送的 IP 数据报进行分片。如果一个待转发的 IP 数据报被设置 DF比特,而其长度又超过了 MTU,那么路由器将返回 ICMP 不可达的差错。目前的操作系统只有少数支持路径
36、 MTU 发现,例如 Solaris 2.x 支持路径 MTU 发现,将来会有越来越多的操作系统支持这个功能。选项表结束: kind=01 字节无操作: Kind=11 字节kind=2最大报文段长度: len=4 最大报文段长度1 字节 1 字节 2 字节窗口扩大因子: kind=3 len=3 移位数1 字节 1 字节 1 字节时间戳: kind=8 len=10 时间戳值 时间戳回显应答1 字节 1 字节 4 字节 4 字节图 2-9 TCP 新选项TCP 的路径 MTU 发现按如下方式进行:在建立 TCP 连接时,TCP 使用输出接口或对端声明的 MSS 中的最小 MTU 作为起始的报
37、文段大小。路径 MTU 的发现不允许 TCP 超过对端声明的 MSS。如果对端没有指定一个 MSS,则默认值为 536。一旦选定了起始的报文段大小,在该连接上的所有被 TCP 发送的 IP 数据报都将被设置 DF 比特。如果某个中间路由器需要对一个设置了 DF 标志的数据报进行分片,它就丢弃这个数据报,并产生一个 ICMP 的“不能分片”差错。如果收到一个“不能分片”的 ICMP 差错,TCP 就减少段大小并进行重传。如果路由器产生的是一个较新的该类 ICMP 差错,则报文段大小设置为下一跳的 MTU 减去 IP 和 TCP 的首部长度。如果是一个较旧的该类 ICMP 差错,则必须尝试下一个可
38、能的最小 MTU。当由这个 ICMP 差错引起的重传发生时,拥塞窗口不需要变化,但要启动慢启动。由于路由经常动态变化,因此在最后一次减少路径 MTU 的一段时间以后,可以尝试使用一个较大的值(直到等于对端声明的 MSS 或输出接口 MTU 的最小值) 。RFC1191 推荐这个时间间隔为 10 分钟,操作系统 Solaris 2.2 使用的时间间隔是 30 分钟。在对非本地目的地。默认的 MSS 通常是 536 字节,路径 MTU 发现可以避免在通过 MTU小于 576(这非常罕见)的中间链路时进行分片。对于本地目的主机,也可以避免在中间链路(如以太网)的 MTU 小于端点网络(如令牌环)的情
39、况下进行分片。但为了能使路径 MTU更加有用和充分利用 MTU 大于 576 的广域网,一个实现必须停止使用为非本地目的制定的536 的 MTU 默认值。MSS 的一个较好的选择是输出接口的 MTU(当然要减去 IP 和 TCP 的首部大小) ,大多数系统的实现都允许系统管理员改变这个默认的 MSS 值。假定分组的大小不足以引起分片,常规知识告诉我们传输较大的分组的性能比较好,因为发送较少的大分组比发送较多的小分组开销要少。这些开销的减少与网络(分组首部负荷) 、路由器(选路的决定)和主机(协议处理和设备中断)等诸多因素有关。2.3.8.2 长肥管道通常,一个 TCP 连接的容量表示为:cap
40、acity(b)=bandwidth(b/s)*round-trip times(s)这个公式称为带宽时延乘积,也称它为两端的管道大小,它的单位是字节,可以用这个值来测量每一端的缓存大小和窗口大小。这个值依赖于网络速度和两端的RTT(round-trip times) ,可以有很大的变动范围。当这个乘积变得越来越大时,TCP 的某些局限性就会暴露出来。表 2-2 列出了一些典型网络的某些数值。表 2-2 典型网络的带宽时延乘积网络 带宽(b/s) RTT(ms) 带宽时延乘积(字节)以太局域网 10000000 3 3750横跨大陆的 T1 电话线 1544000 60 11580卫星 T1
41、电话线 1544000 500 96500横跨大陆的 T3 电话线 45000000 60 337500横跨大陆的 gigabit 线路 1000000000 60 7500000具有大的带宽时延乘积的网络称为长肥网络(Long Fat Network,即 LFN) ,而一个运行在 LFN 上的 TCP 连接称为长肥管道,这种管道可以被水平拉长(一个长的 RTT) ,或被垂直拉高(较高的带宽) ,或向两个方向拉伸。使用长肥管道会遇到多种问题,主要有: TCP 首部中窗口大小为 16bit,从而将窗口限制在 65535 个字节范围内,但是从表12-2 最后一列可以看到,现有的网络需要一个更大的窗
42、口来提供最大的吞吐量。这个问题使用窗口扩大选项可以解决。 在一个长肥网络 LFN 内的分组丢失会使网络吞吐量急剧减少。如果只有一个报文段丢失,可以使用快速重传和快速恢复算法来使管道避免耗尽。但是,即使使用这些算法,在一个窗口内发生的多个分组丢失也会使管道耗尽(如果管道耗尽了,慢启动会使它渐渐填满,但这个过程将需要多个 RTT) 。在 RFC1072 中建议使用有选择的确认(SACK)来处理在一个窗口发生的多个分组丢失。 许多 TCP 实现对每个窗口的 RTT 仅进行一次测量。它们并不对每个报文段进行 RTT测量。在一个长肥网络 LFN 上需要更好的 RTT 测量机制。通常使用时间戳选项来解决这
43、个问题,它允许更多的报文段被计时。 TCP 对每个字节数据使用一个 32bit 无符号的序号进行标识。如果在网络中有一个被延迟一段时间的报文段,它所在的连接已经释放,而一个新的连接在这两个主机之间又建立了,这种情形将产生报文段的再次出现问题。解决这个问题的主要方法是使用 TCP 时间戳选项的 PAWS(Protection Against Wrapped Sequence numbers)算法(保护回绕的序号) 。2.3.8.3 窗口扩大选项窗口扩大选项使 TCP 的窗口定义从 16bit 增加为 32bit。这种扩展不是通过修改 TCP首部来实现的,TCP 的首部仍然使用 16bit,而是通
44、过定义一个选项实现对 16bit 的扩大操作(Scaling Operation)来完成的。这样 TCP 在内部将实际的窗口大小维持为 32bit 的值。这个选项只能出现在一个 SYN 报文段中,因此当 TCP 连接建立起来后,在每个方向的扩大因子是固定的。为了使用窗口扩大选项,通信双方必须在它们的 SYN 报文段中发送这个选项。TCP 连接的发起方在其 SYN 报文中发送这个选项,但是 TCP 连接的接收方只能够在收到带有这个选项的 SYN 之后才可以发送这个选项。每个方向的扩大因子可以不同。如果 TCP 连接的发起方发送一个非零的扩大因子,但是没有从连接的另一端收到一个窗口扩大选项,它就将
45、发送和接收的移位计数器置为 0。这样就允许新的系统能够与较旧的、不理解新选项的系统进行互操作。假定正在使用窗口扩大选项,发送移位计数为 S,而接收移位计数为 R。这样,从另一端收到的每一个 16bit 的通告窗口将被左移 R 位以获得实际的通告窗口大小。每次当向对方发送一个窗口通告的时候,将实际的 32bit 窗口大小右移 S 比特,然后用它来替换TCP 首部中的 16bit 的值。TCP 根据接收缓存的大小自动选择移位计数。缓存的大小由系统设置,但是通常向应用进程提供了修改途径。2.3.8.4 时间戳选项时间戳选项使发送方在每个报文段中放置一个时间戳值。接收方在确认中返回这个数值,从而允许发
46、送方为每一个收到的 ACK 计算 RTT(TCP 通常使用一个 ACK 来确认多个报文段) 。目前许多 TCP 实现为每一个窗口只计算一个 RTT,对于包含 8 个报文段的窗口这是正确的。然而,较大的窗口大小需要进行更好的 RTT 计算。时间戳是一个单调递增的值。由于接收方只需要回显收到的内容,因此不需要关注时间戳单元是什么。这个选项不需要在两个主机之间进行任何形式的时钟同步。RFC1323 中推荐在 1 毫秒和 1 秒之间将时间戳的值加 1。BSD4.4 系统在启动时将时间戳的值设置为0,然后每隔 500 毫秒将时间戳值加 1。在 TCP 连接建立阶段,对这个选项的规定与窗口扩大选项类似。T
47、CP 连接的发起方在它的 SYN 报文中指定选项。只有在它从另一方的 SYN 报文中收到了这个选项之后,该选项才会在以后的报文段中进行设置。接收方 TCP 通常不需要对每个包含数据的报文段进行确认,许多 TCP 实现每两个报文段发送一个 ACK。为了减少连接双方所维持的状态变量,对于每个连接只维持一个时间戳的数值。选择何时更新这个数值的算法非常简单:1 TCP 跟踪下一个 ACK 中将要发送的时间戳的值(一个名为 tsrecent 的变量)以及最后发送的 ACK 中的确认序号(一个名为 lastack 的变量) 。这个序号就是接收方期望的序号。2 当一个包含有字节号 lastack 的报文段到
48、达时,则该报文段中的时间戳被保存在 tsrecent 中。3 无论何时发送一个时间戳选项,tsrecent 就作为时间戳回显应答字段被发送,而序号字段被保存在 lastack 中。2.3.9 T/TCP:为事务用的 TCP 扩展TCP 提供的是一种虚电路方式的传输服务。一个连接的生存时间包括三个不同的阶段:建立连接、数据传输和释放连接。这种虚电路服务非常适合诸如文件传输之类的应用。但是,还有其他的网络应用进程被设计成使用事务(transaction)服务。一个事务就是符合下面这些特征的一个客户请求及其随后的服务员响应。1 应该避免连接建立和连接释放的开销,在可能的时候,发送一个请求分组并接收一
49、个应答分组。2 等待时间应当减少到等于 RTT(Round-Trip Time)与 SPT(Server Processing Time)之和。3 服务员应当能够检测出重复的请求,并且当收到一个重复的请求时不重新处理事务。使用这种类型服务的典型例子是域名系统(DNS) ,尽管域名系统与服务员重新处理重复的请求无关。TCP 提供了过多的事务特征,而 UDP 则不够。通常应用程序为了避免 TCP 连接的开销而使用 UDP 来构造,而许多必要的特征(如拥挤避免等)被放置在应用层实现,从而导致了这些特征的重新设计和实现。一个较好的解决方法是提供一个能够提供足够多的事务处理功能的传输层。T/TCP 协议就是这种方法的一个实现,RFC1379 文档对它进行了介绍。TCP 为处理事务而需要进行的两个改动是为了避免三次握手和缩短 WAIT_TIME 状态。T/TCP 通过使用加速打开来避免三次握手:1 它为打开的连接指定一个 32bit 的连接计数 CC(Connection Count) ,无论是主动打开还是被动