通信网络/TCP 和 UDP 协议/TCP
在了解了模型的各个层次之后,现在该看看 TCP 协议并研究其功能了。本节将帮助读者了解 TCP 的概念和特性,然后逐步深入了解 TCP 的细节,如连接建立/关闭、TCP 中的通信,以及为什么 TCP 协议被称为可靠协议和自适应协议。本节将以 UDP 和 TCP 的比较结束,并附带一个很好的练习,鼓励读者解决更多问题。
在编写本节之前,信息已从各种来源学习,如 TCP 指南、RFC、Tanenbaum 的书和课堂笔记。
什么是 TCP?
从理论上讲,传输层协议可以是一个非常简单的软件例程,但 TCP 协议不能称为简单。为什么要使用像 TCP 那样复杂的传输层?最重要的原因取决于 IP 的不可靠性。事实上,TCP 下面的所有层都是不可靠的,并且逐跳传递数据报。IP 层逐跳传递数据报,不保证数据报的传递;它是一个无连接系统。IP 只是处理数据报的路由;如果出现问题,IP 会毫不犹豫地丢弃数据包,并在此过程中向发送方发送错误消息。确定通过网络发送的数据报状态和处理如果部分数据报被丢弃则重新发送信息的任务由 TCP 完成。
大多数用户将 TCP 和 IP 视为紧密结合的一对,但 TCP 可以并且经常与其他传输协议一起使用。
例如,TCP 或其部分内容用于文件传输协议 (FTP) 和简单邮件传输协议 (SMTP),它们都不使用 IP。
传输控制协议为 IP 层和上层提供了相当多的服务。最重要的是,它为上层提供了一个面向连接的协议,使应用程序能够确定通过网络发送的数据报是否完整地接收。在这种情况下,TCP 充当消息验证协议,提供可靠的通信。如果数据报损坏或丢失,通常是 TCP(而不是更高层的应用程序)处理重传。
TCP 不是软件。它是一种通信协议。
TCP 管理来自更高层的以及来自 IP 层的传入数据报的流。它必须确保优先级和安全性得到尊重。TCP 必须能够处理在其上层正在等待传入数据报的应用程序的终止,以及较低层中的故障。TCP 还必须维护一个所有进出 TCP 层的数据流状态表。将这些服务隔离在单独的层中,使应用程序的设计无需考虑流控制或消息可靠性。如果没有 TCP 层,每个应用程序都必须自己实现这些服务,这是一种浪费资源。
TCP 位于传输层,位于 IP 之上,但在上层及其应用程序之下,如下图所示。TCP 只驻留在实际处理数据报的设备上,以确保数据报已从源机器传递到目标机器。它不驻留在只路由数据报的设备上,因此网关中没有 TCP 层。这是有道理的,因为在网关上,数据报不需要在分层模型中比 IP 层更高。
图 1:TCP 提供可靠的端到端通信
由于 TCP 是一种面向连接的协议,负责确保数据报从源机器传递到目标机器(端到端通信),因此 TCP 必须接收来自目标机器的通信消息以确认数据报的接收。术语虚拟电路通常用于指代两台终端机器之间进行的握手,其中大多数是简单的确认消息(接收确认或故障代码)和数据报序列号。它类似于电话通话;有人通过拨打电话号码来发起通话,该号码被接听,进行双向通话,最后有人结束通话。套接字对标识连接的两端,即虚拟电路。可能需要提醒的是,套接字由 IP 地址和端口号组成,以标识位置。服务器使用众所周知的端口号(< 1000)来表示标准化服务(侦听)。超过 1024 的数字可供用户自由使用。下表列出了某些标准服务的端口号。
端口 | 协议 | 用途 |
---|---|---|
21 | FTP | 文件传输 |
23 | Telnet | 远程登录 |
25 | SMTP | 电子邮件 |
69 | TFTP | 微不足道的文件传输协议 |
79 | Finger | 查找有关用户的详细信息 |
80 | HTTP | 万维网 |
110 | POP-3 | 远程电子邮件访问 |
119 | NNTP | USENET 新闻 |
字节流或消息流?
嗯,消息边界在 TCP 中没有端到端保留。例如,如果发送进程对 TCP 流进行四个 512 字节的写入操作,这些数据可能会作为四个 512 字节的块、两个 1024 字节的块、一个 2048 字节的块或其他方式传递给接收进程。接收方无法检测到写入数据的单元。TCP 实体接受来自本地进程的用户数据流,将其分成不超过 64 KB 的片段(实际上,通常为 1460 个数据字节,以便与 IP 和 TCP 首部一起放入单个以太网帧),并将每个片段作为单独的 IP 数据报发送。当包含 TCP 数据的数据报到达机器时,它们会被传递给 TCP 实体,后者会重建原始字节流。为简单起见,我们有时会使用TCP来指代 TCP 传输实体(一块软件)或 TCP 协议(一组规则)。从上下文中可以清楚地知道指的是哪一个。例如,在用户向 TCP 提供数据中,显然指的是 TCP 传输实体。IP 层不保证数据报会正确传递,因此 TCP 必须在需要时超时并重新传输它们。到达的数据报很可能以错误的顺序到达;TCP 也必须将它们重新组装成按正确顺序排列的消息。简而言之,TCP 必须提供大多数用户想要而 IP 未提供的可靠性。
TCP 的特点
TCP 在每个主机系统上的进程之间提供通信通道。该通道是可靠的、全双工的和流式的。为了实现此功能,TCP 驱动程序将会话数据流分解成离散的段,并为每个段附加一个 TCP 首部。将一个 IP 首部附加到这个 TCP 数据包,然后将复合数据包传递给网络以进行传递。此 TCP 首部具有用于支持预期的 TCP 功能的众多字段。TCP 具有以下功能特性
单播协议 : TCP 基于单播网络模型,支持两个精确的双方之间的数据交换。它不支持广播或组播网络模型。
连接状态 : 而不是在网络内施加状态来支持连接,TCP 在两个端点之间使用同步状态。这种同步状态是在初始连接过程的一部分中建立的,因此 TCP 可以被视为一个面向连接的协议。协议设计的大部分内容旨在确保每个本地状态转换都被传达给远程方并得到远程方的确认。
可靠的 : 可靠性意味着传递到连接一端的 TCP 驱动程序的八位字节流将被传输到整个网络,以便将该流作为与发送方生成的相同顺序的相同八位字节序列呈现给远程进程。这意味着协议检测到数据流的段何时被网络丢弃、重新排序、重复或损坏。如果需要,发送方将重新传输损坏的段,以便接收方能够重建原始数据流。这意味着 TCP 发送方必须保留所有已传输数据的本地副本,直到收到指示接收方已完成数据准确传输的指示。
全双工 : TCP 是一种全双工协议;它允许双方在单个 TCP 连接的上下文中发送和接收数据。
流式 : 尽管 TCP 使用数据包结构进行网络传输,但 TCP 是一种真正的流式协议,应用程序级别的网络操作不是透明的。某些协议明确封装每个应用程序事务;对于每次写入,都必须有一个匹配的读取操作。通过这种方式,应用程序派生的数据流逻辑记录结构的分割在网络上被保留。TCP 不保留施加在数据流上的这种隐式结构,因此网络协议中没有写入和读取操作之间的配对。例如,TCP 应用程序可以将三个数据块依次写入网络连接,这些数据块可以被远程读取器在一个读取操作中收集。在会话开始时协商 TCP 会话中使用的数据块(段)的大小。发送方尝试在其接收方最大段大小、配置的发送方最大段大小以及网络路径最大可支持的非分段数据包大小(路径最大传输单元 [MTU])的限制内,使用它可以使用的最大段大小进行数据传输。路径 MTU 会定期刷新,以适应 TCP 连接处于活动状态时网络中可能发生的任何更改。
速率自适应: TCP 也是一种速率自适应协议,数据传输速率旨在适应网络中普遍存在的负载条件,并适应接收端的处理能力。没有预定的 TCP 数据传输速率;如果网络和接收器都具有额外的可用容量,则 TCP 发送方将尝试将更多数据注入网络以占用此可用空间。相反,如果出现拥塞,TCP 发送方将降低其发送速率以允许网络恢复。此自适应功能试图在不触发一致数据丢失的情况下实现尽可能高的数据传输速率。
TCP 段以互联网数据报的形式发送。互联网协议报头包含几个信息字段,包括源主机地址和目标主机地址。TCP 报头位于互联网报头之后,提供特定于 TCP 协议的信息。这种划分允许存在除 TCP 之外的主机级协议。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |U|A|P|R|S|F| | | Offset| Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ TCP Header Format Note that one tick mark represents one bit position.
源端口:16 位 源端口号。
目标端口:16 位 目标端口号。
序列号:32 位 此段中第一个数据字节的序列号(除非存在 SYN)。如果存在 SYN,则序列号是初始序列号 (ISN),第一个数据字节是 ISN+1。
确认号:32 位 如果设置了 ACK 控制位,则此字段包含段发送方期望接收的下一个序列号的值。一旦建立连接,这将始终被发送。
数据偏移量:4 位 TCP 首部中的 32 位字数。这指示数据开始的位置。TCP 首部(即使包含选项)也是 32 位整数倍。
保留:6 位 为将来使用保留。必须为零。
控制位:6 位(从左到右)
URG:紧急指针字段有效
ACK:确认字段有效
PSH:推送功能
RST:重置连接
SYN:同步序列号
FIN:发送方不再有数据
窗口:16 位 从确认字段中指示的第一个数据字节开始,该段发送方愿意接受的数据字节数。
校验和:16 位 校验和字段是首部和文本中所有 16 位字的按位取反和的 16 位按位取反。如果一个段包含奇数个要进行校验和计算的首部和文本字节,则最后一个字节在右侧填充零以形成一个 16 位字以进行校验和计算。填充不会作为段的一部分传输。在计算校验和时,校验和字段本身被替换为零。
校验和还覆盖概念上加在 TCP 首部之前的 96 位伪首部。此伪首部包含源地址、目标地址、协议和 TCP 长度。这为 TCP 提供了针对错误路由段的保护。此信息包含在互联网协议中,并在 TCP/网络接口中通过 TCP 对 IP 的调用参数或结果进行传输。
TCP 长度是 TCP 首部长度加上数据长度(以字节为单位)(这不是显式传输的数量,而是计算出来的),并且不包括伪首部的 12 个字节。
紧急指针:16 位 此字段以从此段中的序列号开始的正偏移量形式传达紧急指针的当前值。紧急指针指向紧急数据后一个字节的序列号。此字段仅在设置了 URG 控制位的段中进行解释。
选项:可变 选项可以占用 TCP 首部末尾的空间,并且长度为 8 位的倍数。所有选项都包含在校验和中。选项可以从任何字节边界开始。选项格式有两种情况
情况 1:一个字节的选项类型。
情况 2:一个字节的选项类型、一个字节的选项长度和实际的选项数据字节。选项长度包括选项类型和选项长度这两个字节,以及选项数据字节。请注意,选项列表可能比数据偏移量字段所暗示的要短。选项结束选项之外的标头内容必须是标头填充(即零)。
TCP 必须实现所有选项
Ethereal 捕获
可以使用 Ethereal 捕获查看 TCP 数据包。下面捕获并显示了一个这样的 TCP 数据包。请注意,其中设置了 ACK 标志和 PUSH 标志,值为“1”。
在 TCP 可以用于任何实际有用的目的(即发送数据)之前,必须在希望进行通信的两个设备之间建立连接。此过程通常称为连接建立,它涉及交换消息,使两个设备从其初始连接状态(关闭)转换到正常工作状态(已建立)。
连接建立功能
连接建立过程实际上在创建适合数据交换的连接时完成了几个事情
联系和通信: 客户端和服务器通过相互发送消息来进行联系并建立通信。在此之前,服务器通常甚至不知道它将与哪个客户端进行通信,因此它在连接建立过程中发现了这一点。
序列号同步: 每个设备都让对方知道它希望将其第一次传输用于哪个初始序列号。
参数交换: 两个设备交换控制 TCP 连接操作的某些参数。
用于连接建立的控制消息:SYN 和 ACK
TCP 使用控制消息来管理联系和通信的过程。但是,没有特殊的 TCP 控制消息类型;所有 TCP 消息都使用相同的段格式。TCP 首部中的一组控制标志指示段是用于控制目的还是仅用于携带数据。使用控制消息时,以下标志会发生改变。
SYN: 此位表示段用于初始化连接。SYN 代表同步,指的是我上面提到的序列号同步。
ACK: 此位表示发送段的设备正在传递对收到的消息(例如 SYN)的确认。
正常连接建立:“三次握手”
为了建立连接,每个设备都必须发送一个 SYN 并接收来自对方设备的 ACK。因此,从概念上讲,需要在设备之间传递四个控制消息。但是,当一个人可以同时传递两者时,分别发送一个 SYN 和一个 ACK 是低效的。因此,在连接建立的正常事件序列中,一个 SYN 和一个 ACK 通过设置相关位的两个标志同时发送(有时称为 SYN+ACK 消息)。这总共需要三个消息,因此该连接过程被称为三次握手。
Key Concept: The normal process of establishing a connection between a TCP client and server involves three steps:
客户端发送一个 SYN 消息;服务器发送一个消息,该消息包含对客户端 SYN 的 ACK,并包含服务器的 SYN;然后客户端发送对服务器 SYN 的 ACK。这被称为 TCP 三次握手。
连接在其生命周期内会经过一系列状态。
状态包括:监听、SYN 已发送、SYN 已接收、已建立、FIN-WAIT-1、FIN-WAIT-2、CLOSE-WAIT、CLOSING、LAST-ACK、TIME-WAIT 和虚构状态关闭。关闭是虚构的,因为它表示没有 TCB,因此没有连接。简而言之,这些状态的含义是
监听 - 表示正在等待来自任何远程 TCP 和端口的连接请求。
SYN 已发送 - 表示在发送连接请求后正在等待匹配的连接请求。
SYN 已接收 - 表示在收到和发送连接请求后正在等待确认连接请求确认。
已建立 - 表示连接已打开,收到的数据可以传递给用户。连接数据传输阶段的正常状态。
FIN-WAIT-1 - 表示正在等待来自远程 TCP 的连接终止请求,或者正在等待对之前发送的连接终止请求的确认。
FIN-WAIT-2 - 表示正在等待来自远程 TCP 的连接终止请求。
CLOSE-WAIT - 表示正在等待来自本地用户的连接终止请求。
CLOSING - 表示正在等待来自远程 TCP 的连接终止请求确认。
LAST-ACK - 表示正在等待对之前发送给远程 TCP 的连接终止请求的确认(其中包括对连接终止请求的确认)。
TIME-WAIT - 表示正在等待足够的时间过去以确保远程 TCP 收到了对其连接终止请求的确认。
关闭 - 表示根本没有连接状态。
TCP 连接会响应事件从一种状态转换为另一种状态。事件是用户调用、打开、发送、接收、关闭、中止和状态;传入段,特别是包含 SYN、ACK、RST 和 FIN 标志的段;以及超时。
图 6 中的状态图仅说明状态变化,以及导致的事件和产生的操作,但没有涉及错误条件或与状态变化无关的操作。在后面的部分中,将提供有关 TCP 对事件的反应的更多详细信息。
Key Concept: If one device setting up a TCP connection sends a SYN and then receives a SYN from the other one before its SYN is acknowledged, the two devices perform a simultaneous open, which consists of the exchange of two independent SYN and ACK message sets. The end result is the same as the conventional three-way handshake, but the process of getting to the ESTABLISHED state is different. The possibility of collision normally occurs in Peer-2-Peer connection.
缓冲区管理 当发送方(在本例中假设为客户端)要建立连接时,数据包会进入传输缓冲区。数据包应附加一些序列号。发送方选择序列号以最大程度地降低使用已使用序列号的风险。客户端使用该序列号和数据以及数据包长度字段发送数据包。服务器在接收到数据包后,会发送下一个预期序列号的确认(ACK)。它还会发送带有自身序列号的 SYN。
客户端在接收到这两个消息(SYN 和 ACK)后,会向接收方发送 ACK,其中包含从接收方获取的下一个预期序列号。因此,在客户端和服务器之间建立了序列号。现在,它们已准备好进行数据传输。即使在发送数据时,也遵循相同的序列号概念。
TCP 传输策略
TCP 中的窗口管理不像大多数数据链路协议那样直接与确认相关联。例如,假设接收方具有 4096 字节的缓冲区,如图所示。如果发送方传输了一个 2048 字节的段,并且该段被正确接收,接收方将确认该段。但是,由于它现在只有 2048 字节的缓冲区空间(直到应用程序从缓冲区中删除一些数据),它将以下一个预期字节开始,通告一个大小为 2048 的窗口。
现在发送方传输了另外 2048 字节,这些字节被确认,但通告的窗口为 0。发送方必须停止,直到接收主机上的应用程序进程从缓冲区中删除一些数据,此时 TCP 可以通告一个更大的窗口。
当窗口为 0 时,发送方通常可能不会发送段,但有两种例外情况。首先,可以发送紧急数据,例如,允许用户终止在远程机器上运行的进程。其次,发送方可以发送一个 1 字节的段,使接收方重新通告下一个预期字节和窗口大小。TCP 标准明确提供此选项以防止在窗口通告丢失时发生死锁。
发送方不需要在应用程序中的数据到达后立即传输数据。接收方也不需要尽快发送确认。当前 2 KB 的数据到达时,TCP 知道有一个 4 KB 的窗口可用,完全可以将数据缓冲起来,直到另外 2 KB 的数据到达,以便能够传输一个有效负载为 4 KB 的段。这种灵活性可以用来提高性能。
考虑一个与交互式编辑器建立的 telnet 连接,该编辑器会对每个按键做出反应。在最坏的情况下,当一个字符到达发送 TCP 实体时,TCP 会创建一个 21 字节的 TCP 段,然后将其交给 IP 作为 41 字节的 IP 数据报发送。在接收方,TCP 会立即发送一个 40 字节的确认(20 字节的 TCP 头部和 20 字节的 IP 头部)。稍后,当编辑器读取了该字节时,TCP 会发送一个窗口更新,将窗口向右移动 1 个字节。此数据包也是 40 字节。最后,当编辑器处理完该字符后,它会以 41 字节数据包的形式回显该字符。总共使用了 162 字节的带宽,并为每个输入的字符发送了四个段。当带宽稀缺时,这种处理方式不可取。
许多 TCP 实现用来优化这种情况的一种方法是将确认和窗口更新延迟 500 毫秒,以期获得一些可以免费搭乘的数据。假设编辑器在 500 毫秒内回显,现在只需要将一个 41 字节数据包发送回远程用户,从而将数据包数量和带宽使用量减少一半。虽然此规则减少了接收方对网络的负载,但发送方仍然通过发送包含 1 字节数据的 41 字节数据包的方式低效地运行。减少这种使用的一种方法称为 Nagle 算法(Nagle,1984)。Nagle 建议很简单:当数据一次一个字节地进入发送方时,只发送第一个字节,并将所有其他字节缓冲起来,直到确认了未完成的字节。然后,将所有缓冲的字符在一个 TCP 段中发送,并开始重新缓冲,直到它们全部被确认。如果用户打字速度很快,并且网络速度很慢,每个段中可能会包含大量的字符,从而大大减少了带宽的使用。该算法还允许在足够的数据累积到足以填满窗口的一半或最大段时发送新数据包。
Nagle 算法被 TCP 实现广泛使用,但在某些情况下,最好禁用它。特别是,当通过互联网运行 X 窗口应用程序时,必须将鼠标移动发送到远程计算机。(X 窗口系统是大多数 UNIX 系统上使用的窗口系统。)将它们收集起来进行突发发送会导致鼠标光标移动不正常,这会使用户不满意。
另一个可能降低 TCP 性能的问题是愚蠢窗口综合征。当数据以大块形式传递到发送 TCP 实体,而接收方上的交互式应用程序一次读取一个字节的数据时,就会出现此问题。要了解此问题,请查看下图。最初,接收方上的 TCP 缓冲区已满,发送方知道这一点(即窗口大小为 0)。然后,交互式应用程序从 TCP 流中读取一个字符。此操作使接收 TCP 很高兴,因此它会向发送方发送一个窗口更新,表示可以发送 1 个字节。发送方照办,发送 1 个字节。缓冲区现在已满,因此接收方确认了 1 字节的段,但将窗口设置为 0。这种行为可以无限期地持续下去。
Clark 的解决方案是阻止接收方发送 1 字节的窗口更新。相反,它必须等待,直到它有足够的可用空间并通告该空间。具体来说,接收方在能够处理在建立连接时通告的最大段大小或其缓冲区为空一半之前,不应该发送窗口更新,以较小者为准。
此外,发送方也可以通过不发送很小的段来提供帮助。相反,它应该尝试等待,直到它在窗口中累积了足够的空闲空间以发送一个完整段,或者至少发送一个包含接收方缓冲区大小一半的段(它必须从过去接收到的窗口更新模式中估计这个大小)。
Nagle 算法和 Clark 对愚蠢窗口综合征的解决方案是互补的。Nagle 试图解决由发送应用程序以一次一个字节的方式向 TCP 传递数据引起的问题。Clark 试图解决接收应用程序一次一个字节地从 TCP 中提取数据的问题。两种解决方案都有效,可以协同工作。目标是发送方不发送小的段,接收方也不请求小的段。
接收 TCP 可以进一步提高性能,而不仅仅是以大单位进行窗口更新。与发送 TCP 一样,它也可以缓冲数据,因此它可以阻止来自应用程序的 READ 请求,直到它有一个大的数据块可以提供。这样做减少了对 TCP 的调用次数,从而减少了开销。当然,它也会增加响应时间,但对于非交互式应用程序(如文件传输)来说,效率可能比对单个请求的响应时间更重要。另一个接收方问题是:如何处理乱序段。它们可以保留或丢弃,由接收方决定。当然,只有在接收了所有直到被确认的字节的数据后,才能发送确认。如果接收方收到段 0、1、2、4、5、6 和 7,它可以确认直到段 2 中最后一个字节的所有数据。当发送方超时时,它会重新传输段 3。如果接收方缓冲了段 4 到 7,在收到段 3 后,它可以确认直到段 7 末尾的所有字节。
建立连接
只有当两台机器之间不存在连接、两台机器都同意连接,并且两台机器都具有足够的 TCP 资源来为连接提供服务时,才能在两台机器之间建立连接。如果其中任何一个条件不满足,则无法建立连接。应用程序或系统管理例程可以触发连接的接受。
当建立连接时,它将获得某些属性,这些属性在连接关闭之前有效。通常,这些属性将是优先级值和安全值。当连接正在建立过程中时,这两个应用程序会商定这些设置。
在大多数情况下,两个应用程序都会期望一个连接,因此它们会发出主动或被动打开请求。下图显示了 TCP 打开的流程图。该过程从机器 A 的 TCP 接收来自其 ULP 的连接请求开始,它向机器 B 发送一个主动打开原语。构造的段将具有设置的 SYN 标志(设置为 1)并将分配一个序列号。图中使用 SYN SEQ 50 表示 SYN 标志已打开,序列号(初始发送序列号或 ISS)为 50。(可以随意选择任何数字。)
机器 B 上的应用程序将已经向其 TCP 发出了被动打开指令。当收到 SYN SEQ 50 段时,机器 B 的 TCP 将向机器 A 发送一个确认,序列号为 51。机器 B 还将设置其自身的初始发送序列号。图中将此消息显示为 ACK 51; SYN 200 表示该消息是一个确认消息,序列号为 51,它具有设置的 SYN 标志,并且 ISS 为 200。
收到后,机器 A 会将其自己的确认消息发送回,并将序列号设置为 201。图中显示为 ACK 201。然后,机器 A 和机器 B 都通过 ULP 向请求的应用程序发送连接打开消息,从而打开并确认了连接。
如前所述,远程机器不需要有被动打开指令。在这种情况下,发送机器将提供发送和接收套接字号,以及优先级、安全性和超时值。两个应用程序同时请求主动打开是常见的。这很容易解决,尽管这确实会涉及更多网络流量。
数据传输
信息传输过程非常简单,如下图所示。对于从 ULP 收到的每个数据块,机器 A 的 TCP 会对其进行封装,并将其发送到机器 B,并附带一个递增的序列号。机器 B 接收消息后,会用一个段确认消息进行确认,该消息会递增下一个序列号(因此表明它收到了所有到该序列号为止的消息)。图中只显示了单向传输的一个信息段。
TCP 数据传输服务实际上包含六种不同的子服务
全双工:允许连接的两端在任何时间,甚至同时进行传输。
及时性:使用计时器确保数据在合理的时间内传输。
有序:从一个应用程序发送的数据将在另一端按相同顺序接收。即使数据报可能通过 IP 以乱序的方式接收,也会发生这种情况,因为 TCP 会在将消息传递到更高层之前,以正确的顺序重新组装消息。
标记:所有连接都具有商定的优先级和安全值。
流量控制:TCP 可以通过使用缓冲区和窗口限制来调节信息流。
错误校正:校验和确保数据无误(在校验和算法的限制范围内)。
关闭连接
要关闭连接,其中一个 TCP 会从 ULP 接收一个关闭原语,并发出一个消息,并将 FIN 标志设置为打开。如下图 8 所示。在该图中,机器 A 的 TCP 用下一个序列号向机器 B 发送关闭连接的请求。机器 B 随后会发送一个对请求的确认及其下一个序列号。在此之后,机器 B 通过其 ULP 将关闭消息发送到应用程序,并等待应用程序确认关闭。此步骤并非严格必要;TCP 可以不经应用程序批准就关闭连接,但一个行为良好的系统会将状态更改告知应用程序。
在从应用程序接收到关闭连接的批准(或请求超时后),机器 B 的 TCP 会向机器 A 发送一个带有 FIN 标志的段。最后,机器 A 确认关闭,连接终止。
当一方关闭套接字时,可能会发生连接的突然终止。这可以在不通知另一台机器的情况下进行,也不考虑两台机器之间正在传输的任何信息。除了由于故障或电源故障造成的突然关闭外,突然终止还可以由用户、应用程序或判断连接值得终止的系统监视程序启动。连接的另一端可能直到尝试发送消息并且计时器超时时才会意识到发生了突然终止。
为了跟踪所有连接,TCP 使用连接表。每个现有连接在表中都有一个条目,显示有关端到端连接的信息。下面显示了 TCP 连接表的布局。
各列的含义如下
状态:连接状态(关闭、正在关闭、监听、等待等)。
本地地址:连接的 IP 地址。处于监听状态时,这将设置为 0.0.0.0。
本地端口:本地端口号。
远程地址:远程主机的 IP 地址。
远程端口:远程连接的端口号。
TCP 重传和超时
[edit | edit source]我们知道 TCP 提供可靠的数据传输。但是,它如何知道何时重传已传输的包。确实,接收方会用下一个预期的序列号确认接收到的包。但是,如果发送方没有收到任何 ACK,该怎么办?
考虑以下两种情况
未收到 ACK:在这种情况下,接收方会传输累积 ACK,但该帧在传输过程中丢失。发送方通常会在从其缓冲区中刷新已发送的包之前等待此累积 ACK。但是,为此,它必须开发一些机制,以便发送方可以在很长时间内未收到 ACK 时采取一些措施。这里用于此目的的机制是计时器。TCP 在传输包时立即设置计时器。如果在超时之前收到 ACK,则 TCP 会从其缓冲区中刷新这些包以腾出空间。如果在超时之前未收到 ACK,则 TCP 会再次重传该包。但从哪里选择此超时间隔呢?我们将很快看到查找该过程的步骤。
收到重复 ACK:在这种情况下,接收方会多次向发送方发送同一个接收到的包的 ACK。但是,您有没有想过这怎么会发生?嗯,这种事情可能会由于网络问题在某些时候发生,但是如果接收方多次(2-3 次以上)收到 ACK,那么这将具有一定的意义。所有这些问题都始于接收方。接收方会一直向接收到的帧发送 ACK。此 ACK 是累积性的。这意味着接收方有一个缓冲区。用于发送累积 ACK 的算法可能取决于缓冲区区域的填充量或剩余量,或者可能取决于计时器。通常,会设置计时器,以便在特定时间间隔后,接收方会发送累积 ACK。但如果发送方速率非常高怎么办?在这种情况下,接收方缓冲区会变满,然后它将无法再存储来自发送方的任何包。在这种情况下,接收方会一直发送重复 ACK,这意味着缓冲区已满,并且此后不会再接受任何包。此消息有助于发送方控制流速率。
整个过程使 TCP 成为一个自适应流量控制协议。这意味着在发生拥塞时,TCP 会调整其流速率。有关这方面的更多内容将在拥塞控制主题中介绍。此外,TCP 中没有负 ACK。以上两种情况会向发送方传达有关接收方状态的正确信息。现在让我们重点关注 TCP 如何选择超时间隔。
选择超时间隔
计时器是根据包从发送方到接收方完成往返所需的时间选择的。此往返时间称为 RTT。但条件,即 RTT 不会始终保持相同。实际上,RTT 会随着时间的推移而发生很大变化。因此,需要将一些平均值包含到超时间隔的计算中。以下步骤将被执行。
1. 基于先前的结果计算平均 RTT(运行平均值)。
2. 测量该特定时间的 RTT,该值取决于当时的网络条件和拥塞情况(测量值)。
3. 计算超时间隔
0.8*(Running avg. ) + (1- 0.8)*(Measured)
值 0.8 可以根据需要更改,但必须小于 1。
4. 为了获得更准确的结果,可以重复此过程多次。
因此,我们现在已经获得了包完成往返所需的平均时间。为了选择超时间隔,需要将该值乘以某个因子,以留出一些余地。
5. 因此,
超时间隔 = 2 * (步骤 4 中获得的值)
如果我们继续绘制运行平均值和该特定时间的测量值的图表,我们会发现运行平均值几乎保持不变,而测量值波动较大。下面是针对这两个值绘制的图表。这解释了为什么运行平均值乘以的值大于用于乘以测量时间的的值。
比较:TCP 和 UDP
[edit | edit source]用户数据报协议 (UDP) 和传输控制协议 (TCP) 是 TCP/IP 协议套件中传输层的“兄弟”。它们执行相同的角色,为应用程序和互联网协议 (IP) 的数据移动功能提供接口,但它们以非常不同的方式执行此操作。因此,这两个协议为更高层的协议提供了选择,允许每个协议根据其需要选择合适的协议。
下表有助于说明这两个协议最重要的基本属性以及它们之间的对比
练习题
[edit | edit source]这里的练习题包括作业题以及答案。这将有助于学生掌握 TCP 的概念,并鼓励他们从 Kurose 和 Tanenbaum 的书中做更多练习题。
1) UDP 和 TCP 使用 1 的补码来进行校验和计算。假设您有以下三个 8 位字节:01010101、01110000、01001100。这些 8 位字节的和的 1 的补码是多少?(注意,虽然 UDP 和 TCP 在计算校验和时使用 16 位字,但在这个问题中,您被要求考虑 8 位加数。)显示所有步骤。为什么 UDP 对总和取 1 的补码;也就是说,为什么不直接使用总和呢?在 1 的补码方案中,接收方如何检测错误?一个位的错误有可能不被检测到吗?两个位的错误呢?
解答: 01010101 + 01110000 + 11000101 = 110001010
10001010 的 1 的补码 = 校验和 = 01110101。
在接收端,将 3 个消息和校验和加在一起以检测错误。总和应该始终只包含二进制 1。如果总和包含 0 项,则接收方知道存在错误。接收方将检测到 1 位错误。但对于 2 位错误,情况可能并非总是如此,因为两个不同的位可能会改变,但总和可能仍然相同。
2) 对以下问题回答“正确”或“错误”,并简要说明您的答案
a) 使用 SR 协议,发送方有可能收到对超出其当前窗口的包的 ACK。
正确。考虑以下情况:发送方发送的第一个包没有收到 ACK,因为计时器已过期。因此,它会再次发送该包。在此期间,会收到第一个包的 ACK。因此,发送方清空其缓冲区,并用新包填充缓冲区。与此同时,可能会收到第二个帧的 ACK。因此,即使包超出当前窗口,也可能会收到 ACK。
b) 使用 GBN,发送方有可能收到对超出其当前窗口的包的 ACK。
正确。与 (a) 中提供的相同论据在此处适用。
c) 交替位协议与发送方和接收方窗口大小为 1 的 SR 协议相同。
正确。交替位协议将 0 和 1 作为交替 ACK。这里,累积 ACK 不可能,因为每个包接收后都需要发送 ACK。因此,SR 协议开始表现得像交替位协议。
d) 交替位协议与发送方和接收方窗口大小为 1 的 GBN 协议相同。
正确。相同的论据在此处适用。
3) 考虑用于估计 RTT 的 TCP 位置。假设 a=0.1,令样本 RTT1 为最新的样本 RTT,令样本 RTT2 为次最新的样本 RTT,依此类推。
a) 对于给定的 TCP 连接,假设已返回四个确认,对应样本 RTT 分别为样本 RTT4、样本 RTT3、样本 RTT2、样本 RTT1。用四个样本 RTT 表示估计的 RTT。
b) 将您的公式推广到 n 个样本 RTT。
c) 对于 (b) 部分的公式,令 n 趋于无穷大。解释为什么这种平均过程称为指数移动平均。
解决方案
a)
估计的 RTT1 = 样本 RTT1
估计的 RTT2 = (1-a) 估计的 RTT1 + a 样本 RTT2 = (1-a) 样本 RTT1 + a 样本 RTT2
估计的 RTT3 = (1-a) 估计的 RTT2 + a 样本 RTT3 = (1-a)2 样本 RTT1 + (1-a)a 样本 RTT2 + a 样本 RTT3''
估计的 RTT4 = (1-a) 估计的 RTT3 + a 样本 RTT4 = (1-a)3 样本 RTT1 + (1-a)2a 样本 RTT2 + (1-a)a 样本 RTT3 + a 样本 RTT4
b)
估计的 RTTn = (1-a)(n-1) 样本 RTT1 + (1-a)(n-2)a 样本 RTT2 + (1-a)(n-3)a 样本 RTT3 +... (1-a)a 样本 RTTn-1 + a 样本 RTTn
4) 我们从文本中了解到,TCP 在收到三个重复的 ACK 后才会执行快速重传。您认为 TCP 设计人员为什么选择不在收到第一个重复的 ACK 后执行快速重传?
解决方案:假设发送方发送了 3 个连续的数据包 1、2 和 3。接收方收到 1 后,立即发送对它的 ACK。假设接收方收到 3 而不是 2,原因是乱序。由于接收方尚未收到 2,因此再次发送对 1 的 ACK。因此,发送方收到了对 1 的第二个 ACK。尽管如此,它仍然继续等待。现在接收方收到 2 时,发送 ACK 2,然后发送 ACK 3。因此,在重传数据包之前等待超过 2 个 ACK 始终是安全的。
5) 您认为 TCP 为什么避免为重传段测量样本 RTT?
解决方案:让我们看看如果 TCP 测量重传段的样本 RTT 会出现什么问题。假设源发送数据包 P1,P1 的计时器超时,然后源发送 P2,这是同一个数据包的新副本。假设源测量 P2(重传数据包)的样本 RTT。最后假设在发送 P2 后不久,收到对 P1 的确认。源将错误地将此确认视为对 P2 的确认,并计算出不正确的样本 RTT 值。