【网络原理】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

相关文章

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

Java Spring 中 @PostConstruct 注解使用原理及常见场景

《JavaSpring中@PostConstruct注解使用原理及常见场景》在JavaSpring中,@PostConstruct注解是一个非常实用的功能,它允许开发者在Spring容器完全初... 目录一、@PostConstruct 注解概述二、@PostConstruct 注解的基本使用2.1 基本代

Golang HashMap实现原理解析

《GolangHashMap实现原理解析》HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持高效的插入、查找和删除操作,:本文主要介绍GolangH... 目录HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持

Python的time模块一些常用功能(各种与时间相关的函数)

《Python的time模块一些常用功能(各种与时间相关的函数)》Python的time模块提供了各种与时间相关的函数,包括获取当前时间、处理时间间隔、执行时间测量等,:本文主要介绍Python的... 目录1. 获取当前时间2. 时间格式化3. 延时执行4. 时间戳运算5. 计算代码执行时间6. 转换为指

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Python处理函数调用超时的四种方法

《Python处理函数调用超时的四种方法》在实际开发过程中,我们可能会遇到一些场景,需要对函数的执行时间进行限制,例如,当一个函数执行时间过长时,可能会导致程序卡顿、资源占用过高,因此,在某些情况下,... 目录前言func-timeout1. 安装 func-timeout2. 基本用法自定义进程subp

Spring Boot循环依赖原理、解决方案与最佳实践(全解析)

《SpringBoot循环依赖原理、解决方案与最佳实践(全解析)》循环依赖指两个或多个Bean相互直接或间接引用,形成闭环依赖关系,:本文主要介绍SpringBoot循环依赖原理、解决方案与最... 目录一、循环依赖的本质与危害1.1 什么是循环依赖?1.2 核心危害二、Spring的三级缓存机制2.1 三

C#中async await异步关键字用法和异步的底层原理全解析

《C#中asyncawait异步关键字用法和异步的底层原理全解析》:本文主要介绍C#中asyncawait异步关键字用法和异步的底层原理全解析,本文给大家介绍的非常详细,对大家的学习或工作具有一... 目录C#异步编程一、异步编程基础二、异步方法的工作原理三、代码示例四、编译后的底层实现五、总结C#异步编程

SpringRetry重试机制之@Retryable注解与重试策略详解

《SpringRetry重试机制之@Retryable注解与重试策略详解》本文将详细介绍SpringRetry的重试机制,特别是@Retryable注解的使用及各种重试策略的配置,帮助开发者构建更加健... 目录引言一、SpringRetry基础知识二、启用SpringRetry三、@Retryable注解