我是靠谱客的博主 现实大山,最近开发中收集的这篇文章主要介绍TCP 如何保证可靠性,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

TCP 通过如下几种方式保证消息的可靠性:

  • 连接管理
  • 序列号和确认号
  • 校验和
  • 数据分块
  • ARQ 协议
  • 超时重传
  • 流量控制
  • 拥塞控制

连接管理

三次握手,四次挥手。简单来说通过 TCP 定时器以及挥手、握手保证发送方、接收方都确认自身能正常收发消息

关于这块前面博客有详细介绍,这里不再赘述


序列号和确认号

TCP 对每个字节的数据都进行了编号,这个编号也叫序列号。序列号包含以下功能:

  1. 保证数据有序
  2. 去重
  3. 保证可靠性(接收方总能发现少了哪一段数据)

确认号只有在 ACK 报文中才有效,进行确认时,确认号 ack 表示这个值之前的数据已经按序到达。接收方收到 ACK 报文后,就可以接着发送下一段数据,如果没有收到就启动超时重传机制

报文是指应用层的数据单元、数据段是说传输层的数据单元,在本篇两者暂时不区分


校验和

发送方在发送数据前将整个数据段分为多个 16 位的段,对每个段进行反码相加运算,将计算结果保存在校验和字段中。接收方收到数据后用同样的方法进行计算,最终校验字段全为 1 说明数据正确、否则数据异常

校验和的目的在于检验数据在传输过程中是否发生变化,如果校验和检验异常,TCP 将丢弃这个数据段并不返回 ACK


数据分块

TCP 每次将应用数据划分为多个数据块分批发送,存在以下几种情况就可能造成粘包、拆包问题

  1. 发送方写入的数据大于缓冲区大小会造成拆包
  2. 发送方写入的数据小于缓冲区大小,当发送方收集了多个较小的分组,会一起发送给接收方,造成粘包
  3. TCP 报文数据大于 MSS(最大报文长度)时发生拆包
  4. 发送方发送速度过快,接收方处理不及,在缓存区发送粘包

对于粘包、拆包问题,一般通过以下几种方式处理:

  1. 消息头部记录消息长度,接收方每次读取指定长度数据
  2. 固定消息长度,接收方根据该长度每次读取一条完整的数据,消息不够长时,补空位(浪费网络资源)
  3. 设置消息边界,接收方根据消息边界分割不同消息

并不是只要数据包黏在一起就需要处理粘包,当接收方同时收到多个分组,并且分组间无关系时才需要处理粘包;如果多个分组属于同一数据不同部分时,无需处理粘包问题


ARQ 协议

ARQ 协议规定发送方每次放松完一组数据后,直到收到接收方的确认信号才继续发送下一组数据


超时重传

发送方发完数据并在指定时间内没有收到 ACK 报文时重新发送数据。这里包含两种场景:

  1. 发送方数据未到达接收方
  2. 接收方返回 ACK 报文未到达发送方

对于场景一来说,发送方超时重传即可、对于场景二,发送方由于未收到 ACK 报文还会重发数据,接收方通过序列号识别数据是否已收到,如果收到直接放弃数据,重发 ACK 报文

超时重传通过前面博客提到的重传定时器实现,这里重传的时间通过以下逻辑计算:RTO(超时重传时间)随着 TCP 网络状态的不同动态变化。在 linux 中,默认以 500 ms 为单位,第二次变为 2 * 500 ms,第三次变为 4 * 500ms,判断是否超时的时间随指数递增,直到达到最大超时时长,此时 TCP 认为连接无效,主动断开连接

一般情况下接收方收到数据后不会立即应答(延时应答定时器),它先等一会判断是否有需要返回的数据随着 ACK 报文一起返回,减少交互的次数,提高整体效率


流量控制

接收方处理数据的速度是有限的,发送方发送数据过快可能导致接收方缓冲区被占满,如果此时继续发送报文就可能造成丢包。为了解决丢包问题,TCP 根据接收方的处理能力控制发送方发送的数据大小,这种机制就叫流量控制

接收方每次收到数据后,会将自己剩余的缓冲区大小放入 TCP 报文的窗口属性返回,告诉发送方自己所能接收的数据大小,窗口值越大说明网络吞吐量越大,一旦接收方缓冲区已满就返回 0,告诉发送方暂时不要发送数据。直到缓冲区不满后返回发送方 ACK 报文告诉发送方可以发送的数据大小。为了防止该 ACK 报文丢失,TCP 引入坚持定时器,发送方根据坚持定时器定期发送窗口探测数据段,获取接收方窗口大小


拥塞控制

流量控制只能保证网络状况良好的情况下,发送方不会由于接收方缓冲区满而造成丢包。然而如果网络环境非常拥堵,再发送数据只会加剧网络负担,此时发送的报文很有可能丢失。拥塞控制主要防止过多的数据注入到网络中,避免出现网络负载过大的情况。具体实现有:慢启动、拥塞避免、快重传和快恢复。这里拥塞控制引入滑动窗口(cwnd)的概念

慢启动:为了防止一开始发送大量的数据导致网络拥塞,慢启动规定一开始发送少量数据,探探网络情况,即从小到大的增大发送窗口。开始时令 cwnd 为 1,如果收到 ACK 报文,cwnd 值每次变为原来的两倍,即呈指数倍递增。为了防止指数倍后面一次性增加太大,造成网络拥塞,慢启动设置慢启动上限 ssthresh

  • 当 cwnd < ssthresh 时,呈指数倍增长
  • 当 cwnd > ssthresh 时,停止指数增长,转而采用拥塞避免策略
  • 当 cwnd = ssthresh 时,两者均可

拥塞避免:每经过一个 RTT (从发送方发送到收到 ACK 一轮的时间), cwnd 值加 1,呈线性增长,防止继续慢启动造成网络拥塞

用网络拥塞发生时,ssthresh 变为发生拥塞时的窗口大小的一半,cwnd 重置为1,然后再次重复慢启动-》拥塞避免
TCP 拥塞控制
快重传:接收方一旦收到失序的报文立即发送确认报文,不启用延时确认定时器

举个例子:发送方发送 Msg1 ~ Msg4 这 4 个报文,接收方收到 Msg1、Msg3、Msg4。此时由于接收方收到失序报文,根据快重传,接收方会立即返回发送方 Msg1 的 ACK 报文,并且发送三次,发送方根据收到的 ACK 报文重发未确定的数据段

快恢复:快恢复算法配合快重传算法一起使用:

当发送方连续收到 3 个相同 ACK 报文,说明发送网络拥塞,慢开始上限 ssthresh 值减半。此时发送方认为网络可能没有拥塞(报文丢失),和慢开始不同,他将 cwnd 值设置为 ssthresh 的一半,之后根据拥塞避免策略执行

快恢复

最后

以上就是现实大山为你收集整理的TCP 如何保证可靠性的全部内容,希望文章能够帮你解决TCP 如何保证可靠性所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部