详解TCP如何确保可靠传输(确认应答,重传机制,滑动窗口,流量控制)

TCP协议格式

在这里插入图片描述

  1. 源端口目的端口各占2字节、

  2. 序号: 占4字节,范围 [0 ~ 232-1],使用 mod 232计算(即序号到232-1下一个为0)。TCP面向字节流,在传输的字节流中每个字节都按顺序编号整个传送的字节流的起始序号在建立连接时设置(三次握手)
    首部序号字段的值是本报文发送数据起始序号(第一个字节的序号)

  3. 确认序号:占4字节,是期望收到对方下一个报文段的起始序号(对方发送第一个数据字节的序号)
    确认序号 = 对方前一条发送的起始序号 + 数据长度(字节)
    若确认号=N,则表明:到序号 N-1 为止的所有数据都已正确收到。

  4. 数据偏移:4位首部长度,即报文数据的起始处距离报文的起始处有多远(因为首部长度不确定)。4位二进制所能表示最大值为1111,即15;数据偏移的单位是32位(4字节),所以TCP首部最大长度15*4 = 60字节(即选项小于等于40字节)

  5. 保留位:6位
    在这里插入图片描述

  6. 窗口:16位窗口大小(2字节)。是指发送本报文方的接收窗口大小;窗口值告诉对方:从本报文段首部的确认号算起,接受方允许对方数据的发送量(字节);因为TCP全双工通信,接收方数据缓存空间是有限的
      窗口值是接收方让发送方设置其发送窗口大小依据

  7. 紧急指针:占2字节。仅在URG=1时才有意义,指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据)。因此,紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为零时也可发送紧急数据

  8. 选项(变长)

在这里插入图片描述

MSS 是TCP连接双方每次发送数据的最大值 (TCP报文段长度 - TCP首部长度)
有MSS,IP就不用在对数据进行分片,也受数据链路层MTU(最大传输单元)的限制

ip header + tcp header + MSS <= MTU

在这里插入图片描述


TCP的可靠传输

TCP主要提供了检验和、序列号/确认应答、超时重传、最大消息长度、滑动窗口控制等方法实现了可靠性传输。

检验和

检验和
占2字节。检验和字段检验的范围包括首部和数据这两部分。和UDP一样,在计算检验和时,要在TCP报文段的前面加上12字节的伪首部。伪首部的格式与图中UDP用户数据报的伪首部一样。但应把伪首部第4个字段中的17改为6(TCP的协议号是6),把第5字段中的UDP长度改为TCP长度。接收方收到此报文段后,仍要加上这个伪首部来计算检验和。若使用IPv6,则相应的伪首部也要改变。

UDP伪首部格式

在这里插入图片描述

通过检验和的方式,接收端可以检测出来数据是否有差错和异常,假如有差错就会直接丢弃TCP段,重新发送。

在这里插入图片描述

确认应答(序列号)

回顾序号与确认序号概念
在这里插入图片描述

在这里插入图片描述

即发送方发送消息需要接收方确认;本质上是对序号的确认,就是通过确认序号告诉发送方下一次需要发送的数据,也表明上一个序号及之前的数据都收到了

上述过程中,只要发送端有一个包传输,接收端没有回应确认包(ACK包),都会重发。或者接收端的应答包,发送端没有收到也会重发数据。这就可以保证数据的完整与可靠。

重传机制

TCP 针对数据包丢失的情况,会用重传机制解决。

接下来说说常见的重传机制:
1.超时重传 2.快速重传 3.SACK

超时重传

重传机制的其中一个方式,在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据

一般发生超时重传的两种常见情况

  1. 是数据包丢失 2. 是确认应答的ack丢失在这里插入图片描述

RTO超时时间设置

RTT 报文往返时间: 数据发送到接受确认的时间差值

在这里插入图片描述

RTO 超时重传时间:超时重传时间 RTO 的值应该略大于报文往返 RTT 的值。

因为如果当超时时间 RTO 较大时,重发慢,丢了半天才重发,效率低,性能差;
当超时时间 RTO 较小时,会导致可能并没有丢就重发,重发的快,但是会增加网络拥塞,可能导致更多的超时,更多的超时导致更多的重发,恶性循环。

《计算机网络第七版》 在这里插入图片描述

实际上「报文往返 RTT 的值」是经常变化的,因为我们的网络也是时常变化的。也就因为「报文往返 RTT 的值」 是经常波动变化的,所以「超时重传时间 RTO 的值」应该是一个动态变化的值
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

如果超时重发的数据,再次超时的时候,又需要重传的时候,TCP 的策略是超时间隔加倍。

也就是每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。

快速重传

采用快重传算法可以让发送方尽早知道发生了个别报文段的丢失。快重传算法首先要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认,即使收到了失序的报文段也要立即发出对己收到的报文段的重复确认。

快重传算法规定,发送方只要连收到3个重复确认,就知道接收方确实没有收到报文段,因而应当立即进行重传(即“快重传”),这样就不会出现超时,发送方也不就会误认为出现了网络拥塞。使用快重传可以使整个网络的吞吐量提高约20%。

如图5-26所示,接收方收到了M1和M2后都分别及时发出了确认。现假定接收方没有收到M3但却收到了M4。本来接收方可以什么都不做。但按照快重传算法,接收方必须立即发送对M2的重复确认,以便让发送方及早知道接收方没有收到报文段M3。发送方接着发送Ms和M6。接收方收到后也仍要再次分别发出对M2的重复确认。这样,发送方共收到了接收方的4个对M2的确认,其中后3个都是重复确认。发送方只要连收到3个重复确认,就知道接收方确实没有收到报文段M3在这里插入图片描述

所以,快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。

问题:快速重传机制只解决了一个问题,就是超时时间的问题,但是它依然面临着另外一个问题。就是重传的时候,是重传一个,还是重传所有的问题。

在这里插入图片描述
如果只选择重传 Seq2 一个报文,那么重传的效率很低。因为对于丢失的 Seq3 报文,还得在后续收到三个重复的 ACK3 才能触发重传。
如果选择重传 Seq2 之后已发送的所有报文,虽然能同时重传已丢失的 Seq2 和 Seq3 报文,但是 Seq4、Seq5、Seq6 的报文是已经被接收过了,对于重传 Seq4 ~Seq6 折部分数据相当于做了一次无用功,浪费资源。

所以 SACK 选择确认派上了用场

选择确认 SACK

若收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据,那么能否设法只传送缺少的数据而不重传已经正确到达接收方的数据?
答案是可以的。选择确认就是-种可行的处理方法。

在TCP首部的选项字段提供了SACK的选项,它可以将已收到的数据的信息(边界信息)发送给「发送方」,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据

RFC2018 规定,如果要使用选择确认SACK,那么在建立TCP连接时,就要在TCP首部的选项中加上“允许SACK"的选项,而双方必须都事先商定好。如果使用选择确认,那么原来首部中的“确认号字段”的用法仍然不变。只是以后在TCP报文段的首部中都增加了SACK 选项以便报告收到不连续字节块的边界

实际场景:

发送方收到了三次同样的 ACK 确认报文,于是就会触发快速重发机制,通过 SACK 信息发现只有 200~299 这段数据丢失,则重发时,就只选择了这个 TCP 段进行重复在这里插入图片描述

D- SACK

Duplicate SACK,即重复的选择确认,使用了 SACK 来告诉「发送方」有哪些数据被重复接收了

主要有下列两种常见情况:

  1. ACK包丢失

「接收方」发给「发送方」的两个 ACK 确认应答都丢失了,所以发送方超时后,重传第一个数据包(3000 ~ 3499)
于是「接收方」发现数据是重复收到的,于是回了一个 SACK = 3000~3500,告诉「发送方」 3000~3500 的数据早已被接收了,因为 ACK 都到了 4000 了,已经意味着 4000 之前的所有数据都已收到,所以这个 SACK 就代表着 D-SACK。
这样「发送方」就知道了,数据没有丢,是「接收方」的 ACK 确认报文丢了。在这里插入图片描述

  1. 网络延时

数据包(1000~1499) 被网络延迟了,导致「发送方」没有收到 Ack 1500 的确认报文。
而后面报文到达的三个相同的 ACK 确认报文,就触发了快速重传机制,但是在重传后,被延迟的数据包(1000~1499)又到了「接收方」;
所以「接收方」回了一个 SACK=1000~1500,因为 ACK 已经到了 3000,所以这个 SACK 是 D-SACK,表示收到了重复的包。
这样发送方就知道快速重传触发的原因不是发出去的包丢了,也不是因为回应的 ACK 包丢了,而是因为网络延迟了。在这里插入图片描述

使用D-SACK的好处:

  1. 可以让「发送方」知道是发出去的包丢了,还是接收方回应的 ACK 包丢了;
  2. 可以知道是不是「发送方」的数据包被网络延迟了;
  3. 可以知道网络中是不是把「发送方」的数据包给复制了;

滑动窗口

因为TCP 是每发送一个数据,都要进行一次确认应答。当上一个数据包收到了应答了,再发送下一个;这样一问一答的模式显然效率低下,数据包的往返时间越长,通信的效率就越低,网络吞吐量越低
所以为了提高传输的数据量,引入了窗口的概念

TCP是全双工通信,通信的每一方都在发送和接受报文段,都有自己的发送窗口和接受窗口,注意区分!

滑动窗口机制:允许窗口中的数据发送到网路进行传输(不需要等待确认),提高数据吞吐量。
窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值

窗口大小

TCP 头里有一个字段叫 Window,也就是窗口大小
这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据

在这里插入图片描述

发送方窗口

在这里插入图片描述
发送方把数据「全部」都一下发送出去后,可用窗口的大小就为 0 了,表明可用窗口耗尽,在没收到 ACK 确认之前是无法继续发送数据了。

当收到之前发送的数据 32~36 字节的 ACK 确认应答后,如果发送窗口的大小没有变化,则滑动窗口往右边移动 5 个字节,因为有 5 个字节的数据被应答确认,接下来 52~56 字节又变成了可用窗口,那么后续也就可以发送 52~56 这 5 个字节的数据了。在这里插入图片描述

接收方窗口

在这里插入图片描述

接收窗口的大小是约等于发送窗口的大小的

因为滑动窗口并不是一成不变的。比如,当接收方的应用进程读取数据的速度非常快的话,这样的话接收窗口可以很快的就空缺出来。那么新的接收窗口大小,是通过 TCP 报文中的 Windows 字段来告诉发送方。那么这个传输过程是存在时延的,所以接收窗口和发送窗口是约等于的关系。

在这里插入图片描述
第一,缓存空间和序号空间都是有限的,并且都是循环使用的。最好是把它们画成圆环状的。但这里为了画图的方便,我们还是把它们画成长条状的。
第二,由于实际上缓存或窗口中的字节数是非常之大的,因此图5-19 仅仅是个示意
图,没有标出具体的数值。但用这样的图来说明缓存和发送窗口以及接收窗口的关系是很清楚的。

流量控制

接收端处理数据的速度是有限的,如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列反应。

因而TCP支持让「发送方」根据「接收方」的实际接收能力控制发送的数据量这个机制就叫做流量控制(Tlow Control)。

TCP是利用窗口来实现流量控制的

在这里插入图片描述

在这里插入图片描述

窗口扩大因子M:
接收端如何把窗口大小告诉发送端呢?在我们的TCP首部中,有一个16为窗口字段,就是存放了窗口大小信息;16位数字最大表示65535,,那么TCP窗口最大就是65536字节吗?
实际上,TCP首部40字节选项上还包含了一个窗口扩大因子M,实际窗口大小是窗口字段的值左移M位;

窗口关闭 / 探测

TCP 通过让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制。
如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭

在这里插入图片描述
如不采取措施,这种相互等待的过程,会造成了死锁的现象。

TCP 为每个连接设有一个持续定时器,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器。

如果持续计时器超时,就会发送窗口探测 ( Window probe ) 报文,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。

在这里插入图片描述
如果接收窗口仍然为 0,那么收到这个报文的一方就会重新启动持续计时器
如果接收窗口不是 0,那么死锁的局面就可以被打破了。

窗口探测的次数一般为 3 次,每次大约 30-60 秒(不同的实现可能会不一样)。如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 RST (复位标志,用于非正常地关闭连接)报文来中断连接。

糊涂窗口综合症

如果接收方太忙了,来不及取走接收窗口里的数据,那么就会导致发送方的发送窗口越来越小。
到最后,如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症

详细参考:糊涂窗口综合症

累计确认

图中的 ACK 600 确认应答报文丢失,也没关系,因为可以通过下一个确认应答进行确认,只要发送方收到了 ACK 700 确认应答,就意味着 700 之前的所有数据「接收方」都收到了。这个模式就叫累计确认或者累计应答。在这里插入图片描述

延迟应答

为什么要延迟?

  1. 每次接收方回复确认应答的时候,会在TCP头部当中携带窗口大小, 来告知发送方自己的接收能力
  2. 发送方是要通过接收方通告的窗口大小来调整发送窗口

假设不考虑网络的情况下:
● 接收方通告的窗口大小越大,则发送窗口越大, 则发送方发送的数据越多
● 接收方通告的窗口大小越小,则发送窗口越小, 则发送方发送的数据越少

如果接收方收到数据就立即返回ACK应答,这时候的缓冲区中接受的数据许多还没能够处理,缓冲区的剩余大小就是窗口大小,所以此时返回的窗口值会比较小。
在收到数据以后并不立即返回确认应答,延迟一小会,等待缓冲区中数据被处理,接收缓冲区空间变大一些,再进行应答(此时确认应答的窗口值会大一些)——这是延迟应答。

在这里插入图片描述

窗口越大,网络吞吐量就越大,传输效率就越高,我们的目标是在保证网络不拥堵的情况下尽量提高传输效率!
在这里插入图片描述

那么所有的包都可以延迟应答吗?? 不是的,与以下有关:
● 数量限制:每隔N个包就应答一次;
● 时间限制:超过最大延迟时间就应答一次;
具体的数量和超时时间,依操作系统不同也有差异;一般N取2,超时时间取200ms;

在系统中,有一个固定的定时器每隔200ms会来检查是否需要发送ACK包,这样做有两个目的。

  1. 这样做的目的是ACK是可以合并的,也就是指如果连续收到两个TCP包,并不一定需要ACK两次,只要回复最终的ACK就可以了,可以降低网络流量;
  2. 如果接收方有数据要发送,那么就会在发送数据的TCP数据包里,带上ACK信息,这样做,可以避免大量的ACK以一个单独的TCP包发送,减少了网络流量。

捎带应答

捎带应答是指在同一个TCP包中即发送数据又发送确认应答的一种机制。本质上就是回复的TCP数据当中的PSH标志位和ACK标志位被置为1了

根据应用层协议,发送出去的消息到达对端,对端进行处理之后,会返回一个回执。即在很多情况下,客户端服务器在应用层也是“一发一收”的,意味着客户端给服务器说了“How are you”,服务器也会给客户端回一个“Fine,thank you”;
那么这个时候ACK就可以搭顺风车了,和服务器回应的“Fine,thank you”一起回给客户端。

例子有:电子邮件协议的AMTP或POP、文件传输协议FTP中的连接控制部分等,例如远程登录中针对输入的字符进行回送校验也是对发送消息的一种回执。

在此类通信中,TCP的确认应答和回执数据可以通过一个包发送,这种方式叫做捎带应答。通过这种机制,可以使收发的数据量较减少。

另外接受数据以后如果立刻返回数据,就无法实现捎带应答,所以是在延迟应答的基础上,进行的捎带应答。

在这里插入图片描述
网络的利用率会提高,计算机的负荷也会减轻。不过,确认应答必须等到应用层处理完数据并将作为回执的数据返回为止,才能进行捎带应答。

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值