概述
TCP 通过如下几种方式保证消息的可靠性:
- 连接管理
- 序列号和确认号
- 校验和
- 数据分块
- ARQ 协议
- 超时重传
- 流量控制
- 拥塞控制
连接管理
三次握手,四次挥手。简单来说通过 TCP 定时器以及挥手、握手保证发送方、接收方都确认自身能正常收发消息
关于这块前面博客有详细介绍,这里不再赘述
序列号和确认号
TCP 对每个字节的数据都进行了编号,这个编号也叫序列号。序列号包含以下功能:
- 保证数据有序
- 去重
- 保证可靠性(接收方总能发现少了哪一段数据)
确认号只有在 ACK 报文中才有效,进行确认时,确认号 ack 表示这个值之前的数据已经按序到达。接收方收到 ACK 报文后,就可以接着发送下一段数据,如果没有收到就启动超时重传机制
报文是指应用层的数据单元、数据段是说传输层的数据单元,在本篇两者暂时不区分
校验和
发送方在发送数据前将整个数据段分为多个 16 位的段,对每个段进行反码相加运算,将计算结果保存在校验和字段中。接收方收到数据后用同样的方法进行计算,最终校验字段全为 1 说明数据正确、否则数据异常
校验和的目的在于检验数据在传输过程中是否发生变化,如果校验和检验异常,TCP 将丢弃这个数据段并不返回 ACK
数据分块
TCP 每次将应用数据划分为多个数据块分批发送,存在以下几种情况就可能造成粘包、拆包问题
- 发送方写入的数据大于缓冲区大小会造成拆包
- 发送方写入的数据小于缓冲区大小,当发送方收集了多个较小的分组,会一起发送给接收方,造成粘包
- TCP 报文数据大于 MSS(最大报文长度)时发生拆包
- 发送方发送速度过快,接收方处理不及,在缓存区发送粘包
对于粘包、拆包问题,一般通过以下几种方式处理:
- 消息头部记录消息长度,接收方每次读取指定长度数据
- 固定消息长度,接收方根据该长度每次读取一条完整的数据,消息不够长时,补空位(浪费网络资源)
- 设置消息边界,接收方根据消息边界分割不同消息
并不是只要数据包黏在一起就需要处理粘包,当接收方同时收到多个分组,并且分组间无关系时才需要处理粘包;如果多个分组属于同一数据不同部分时,无需处理粘包问题
ARQ 协议
ARQ 协议规定发送方每次放松完一组数据后,直到收到接收方的确认信号才继续发送下一组数据
超时重传
发送方发完数据并在指定时间内没有收到 ACK 报文时重新发送数据。这里包含两种场景:
- 发送方数据未到达接收方
- 接收方返回 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,然后再次重复慢启动-》拥塞避免
快重传:接收方一旦收到失序的报文立即发送确认报文,不启用延时确认定时器
举个例子:发送方发送 Msg1 ~ Msg4 这 4 个报文,接收方收到 Msg1、Msg3、Msg4。此时由于接收方收到失序报文,根据快重传,接收方会立即返回发送方 Msg1 的 ACK 报文,并且发送三次,发送方根据收到的 ACK 报文重发未确定的数据段
快恢复:快恢复算法配合快重传算法一起使用:
当发送方连续收到 3 个相同 ACK 报文,说明发送网络拥塞,慢开始上限 ssthresh 值减半。此时发送方认为网络可能没有拥塞(报文丢失),和慢开始不同,他将 cwnd 值设置为 ssthresh 的一半,之后根据拥塞避免策略执行
最后
以上就是现实大山为你收集整理的TCP 如何保证可靠性的全部内容,希望文章能够帮你解决TCP 如何保证可靠性所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复