二十、TCP 如何保证消息的可靠性和顺序性(干货版)

题目

TCP 如何保证消息的可靠性和顺序性

TCP 保证可靠性主要依靠下面 7 种机制:

一、校验和

  • 发送方将整个报文段分为多个 16 位的段,然后将所有段进行反码相加,将结果存放在检验和字段中,接收方用相同的方法进行计算,如最终结果为检验字段所有位是全 1 则正确,否则存在错误;
  • 如果收到段的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段;

二、序列号

TCP 会对每个包都进行编号,序列号的作用是:

  • 保证可靠性(当接收到的数据中少了某个序号的数据时,能马上知道);
  • 保证数据的按序到达;
  • 提高效率,可实现多次发送,一次确认;
  • 去除重复数据;

数据传输过程中的确认应答处理、重发控制以及重复控制等功能都可以通过序列号来实现;

三、确认应答机制(ACK)

  • TCP 通过确认应答机制实现可靠的数据传输。在 TCP 的首部中有一个标志位 —— ACK,此标志位表示确认号是否有效;
  • 接收方对于按序到达的数据会进行确认,当标志位 ACK = 1 时,首部的确认字段有效。进行确认时,确认字段值表示这个值之前的数据都已经按序到达了
  • 而发送方如果收到了已发送的数据的确认报文,则继续传输下一部分数据;而如果等待了一定时间还没有收到确认报文就会启动重传机制;

正常情况下的应答机制:

在这里插入图片描述

四、超时重传机制

当报文发出后在一定的时间内未收到接收方的确认,发送方就会进行重传(通常是在发出报文段后设定一个闹钟,到点了还没有收到应答则进行重传),其基本过程如下:

在这里插入图片描述
当然,未收到确认不一定就是发送的数据包丢了,还可能是确认的 ACK 丢了:

在这里插入图片描述
当接收方接收到重复的数据时就将其丢掉,重新发送 ACK。而要识别出重复的数据,就要用到前面提到的序列号了,利用序列号很容易就可以做到去重的效果;

重传时间的确定

  • 报文段发出到收到应答中间有一个报文段的往返时间 RTT显然超时重传时间 RTO 会略大于这个 RTT
  • TCP会根据网络情况动态的计算 RTT,即 RTO 是不断变化的。在 Linux 中,超时以 500ms 为单位进行控制,每次判定超时重发的超时时间都是 500ms 的整数倍;
  • 其规律为:如果重发一次仍得不到应答,就等待 2 * 500ms 后再进行重传,如果仍然得不到应答就等待 4 * 500ms 后重传,依次类推,以指数形式递增
  • 重传次数累计到一定次数后,TCP 认为网络或对端主机出现异常,就会强行关闭连接

超时重传时间 RTO 小于往返时间 RTT

在这里插入图片描述
超时重传时间 RTO 远大于往返时间 RTT

在这里插入图片描述
超时重传时间的确定

在这里插入图片描述
计算超时重传时间
在这里插入图片描述

五、连接管理机制

连接管理机制即 TCP 建立连接时的三次握手和断开连接时的四次挥手

TCP 报文段的首部格式
在这里插入图片描述
在这里插入图片描述

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

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

5.1、三次握手

在这里插入图片描述

建立过程为:

  • 第一次握手:建立连接时,客户端发送 SYN(SYN = j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;
  • 第二次握手:服务器收到 SYN 包,必须确认客户的 SYN(ACK = j + 1),自己也发送一个 SYN(SYN = k),即 SYN + ACK 包,此时服务器进入 SYN_RECV 状态;
  • 第三次握手:客户端收到服务器的 SYN + ACK 包,向服务器发送确认包 ACK(ACK = k + 1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手;

通过这样的三次握手,客户端与服务端建立起可靠的双工的连接,开始传送数据。 三次握手的最主要目的是保证连接是双工的,可靠更多的是通过重传机制来保证的。 但是为什么一定要进行三次握手来保证连接是双工的呢,一次不行么?两次不行么?

  • 第一次握手Server 确认了对方发送正常,自己接收正常;
  • 第二次握手Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常;
  • 第三次握手Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常;

如果是两次握手,则已失效的连接请求报文段突然又传送到了 TCP 服务器,因而导致错误

5.2、四次挥手

由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这好比,我们打电话(全双工), 正常的情况下(出于礼貌),通话的双方都要说再见后才能挂电话,保证通信双方都把话说完了才挂电话

在这里插入图片描述
TCP 的四次挥手,是为了保证通信双方都关闭了连接,具体过程如下:

在这里插入图片描述

  • 第一次挥手:客户端 A 发送一个 FIN,然后进入 FIN_WAIT_1 状态;这表示客户端 A 没有数据要发送给服务器 B 了;
  • 第二次挥手:服务器 B 收到这个 FIN,发回一个 ACK,确认序号为收到的序号加 1,客户端 A 进入 FIN_WAIT_2 状态;服务器 B 告诉客户端 A,我"同意"你的关闭请求;
  • 第三次挥手:服务器 B 发送一个 FIN 给客户端 A,请求关闭连接,同时服务器 B 进入 LAST_ACK 状态;
  • 第四次挥手:客户端 A 发回 ACK 报文确认,并将确认序号设置为收到序号加 1,然后客户端 A 进入 TIME_WAIT 状态,服务器 B 收到客户端 A 的报文段以后,就关闭连接; 此时,客户端 A 等待 2MSL 后依然没有收到回复,则证明服务器 B 已正常关闭,那好,客户端 A 也可以关闭连接了;

为什么连接的时候是三次握手,关闭的时候却是四次握手

Server 端收到 Client 端的 SYN 连接请求报文后,可以直接发送 SYN + ACK 报文。其中 ACK 报文是用来应答的,SYN 报文是用来同步的。 但是关闭连接时,当 Server 端收到 FIN 报文时,很可能并不会立即关闭 SOCKET,所以只能先回复一个 ACK 报文, 告诉 Client 端,“你发的 FIN 报文我收到了”。只有等到我 Server 端所有的报文都发送完了,我才能发送 FIN 报文,因此不能一起发送。故需要四次挥手;

为什么 TIME_WAIT 状态还需要等 2MSL 后才能返回到 CLOSED 状态?

  • TIME_WAIT 状态就是用来重发可能丢失的 ACK 报文。在客户端发送出最后的 ACK 回复,但该 ACK 可能丢失。服务端如果没有收到 ACK 将不断重复发送 FIN 片段。所以客户端不能立即关闭,它必须确认服务端接收到了该 ACK
  • 客户端会在发送出 ACK 之后进入到 TIME_WAIT 状态。**客户端**会设置一个计时器,等待 2MSL 的时间。如果在该时间内再次收到 FIN,那么客户端会重发 ACK 并再次等待 2MSL;
  • 所谓的 2MSL 是两倍的 MSL(Maximum Segment Lifetime)MSL 指一个片段在网络中最大的存活时间,2MSL 就是一个发送和一个回复所需的最大时间;
  • 如果直到 2MSL,客户端都没有再次收到 FIN,那么客户端推断 ACK 已经被成功接收,则结束 TCP 连接;

六、流量控制

接收端处理数据的速度是有限的,如果发送方发送数据的速度过快,导致接收端的缓冲区满,而发送方继续发送,就会造成丢包,继而引起丢包重传等一系列连锁反应;

  • TCP 支持根据接收端的处理能力,来决定发送端的发送速度,这个机制叫做流量控制
  • TCP 报文段首部中有一个 16 位窗口,当接收端接收到发送方的数据后,在应答报文 ACK 中就将自身缓冲区的剩余大小,放入 16 窗口中。这个大小随数据传输情况而变,窗口越大,网络吞吐量越高,而一旦接收方发现自身的缓冲区快满了,就将窗口设置为更小的值通知发送方
  • 如果缓冲区满,就将窗口置为 0,发送方收到后就不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端

在这里插入图片描述
注意:窗口大小不受 16 位窗口大小限制,在 TCP 首部 40 字节选项中还包含一个窗口扩大因子 M,实际窗口大小是窗口字段的值左移 M 位;

在这里插入图片描述

在这里插入图片描述

七、拥塞控制

在这里插入图片描述

  • 流量控制解决了两台主机之间因传送速率而可能引起的丢包问题,保证了 TCP 数据传送的可靠性。然而如果网络非常拥堵,此时再发送数据就会加重网络负担,发送的数据段很可能超过了最大生存时间也没有到达接收方,就会产生丢包问题
  • TCP 引入慢启动机制,先发出少量数据,就像探路一样,先摸清当前的网络拥堵状态后,再决定按照多大的速度传送数据

拥塞窗口(cwnd)

  • 发送开始时定义拥塞窗口大小为 1;每次收到一个 ACK 应答,拥塞窗口加 1;而在每次发送数据时,发送窗口取拥塞窗口与发送端接收窗口最小者;
  • 拥塞窗口的维护原则:只要网络没有出现拥塞,拥塞窗口就再增大一些,但只要网络出现拥塞,拥塞窗口就会减少一些
  • 判断出现网络拥塞的依据:没有按时收到应当到达的确认报文(即发生超时重传

7.1、拥塞控制算法1:慢启动

维护一个慢开始门限 ssthresh 状态变量

  • 当 cwnd < ssthresh 时,使用慢开始算法
  • 当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法
  • 当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞避免算法

在启动初期以指数增长方式增长;设置一个慢启动的阈值,当以指数增长达到阈值时就停止指数增长,按照线性增长方式增加

7.2、拥塞控制算法2:拥塞避免

线性增长达到网络拥塞时(重传计数器超时)立即"乘法减小",拥塞窗口置回 1,进行新一轮的“慢启动”,同时新一轮的阈值变为原来的一半

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

7.3、拥塞控制算法3:快重传

在这里插入图片描述

7.4、拥塞控制算法4:快恢复

在这里插入图片描述

总结:

在这里插入图片描述


转载请标明出处,原文地址:https://blog.csdn.net/weixin_41835916 如果觉得本文对您有帮助,请点击支持一下,您的支持是我写作最大的动力,谢谢。
这里写图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北极星小王子

你的鼓励是我创作的最大动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值