【网络原理】TCP协议的相关机制(确认应答、超时重传)

2024-04-27 07:28

本文主要是介绍【网络原理】TCP协议的相关机制(确认应答、超时重传),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

系列文章目录

【网络通信基础】网络中的常见基本概念

【网络编程】Java网络编程中的基本概念及实现UDP、TCP客户端服务器程序(万字博文)

【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制(CRC算法、MD5算法)


文章目录

一、TCP协议的特性

TCP协议段格式

二、TCP协议的相关机制

1. 确认应答

2. 超时重传


一、TCP协议的特性

前面介绍过,TCP(Transmission Control Protocol,传输控制协议)是互联网中的一种有连接的、可靠的、面向字节流、全双工的传输层协议。它是TCP/IP协议族中的一个重要组成部分,用于在网络中可靠地传输数据。

TCP协议是以后工作中最常用到的传输层协议,也是面试最常考的协议,非常非常重要!

TCP协议段格式

TCP协议段(Segment)的格式如下图所示:

301444ec6e994608a0c82b21befa385d.png

由于TCP报头最大长度是60字节,而除选项外,其余报头字段加起来一共是20字节,是固定的。这个选项部分是可选的,因此选项部分为0 ~ 40个字节。

可以看到,TCP报头是比UDP报头复杂很多的。

TCP的内部机制也是很多的,需要了解TCP的这些机制,才能理解报头的含义。

二、TCP协议的相关机制

1. 确认应答

确认应答,可以说是TCP协议用来确保可靠性,最核心的机制。

假设你给“朋友“发了一条短信:

  • 当你发出短信后,你可能会期待朋友收到并回复你,以确认他已经收到了你的短信。如果你很长时间没有收到回复,你可能会怀疑短信没有发送成功(前提是朋友一定会回复你的短信)。
  • 如果朋友此时收到短信,并给你回复了,你看到回复,就可以知道之前发的短信是一定被正确收到了。在这里,朋友的回复就相当于确认应答

再假设,你给“朋友”发了两条短信: 

  • 由于网络传输过程中,经常会出现"后发先至"的情况。导致朋友回复短信的时候,回复短信的顺序和发送短信的顺序是不一致的,就容易发生“答非所问”的情况。

出现后发先至的情况是,由于网络拥塞或者数据包的路由选择引起的。这种情况可能发生在网络中存在多条路径,但是不同路径的延迟不同,导致一些数据包比其他数据包先到达目的地。

为了解决上述问题,TCP就入了序号和确认序号。通过对数据进行编号,应答报文里就告诉发送方,我这次应答的是哪个数据。

而TCP是面向字节流的,以字节为单位进行传输的。因此,TCP的序号和确认序号都是以字节来进行编号的。如下图:

  • 假设载荷有1000个字节,一个载荷就有1000个序号,由于序号是连续的,只需要在报头保存第一个字节的序号即可,后续字节的序号都是很容易计算得到的。
  • 应答报文中的确认序号,是按照发送过去的最后一个字节的序号+1,来进行设定的。

如上图,当发送第一条数据:

  • 数据报头的序号就是1,主机B收到了1 - 1000这些字节数据后,反馈一个应答报文,应答报文中的确认序号的值就是1001

此处1001的含义,有两种理解方式:

  1. 小于1001的数据,都已经收到了.
  2. 发送方接下来要给我发的数据是从1001开始的.

确认应答中,通过应答报文来反馈给发送方,当前的数据正确收到了。应答报文,也叫 ACK 报文,ACK => acknowledge 单词的缩写。

ACK报文也就是TCP报头中六位标志位的第二位。平时该位是0,如果当前报文时应答报文,则这一位就是1.

2. 超时重传

超时重传,可以被视为是确认应答机制的一种补充。

发送方发送数据,如果一切顺利,接收方通过 ACK 标志位就可以告诉发送方,当前数据是否成功收到了。

但是,在某些情况下,1. 如果接收方没有及时发送 ACK 确认,2. 或者数据包丢失(丢包),发送方就无法得知数据是否已成功传输(至于为什么会丢包,这里就不细说了)。这时就需要超时重传来补充确认应答机制。

简单来说:

  • 发送方发了个数据之后,要等待接收来自接收方的ACK确认。
  • 如果等了很久,ACK还没等到,此时发送方就认为数据的传输出现丢包了。
  • 当认为丢包之后,就会把刚才的数据包再传输一次(重传)。
  • 而这个等待的过程有一个时间阈值(定时器),超过等待时间,就是(超时)。

上面的过程,我们认为没收到ACK就是丢包。实际上,超时重传的触发可能不仅是由于数据包丢失导致的,还可能是由于ACK确认丢失导致的。

不论是数据包丢了,还是ACK丢了,从发送方的角度来看,是区分不了的,都是认为ACK没收到。如下图,对比一下这两种情况:

对于ACK丢失而导致的主机B收到很多重复数据这种情况,这里涉及到的内容也很多:

  • TCP socket 在内核中存在接收缓冲区,对于发送方发来的数据,是要先放到接收缓冲区中的。而应用程序调用读方法读数据的操作,其实是读取接收缓冲区的数据。
  • 当数据到达接收缓冲区的时候,接收方首先会判定当前缓冲区是否已经有,或者有过这个数据了。
  • 如果这个数据已经存在或者存在过,TCP就会直接把重复发来的数据丢弃(去重),这样就能确保应用程序,调用读方法的时候,不会出现重复数据了。

※ 接收缓冲区,除了能进行去重之外,还能够进行排序,对收到的数据按照序号进行排序,确保上层应用程序读到的数据和发送的数据顺序是一致的

接收方如何判定这个数据是否是"重复数据"?

核心判定依据:前面谈到的数据的序号。

  1. 数据还在接收缓冲区,还没被读走。此时,拿着新收到的数据的序号,和缓冲区中所有的数据的序号对一下,看看有没有一样的。有一样的就是重复了,就把这个数据丢弃了。
  2. 数据已经不在接收缓冲区,被应用程序读走了。此时,新来的数据序号是无法从接收缓冲区查到的。但是!应用程序读取数据的时候,是按照序号的先后顺序进行读取的。例如,1-1000,1001-2000,2001-3000。一定是先读序号小的,后读序号大的数据。此时, socket api中就可以记录上次读的最后一个字节的序号是多少。比如,上次读到的最后一个字节的序号是3000,新收到的数据的序号是1001,而这个1001一定是之前已经读过了。这个时候同样可以把这个新的数据包判定为“重复数据”直接丢弃了。

上述这些机制,都是TCP内置的,我们在使用TCP的api的时候,不需要考虑这些。但是学习这些能够让我们了解TCP内部做了哪些事情,从而写出更正确的代码。

TCP为了保证较高性能通信,此处的超时重传也不是无限重传,重传过程也是有一定的策略的。

  1. 重传次数是有上限的。如果累计到一定次数,还没有收到ACK,TCP认为网络或者对端主机出现异常,强制关闭连接。
  2. 重传的超时时间也不是固定不变的。随着重传次数的增加,这个超时时间会动态增长。

使用上述策略1的原因:

  • 假设,一次网络通信过程中,丢包的概率是10%(已经是一个很大的数字了),包顺利到达的概率是90%
  • 如果此时重传了一次,两次都丢包的概率只有10% * 10%,即1%;而两次至少有一次传输成功的概率,就是99%
  • 随着重传次数的增加,包能到达接收端的概率也会大大增加,这个概率就非常高了。

如果继续重传,在成功概率这么高的情况下,还是出现丢包的情况,说明当前网络已经出现非常严重的故障了,再重传也意义不大了。因此,就会关闭连接。

使用上述策略2的原因:

结合策略1的分析,数据经过了重传之后还是丢包,大概率是网络出现严重问题了,在没达到重传次数上限之前,重传还是要重传的,但是可以省点力气,少传几次(降低重传频率)。

这篇关于【网络原理】TCP协议的相关机制(确认应答、超时重传)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/939862

相关文章

Redis的Zset类型及相关命令详细讲解

《Redis的Zset类型及相关命令详细讲解》:本文主要介绍Redis的Zset类型及相关命令的相关资料,有序集合Zset是一种Redis数据结构,它类似于集合Set,但每个元素都有一个关联的分数... 目录Zset简介ZADDZCARDZCOUNTZRANGEZREVRANGEZRANGEBYSCOREZ

Java CompletableFuture如何实现超时功能

《JavaCompletableFuture如何实现超时功能》:本文主要介绍实现超时功能的基本思路以及CompletableFuture(之后简称CF)是如何通过代码实现超时功能的,需要的... 目录基本思路CompletableFuture 的实现1. 基本实现流程2. 静态条件分析3. 内存泄露 bug

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

关于Maven生命周期相关命令演示

《关于Maven生命周期相关命令演示》Maven的生命周期分为Clean、Default和Site三个主要阶段,每个阶段包含多个关键步骤,如清理、编译、测试、打包等,通过执行相应的Maven命令,可以... 目录1. Maven 生命周期概述1.1 Clean Lifecycle1.2 Default Li

numpy求解线性代数相关问题

《numpy求解线性代数相关问题》本文主要介绍了numpy求解线性代数相关问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 在numpy中有numpy.array类型和numpy.mat类型,前者是数组类型,后者是矩阵类型。数组

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取