《计算机网络-自顶向下》03. 运输层

TCP

TCP 报文段结构

image-20230322220555736

序号和确认号
  • 序号:报文段首字节在字节流的编号。
  • 确认号:期望从另一方收到的下一个字节的序号。

一个 50000 字节的文件,MSS = 1000 字节,数据流的首字节编号为 0。TCP 将该数据流分为 500 个报文段,给第一个报文段分配序号 0,第二个为报文段分配序号 1000,第三个是 2000,以此类推。每个序号被写入到 TCP 报文段首部的序号字段中。

image-20230322221009351

一条 TCP 连接的双方均可随机地选择初始序号。

TCP 收到失序报文

这个问题留给程序员自己解决,有两种解决方式:

  • 接收方立即丢弃失序报文段。
  • 接收方保留失序的字节,并等待缺少的字节以填补该间隔。

可靠数据传输原理

可靠数据传输:服务模型

image-20230315194052678

大致的抽象过程:准备发送数据,首先发送方先由应用层调用运输层的提供的方法 rdt_send()运输层传递要发送的数据,运输层数据添加自己的首部信息,接着再由运输层调用自己的 udt_send() 向网络层发送分组,通过 UDT 这种不可靠协议的不可靠信道传输给接收方,接收方的网络层收到分组,调用接收方传输层的 rdt_rcv() 向传输层传递该分组,最后接收方的运输层调用自己的 deliver_data() 向应用层传递数据。

注意:可靠数据传输协议的下层协议可能是不可靠的。

构造可靠数据传输协议

有限状态机 FSM

有限状态机 FSM(Finitee-State Machine, FSM),我了解的也不多,简单的画了个图,便于可以读懂书。

image-20230315195830329

rdt1.0 —— 完全可靠的可靠数据传输

rdt1.0 假设:传输过程不会丢包、丢比特,假设是完全可靠的数据传输,且假定双方发送和接收的速率一样快。

image-20230315194539121

可以看到,发送端和接收端的 FSM 都只有一个状态,因此迁移状态也是指向自己。

发送方发送数据和接收方接收数据的过程:

  1. 上层,即应用层调用了运输层的 rdt_send(data) 向运输层传递数据。
  2. 接下来指向该事件所对应的动作,运输层拿到数据后,接着调用 make_pkt(data) 将数据进行处理,得到一个 packet,即包。
  3. 紧接着调用 udt_send(packet) 将包发送给网络层,即信道。
  4. 接收方的网络层,即发送方通过信道将包送到了接收方的网络层,接收方的网络层调用上层(运输层)所提供的 rdt_rcv(packet)
  5. 开始执行 rdt_rcv(packet) 所对应的动作,首先调用 extract(packet, data) 进行拆包,提取发送方发送的真实数据。
  6. 最后调用 deliver_data(data) 将数据发送给上层,即应用层。
rdt2.0 —— 具有比特差错信道的可靠数据传输

rdt2.0 假设:传输过程中可能出现丢比特,但是注意,目前不假设丢包(即分组)。

image-20230315203356850

rdt2.0 中发送方拥有两个状态,分别是“等待来着上层的调用” 以及 “等待ACK或NAK”。

重传机制,重传机制的可靠数据传输协议称为自动重传请求协议(Automatic Repeat reQuest, ARQ)。

ARQ 协议还需要另外三种协议来处理存在比特差错的情况:

  • 差错检测
  • 接收方反馈
  • 重传

发送方发送数据和接收方接收数据的过程:

  1. 发送方应用层调用运输层 rdt_send(data) 将数据传递给运输层,运输层调用 make_pkt(data, checksum) 将数据处理,得到 sndpkt

  2. 将分组 sndpkt 调用 udt_send(sndpkt) 通过信道(即网络层)发送给接收方。

    此时,需要等待来自接收方的响应,因此无法再次发送新的信息。

  3. 接收方网络层收到分组 sndpkt,调用 corrupt(rcvpkt) 看看分组是否有错。(在接收方包的变量名改为了 rcvpkt)

    没错,则响应 ACK,若错了,则响应 NAK。

  4. 发送方接收到了来自接收方的响应信息。

    若是 NAK,则重传上一个分组,若是 ACK,目前暂无动作。

注意:当发送方处于等待 ACK/NAK 的状态时,发送方无法再次发送新的数据。 因此这样的协议被称为停等(stop-and-wait)协议。

rdt2.1 —— 重传协议

rdt2.0 目前还存在的问题:响应的 ACK 和 NAK 分组也可能会受损。

如果一个 ACK 或 NAK 分组受损,发送方将无法知道接收方是否正确的接收了上一次的分组。

解决这个问题,使用一种最简单的实现方式:【rdt2.1】

  • 发送方对数据分组增加一个字段,用于编号,即发送方数据分组的序号。
  • 接收方只需要检查序号即可确定收到的分组是否为一次重传。
  • 对于停等协议,1比特足矣(两个值,0和1,若接收方收到的分组和上次接收的分组编号相同,则判定为重复)。

注意:目前假定不丢分组,因此 ACK/NAK 分组不需要添加分组序号。

可以发现,发送方和接收方比之前多了 1 倍,那是因为需要对“分组0” 和 “分组1” 分别进行处理。

发送方接收到对同一个分组的两次 ACK(即接收到冗余 ACK)后,就知道接收方没有正确接收到跟在被确认两次的分组后的分组,间接达到 NAK 效果,因此发送方将会再次重传。

image-20230315220233809

rdt2.2 —— 无 NAK 的可靠数据传输协议

rdt2.2 是在有特差错信道上实现的一个无 NAK 的可靠数据传输协议。

rdt2.1 余 rdt2.2 的不同之处:rdt2.2 接收方必须包括由一个 ACK 报文所确认的分组序号,发送方必须检查接收到的 ACK 报文中被确认的分组序号。

ACK 分组序号:ACK0、ACK1。

  • 接收方等待分组0,收到分组0,响应 ACK0,表示成功接收到分组0。
  • 接收方等待分组1,收到分组1,响应 ACK1,表示成功接收到分组1。

image-20230316174954052

rdt3.0 —— 具有比特差错的丢包信道的可靠数据传输

假设:比特受损、信道丢包。

发送方发送以及接收方响应 ACK/NAK 状态时都可能丢包,而其中任何一方丢失分组时,自己是不知道的,另一方自然也不知道。那么解决这种方案我们使用其中一种方法,即让发送方负责检测和恢复丢包的工作。假定发送方传输一个分组,该分组或接收方响应该分组的 ACK 发生了丢失。这两种情况下,发送方都收不到应当到来的接收方的响应。

我们可以设定一个超时定时器,若接收方等待响应超过了这个时间,那么将重新发送该分组,以此来确定分组是否丢失。

发送方至少需要等待:RTT + 接收方处理一个分组所需的时间。

既然是重传,那么就可能会造成分组冗余,但我们已经通过分组序号来解决了。

image-20230316180420128

各种过程图:

image-20230316180919076

D过早超时,指的就是定时器设置的时间不合理,正常时间为RTT+接收方处理一个分组的时间,而D所设置的远远小于这个值,因此可能造成以及成功接收的分组会再次重新发送,但是并不影响正常运行。

流水线的可靠传输协议

回退 N 步 —— GBN 协议

特点:

  • 可以连续发送多个分组。
  • 发送方窗口大小为 N。
  • 接收方窗口大小为 1。
  • 不支持乱序接收(因为接收方窗口大小为 1)。
  • 发送连续收到的最大的分组序号的确认(即累积确认)。
窗口格式

窗口格式(黑白版):

image-20230316204353205

  • base:基序号,最早的(即最先发送的)未确认分组的序号。
  • nextseqnum:下一个待发分组的序号。
  • [0, base - 1]:已发送并确认的分组。
  • [base, nextseqnum - 1]:已发送,但未确认的分组。
  • [nextseqnum, base + N - 1]:立即要被发送的分组。

发送方的滑动窗口(鲜艳版):

image-20230316211316392

滑动窗口的滑动方式

image-20230316212848758

累积确认,以及 GBN 流程图

image-20230316220428429

在 GBN 协议中,因为使用的是累积确认,所以接收方将会丢弃所有的失序分组。假定现在期望接收到分组 n n n,而分组 n + 1 n+1 n+1 却到了,如果分组 n n n 丢失,则该分组以及分组 n + 1 n+1 n+1 最终将在发送方根据 GBN 的重传规则而被重传。

  • 优点:接收缓存简单,即接收方不需要缓存任何失序分组。

  • 缺点:丢弃一个接收正确的分组,后续可能导致该分组的重传也可能出错或丢失,因此甚至需要更多的重传。

对于定时器,发送方只有一个定时器。

详细过程图:

image-20230316215253206

选择重传 —— SR 协议

特点:

  • 可以连续发送多个分组。
  • 发送方窗口大小为 N。
  • 接收方窗口大小为 N。
  • 支持乱序接收(因为接收方窗口大小为 N)。
  • 接收到了哪个分组,就响应哪个分组的确认(即非累积确认)。
发送方和接收方的窗口格式

image-20230316215845090

非累积确认,以及 SR 流程图

image-20230316220804598

和 GBN 协议不同的是 SR 接收方的窗口为 >= 1,可以乱序接收。对于定时器,发送方的给每个分组都设置了一个定时器,因此 SR 也不需要向 GBN 那样重新发送所有已发送但未确认的分组。

详细过程图:

image-20230316220909747

可靠数据传输机制以及用途的总结

image-20230316221817196

TCP 单一定时器 RFC 6298【略!!!】

不是我不想了解,是我了解了 RFC 6298 单一定时器后更懵了,书上也没讲,导致图 3-25 理解不够彻底…

书上说,重传 92 后,将重启定时器,第二段只需要在新的超时之前到达就行,也就是说我可以理解为“当一个定时器重传后,将重置下一个报文段的定时时间”。

我现在已经懵逼了,就先按照书上的来吧,我查阅的 RFC 6298 资料和书上的说的貌似都不是一回事,发送多个报文段所处理的方式也不一样,额…

可靠的数据传输

重传与不重传的情况

image-20230322152734389

由于确认丢失而重传

如图一,主机A向主机B发送一个报文段,主机B发送一个确认报文段,结果丢包了,导致在超时时间之前未到达主机A,因此发送方启动超时事件,将重传丢失的那个报文段。

确认报文丢失,但并不重传的情况 1

如图二,主机A发送了两个报文段,即一个92,一个100,主机B收到了这两个报文段,并且发送两个确认报文,分别对应 100 和 120。假设这两个确认报文在超时时间之前还未到达主机A,等超时时间一到,将重传 92,并且重新启动超时定时器,若 120 能在新超时之前到达,则不会重传 100.

确认报文丢失,但并不重传的情况 2

如图三,主机A发送了两个报文段,即一个报文段序号是 92,一个报文段序号是 100,主机B收到后发送两个确认号,分别对应 100 和 120,可是确认号为 100 的那个在传输的过程中丢失了,但确认号为 120 的那个却抵达了,因为 TCP 采用的是累积确认,所以主机A收到了确认号为 120 的报文就表示 119 及之前的都已经被主机B确认了,因此即便 100 丢失,也不需要重传它。

超时间间隔

TCP 重传具有最小序号的已发送还未被确认的报文段,若该报文段多次超时,则每次超时的时间都被进行翻倍。好处是当出现网络拥塞时,若发送方持续重传分组,会使拥塞更加严重,因此 TCP 使每个发送方的重传都是经过越来越长的时间间隔后进行的。

快速重传

超时重传的问题之一就是超时周期可能相对较长。会造成端到端的延迟。我看可以通过冗余 ACK 来解决,一旦接收方收到了 3 个冗余 ACK,TCP 就立即执行快速重传。

快速重传:在该报文段的定时器过期之前重传丢失的报文段。

冗余 ACK 如图所示:

image-20230322214910257

产生 ACK 的建议:

image-20230322215318434

流量控制

流量控制: 接收方控制发送方,不让发送方发送的太多或太快以至于让接收方的缓冲区溢出。

  • LastByteRead: 接收方应用进程从缓存读出的数据流中的最后一个字节编号。
  • LastByteRcvd: 从发送方接收到的数据存入到缓存中的最后一个字节编号。
  • RcvBuffer: 整个接收缓存区。
  • 接收窗口 rwnd 的计算: r w n d = R c v B u f f e r − [ L a s t B y t e R c v d − L a s t B y t e R e a d ] rwnd = RcvBuffer - [LastByteRcvd - LastByteRead] rwnd=RcvBuffer[LastByteRcvdLastByteRead]

接收窗口(rwnd)和接收缓存(RcvBuffer)示意图

image-20230319211938198

如何通过变量 rwnd 来提供流量控制服务?

接收方会把当前的 rwnd 放入到发送至发送方的报文段的接收窗口字段中,以此来通知接收方该连接还有多少可用空间。起初,接收方将 rwnd = RcvBuffer。

当接收方的接收缓存已满,使得 rwnd = 0。并且将 rwnd = 0 通知给发送方后,假设接收方也没有任何数据发送给发送方。此时若接收方突然有空间了,那么请问发送方应该如何知道这个信息呢?如果它不知道,那么就无法再次发送新的数据给接收方,因为上层接收方已经通知了发送方 rwnd = 0。

为了解决发送方不知道接收方有空间而造成发送方阻塞,TCP 规范中要求:

  • 当接收方的接收窗口为 0 时,启动一个定时器,发送方继续发送只有一个字节数据的报文段。
  • 若接收方有空间,则会发送一个包含非 0 的 rwnd 确认报文。

TCP 连接管理

两次握手所带来的的问题

image-20230320194743565

半连接: 采用两次握手建立TCP连接时,会出现半连接状态,这是因为在这种情况下,客户端发送了SYN后,并没有等到服务器端的 ACK+SYN,可能是因为丢包了或延迟了,因此导致服务器端维护着一个半连接。

至于半连接所分配的冗余空间,如果在半连接状态下客户端没有发送ACK包,服务器端会超时关闭连接并释放资源。

老数据: 当两次握手建立连接后,客户端向服务器发送数据,过程中突然断开连接,在服务器接收到这个数据之前,突然又和客户端建立了一个 TCP 连接,接着服务器收到后来到的数据,此时这个数据属于后来的那个连接,该连接会接收这个数据,导致客户端和服务器端的数据不一致。

造成老数据的主要原因是:客户端建立的两次 TCP 连接所采用的序列号一致。

三次握手

image-20230320195528920

三次握手建立连接的步骤:

  1. 客户端向服务器发送一个 SYN 包,并且随机一个初始序号。
  2. 服务器接收到 SYN 包后,回复一个 SYN+ACK+服务端初始序号 的包,表示确认连接请求。
  3. 客户端接收到 SYN+ACK 包后,再次向服务端发送一个 ACK 包,表示服务器端的确认好。

关闭连接

image-20230320200304933

FIN 标志位,谁要关闭连接,就发送包含 FIN = 1 的报文。

TCP 拥塞控制

TCP 使用端到端拥塞控制。

TCP 所采用的的方法是让每一个发送方根据所感知到的网络拥塞程度来限制其能向连接发送流量的速率。

  • 若一个 TCP 发送方感知到从它到目的主机之间的路径上没什么拥塞,则 TCP 发送方增加其发送速率。
  • 若感知到该路径上有拥塞,则 TCP 发送方降低其发送速率。

使用这种方法,有三个问题:

  1. TCP 发送方如何限制其连接发送流量的速率?
  2. TCP 发送方如何感知从它到目的主机之间的路径上存在拥塞?
  3. 当发送方感知到端到端的拥塞时,采用何种算法来改变其发送速率?

TCP 发送方如何限制其连接发送流量的速率?

TCP 连接的每一端都是由一个接收缓存、一个发送缓存和几个变量(LastByteRead、rwnd等)组成。运行在发送方的 TCP 需要跟踪一个额外的变量,即拥塞窗口(congestion window),表示为 cwnd,它对一个 TCP 发送方能向网络中发送流量的速率进行了限制。

发送方传输数据的大小取决于:min(cwnd, rwnd)

TCP 发送方如何感知从它到目的主机之间的路径上存在拥塞?

如何让发送方知道该路径出现了拥塞:

  • 分组超时。
  • 收到 3 个冗余 ACK。

确认发送方发送速率的原则:

  • 报文段丢失,意味拥塞,因此丢失报文段时应当降低 TCP 发送方的速率。
  • 接收方响应确认报文段,这是一切顺利的隐含指示,表示网络不拥塞,因此应该增加发送方的发送速率。
  • 带宽检测。

当发送方感知到端到端的拥塞时,采用何种算法来改变其发送速率?

TCP 拥塞控制算法,该算法主要包含 3 个主要部分:

  1. 慢启动。
  2. 拥塞避免。
  3. 快速恢复。
慢启动

image-20230321212642069

当一条 TCP 连接开始时,cwnd 的值通常初始为一个 MSS 的较小值,这就使得初始发送速率大约为 MSS/RTT。

cwnd 的值以 1 个 MSS 开始,往后每次传输报文段且被确认后,cwnd 将翻倍。

如图,过程如下:

  1. 首先主机A向主机B发送了一个报文段,此时 cwnd = 1。
  2. 主机 A 确认报文后,向主机B发送两个报文段,此时 cwnd = 2。
  3. 主机 A 确认报文后,向主机B发送四个报文段,此时 cwnd = 4。
  4. 以此类推…

可以看到,慢启动的 cwnd 是以指数级增长的。

结束这种指数级增长的方式:

  • 发生超时丢包(即阻塞),TCP 发送方将 cwnd 置为 1,并重新开启慢启动。它还将 ssthresh(慢启动阀值)设置为 cwnd/2,即当检测到拥塞时将 ssthresh 置为 cwnd 的一半。
  • 当检测到拥塞时 ssthresh 是 cwnd 的一半时,为了稳健,不再使用指数级增长的方式。因此当 cwnd = ssthresh 时,结束慢启动并且 TCP 转移到拥塞避免模式。
  • 当检测到 3 个冗余 ACK 时,这时 TCP 执行一种快速重传并进入快速恢复状态。

ssthresh 慢启动阀值,指的是 cwnd 可以到达的最大值。

SS 和 CA:

  • SS 阶段:加倍增加(慢启动模式)。
  • CS 阶段:线性增加(拥塞避免模式)。
拥塞避免

进入拥塞避免模式后,不再采用指数级增长 cwnd 的方式,而是每个 RTT 只将 cwnd 的值增加一个 MSS。

一种通用的做法:对于 TCP 发送方无论何时到达一个新的确认,就将 cwnd 增加 MSS/cwnd 字节。

例如:MSS = 1460,cwnd = 14600,在一个 RTT 内发送 10 个报文段。只要到达一个 ACK,则 cwnd 增加 1/10 MSS,因此等收到 10 个报文段的 ACK 后,cwnd 就增加了一个 MSS。

结束拥塞避免模式的时机:

  • 超时丢包,cwnd 设置为 1 个 MSS,ssthresh = cwnd / 2。
  • 丢包也可以由 3 次冗余 ACK 造成,这种造成超时的方式,处理方式为:cwnd = cwnd / 2 + 3。(3 是收到的 3 个冗余 ACK),ssthresh = ssthresh / 2,最后进入快速恢复状态。
AIMD 拥塞控制

image-20230322003639058

遇到超时事件后,cwnd 下降到一半,随后进入拥塞避免模式,开始线性增长 cwnd。

当收到 3 个冗余 ACK后:

  • cwnd = cwnd / 2
  • 进入拥塞避免模式。

当超时事件发生后:

  • cwnd = 1,进入慢启动模式。
  • 慢启动阈值 = cwnd / 2

3 个冗余的 ACK,表示网络还有一定的传输能力。
超时之前的 3 个冗余 ACK,表示“警报”。

乘性减:丢失事件后将 cwnd 置为 1,将 cwnd / 2 作为阀值,进入慢启动阶段。
加性增:当 cwnd > 阀值,进入拥塞避免阶段。

公平性

image-20230322014606169

有两条 TCP 连接共享一段传输速率为 R 的链路。假设这两条连接具有相同的 MSS 和 RTT,它们有大量数据需要发送,且没有其它 TCP 连接或 UDP 数据报。并且忽略 TCP 的慢启动阶段,以 CA 运行。

如果 TCP 要在这两条 TCP 连接之间平等共享链路带宽,那么实现的吞吐量曲线必须从 45° 射出(平等共享带宽)。

理想状态:两个的吞吐量的和等于 R。

如图,假设一开始并不公平,处于 A 点,此刻连接1的吞吐量比连接2的吞吐量要大,为了公平,它将沿着 45° 前行(两条连接都有相同增长),最终这两条连接共同消耗的带宽将超过 R,导致分组丢失。

分组丢失采取如下措施:

  • 将拥塞窗口 cwnd 减半。
  • 进入拥塞避免。

执行分组丢失的措施后,以为 cwnd 减半,所以此刻到达了 C 点,此刻连接1的吞吐量比连接2的吞吐量要大,一样沿着 45° 前行,一样最终丢包,继续采取措施,最终调整到了连接1的吞吐量等于连接2的吞吐量。

公平性和 UDP
  • UDP 不具备拥塞控制。
  • 在UDP上,总是以恒定的速率将分组注入网络,并且偶尔可能丢失分组。
  • UDP 可能压制 TCP 流量。

公平性和并行的 TCP 连接

  • 当一个应用使用多条并行连接时,它将占用一条拥塞链路中较大比例的带宽。
  • 例如:Web 浏览器通常使用多个并行 TCP 连接来传送一个 Web 页面中的多个对象(即页面资源)。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值