相比于 UDP 来说,TCP 的主要特性是三个:有连接、可靠、面向数据流。所谓的“有连接”指的是 TCP 中的连接管理机制,也就是著名的三次握手和四次挥手,就像打电话一样,想要正常的交流,必须先和对方建立起连接,这就是所谓的“有连接”,而面向数据流的机制咱们以后再讲,我们今天要讨论的主题是:TCP 是如何保证可靠性的?

TCP 之所以能保证可靠性,主要是通过以下 6 个手段:

  1. 校验和
  2. 确认应答
  3. 超时重传
  4. 流量控制
  5. 拥塞控制
  6. 丢弃重复数据

接下来,我们详细来看这几种手段的具体实现。

1.校验和

TCP 协议的数据格式如下图所示:



(图片来源:许许如生xxrs)

从上图可以看出“校验和”是保存在 TCP 首部中的一个数据,TCP 的发送端和接收端会采用相同的算法,根据发送的数据计算出一个 16 位的校验和,并且校验和会连同数据一起发送给接收端。

接收端在得到数据之后,会根据接收的数据生成一个新的校验和,然后用新的校验和与传递过来的校验和做对比,如果校验和相同,那么说明数据在传递过程中没有发生任何改变,是一个有效的数据,反之则为无效数据,舍弃即可。

校验和基本算法

TCP/UDP/IP 等协议的校验和算法都是相同的,采用的都是将数据流视为 16 位整数流进行重复叠加计算。为了计算检验和,首先把检验和字段置为 0,然后,对有效数据范围内中每个 16 位进行二进制反码求和,结果存在检验和字段中,如果数据长度为奇数则补一字节 0。当收到数据后,同样对有效数据范围中每个 16 位数进行二进制反码的求和。由于接收方在计算过程中包含了发送方存在首部中的检验和,因此,如果首部在传输过程中没有发生任何差错,那么接收方计算的结果应该为全 0 或全 1(具体看实现了,本质一样) 。如果结果不是全 0 或全 1,那么表示数据错误。

2.确认应答

确认应答机制是保证消息传递可靠性的关键手段,也是几乎所有消息中间件(MQ)中,最常用的技术之一,比如主流的消息中间件 RabbitMQ、Kafka、RocketMQ 中都有确认应答机制,也就是我们常说的 ACK(ACKnowledge Character,确认字符)。

确认应答机制是 TCP 中,保证消息可靠性的核心机制。怎么才能确认你发的消息对方一定收到了呢?最有效的手段无疑是对方告诉你,它已经收到了,这就是确认应答。

确认应答的流程如下图所示:

3.超时重传

消息在确认应答的过程中可能会出现两个问题:第一,消息在发送的时候丢失了,第二,消息在确认应答时丢失了,如下图所示:



显然,即使有了确认应答机制也保证不了消息不丢失,那怎么办呢?

消息丢了没关系,发送端在确认了消息丢失之后,再补偿一个同样的消息给接收端不就解决了?这就是超时重传机制。

巧妙的超时重传机制

TCP 的超时重传机制在设计上也非常巧妙,它为了保证消息在任何环境中,都能高效的通讯,所以 TCP 采用的是“动态时间”的超时重传机制。

比如第一次如果消息丢了,那么发送端会在 500ms 之后再发送一个消息,如果发送的第二个消息也丢了,那么发送端会在 1000ms 之后再发送一个消息,如果第三个消息也丢了,那么它会在 2000ms 之后再发送一个消息,如果累计了一定的次数,消息还没有成功的发送,那么 TCP 会认为对方主机存在异常,会强制关闭连接,这就是 TCP 超时重传的主要执行流程。

4.流量控制

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

因此 TCP 会根据接收端的处理情况,动态调整发送数据的大小,这个机制就叫流量控制(Flow Control)。

5.拥塞控制

拥塞控制指的是 TCP 会根据当前网络的情况,动态的控制发送数据的多少,以适合的速度来传递数据。

想象一下,如果 TCP 在不清楚网络情况的环境下,贸然的发送大量的数据给接收端,这样就会导致更多的丢包及超时重传,从而引起一系列的连锁反应,导致数据传递变慢。

TCP 采取的是“慢启动”机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据,这就是拥塞控制机制。

如果传递的数据多了,出现了大量的丢包,那么 TCP 会将发送的数据量调小,然后再尝试慢慢的增加发送的数据量,通过这种动态发送数据包的形式,来实现适合当前网速的数据传递,这就是 TCP 拥塞控制的具体实现。

6.丢弃重复数据

通过前面的知识我们知道,在确认应答时,由于确认应答消息的丢失,那么接收方可能会收到发送方的重复数据,如下图所示:



而此时对于业务方来说,只需要一个数据就可以了,所以 TCP 还有一个机制,丢弃重复数据的机制,这样就能保证业务方接收到的数据是正确的了。

TCP 会给每一个发送的包上加上一个编号,如果接收到了编号相同的数据包,那么就说明接收端得到了重复的包,丢弃即可。

总结

TCP 保证可靠性的主要手段有 6 个:校验和、确认应答、超时重传、流量控制、拥塞控制、丢弃重复数据。其中流量控制和拥塞控制很容易搞混,我们要清楚的知道,流量控制是针对接收端接收能力的控制机制,而拥塞控制是针对当前网络的控制机制,所以千万不要搞混了。

参考 & 鸣谢

blog.csdn.net/namelcx/article/details/6866720

是非审之于己,毁誉听之于人,得失安之于数。

公众号:Java面试真题解析

面试合集:https://gitee.com/mydb/interview

面试突击69:TCP 可靠吗?为什么?的更多相关文章

  1. 计算机网络传输层之TCP可靠传输

    文章转自:https://blog.csdn.net/weixin_43914604/article/details/105524592 学习课程:<2019王道考研计算机网络> 学习目的 ...

  2. 计算机网络(9)-----TCP可靠传输的实现

    TCP可靠传输的实现 以字节为单位的滑动窗口 滑动窗口的滑动是以字节为单位的,发送方A和接收方B在TCP三次握手的前两次握手时协商好了发送窗口和接受窗口的大小,发送方A根据B发送来的确认连接报文中标明 ...

  3. TCP可靠传输的实现

    TCP可靠传输的实现 1.概述      为方便描述可靠传输原理,假定数据传输只在一个方向上进行,即A发送数据,B给出确认 2.以字节为单位的滑动窗口      TCP的滑动窗口是以字节为单位的.为了 ...

  4. TCP可靠数据传输

    TCP可靠数据传输   在TCP在IP不可靠的尽力而为的服务之上,创建了一条可靠数据传输服务(reliable data transfer service).TCP提供的可靠数据传输的方法涉及到可靠数 ...

  5. TCP可靠传输及流量控制实现原理

    一.为什么TCP是可靠传输? 1. 停止等待协议 通过确认与超时重传机制实现可靠传输 在发送完一个分组后,必须暂时保留已发送的分组的副本. 分组和确认分组都必须进行编号. 超时计时器的重传时间应当比数 ...

  6. 计算机网络概述 传输层 TCP可靠传输的实现

    TCP可靠传输的实现 TCP的可靠性表现在:它向应用层提供的数据是 无差错的.有序的.无丢失的,简单的说就是:TCP最终递交给应用层的数据和发送者发送的数据是一模一样的. TCP采用了流量控制.拥塞控 ...

  7. 运输层6——TCP可靠传输的实现

    目录 1. 以字节为单位的滑动窗口 2. 超时重传时间的选择 写在前面:本文章是针对<计算机网络第七版>的学习笔记 运输层1--运输层协议概述 运输层2--用户数据报协议UDP 运输层3- ...

  8. 运输层4——TCP可靠运输的工作原理

    目录 1. 停止等待协议 写在前面:本文章是针对<计算机网络第七版>的学习笔记 运输层1--运输层协议概述 运输层2--用户数据报协议UDP 运输层3--传输控制协议TCP概述 运输层4- ...

  9. 《【面试突击】— Redis篇》--Redis都有哪些数据类型?分别在哪些场景下使用比较合适?

    能坚持别人不能坚持的,才能拥有别人不能拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! <[面试突击]— Redis篇>--Redis都有哪些数据类型?分别在哪些场景下使用 ...

随机推荐

  1. OSError: no library called "cairo-2" was found

    环境 Windows 11 python 3.8.13 (anaconda->envs) PyCharm 场景 我使用了GitHub上的第三方组件进行GUI开发,https://github.c ...

  2. Spring 源码(14)Spring Bean 的创建过程(6)对象的提前暴露

    知识回顾 解析完Bean信息的合并,可以知道Spring在实例化Bean之后,属性填充前,对Bean进行了Bean的合并操作,这里的操作主要做了对Bean对象标记了@Autowired.@Value. ...

  3. Git技法:.gitignore、移除暂存与撤销修改

    1. .gitignore常见项目添加 1.1 .gitignore模板 .gitignore针对每个语言都有对应的模板,在GitHub创建项目时就可以选择(你可以在GitHub提供的.gitigno ...

  4. ESP8266远程控制电子门

    ESP8266远程控制电子门 最前面介绍: 这是一个使用ESP8266 联网控制继电器,实现手机远程控制电子门,打开关闭,开关一次的物联网联手小项目 附git地址:https://github.com ...

  5. Redis概述及基本数据结构

    SQL vs NoSQL 结构化 SQL 是结构化的,一旦定义了表结构,以后在维护数据的时候必须严格遵守定义的结构. NoSQL 是非结构化的,常见的形式有 Redis 的 Key-Value 存储形 ...

  6. LVGL库入门教程02-基本控件与交互

    LVGL 本质上是一个 GUI 库,它包含大量的控件(widget),即按钮.标签.滑块.菜单栏这种具有一定人机交互特征的组合图形.LVGL 在设计时,采用了一定面向对象编程的设计思路,有效降低了代码 ...

  7. .NET中如何在同步代码块中调用异步方法

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年7月2日. 在同步代码块中调用异步方法,方法有很多. 一.对于有返回值的Task 在同步代码块中直接访问 Task 的 Result ...

  8. 使用http://start.spring.io/构建maven微服务项目的几个坑及eclipse构建spring boot微服务项目

    一,使用http://start.spring.io/构建maven微服务项目 本来嘛,直接构建的项目导入时没有任何问题的导入就可以运行,可是最近构建好项目,然后导入,种种报错 1.导入之后POM报错 ...

  9. electron-vue 项目启动动态获取配置文件中的后端服务地址

    前言 最近的项目迭代中新增一个需求,需要在electron-vue 项目打包之后,启动exe 可执行程序的时候,动态获取配置文件中的 baseUrl 作为服务端的地址.electron 可以使用 no ...

  10. JS:in语法

    1.应用于判断对象中是否有某一个成员 var obj = { name: "lili", age:10, gender:"girl" } console.log ...