TCP协议的可靠性详解

devtools/2024/9/25 9:37:00/

        由于网络部分内容相对于来说比较多,本文只针对TCP协议来进行讲解,后面UDP/Http/Https的讲解有可能会单独出一篇文章。 udp协议相对来来说会比tcp简单不少,同时面试频率tcp也会高上不少。
        同时本博客也仅仅只是做出部分讲解,并不适合系统全面的学习,要想真系统且全面学习的话,本人推荐去百度找------RFC标准文档 (百度RFC tcp)应该就可以出来

TCP协议特点

TCP协议的格式,下面的内容会对上述部分内容做详细讲解。

1. 确认应答

       tcp的可靠传输机制主要是靠确认应答机制来实现的。确认应答就是每一次客户端向服务器发送请求之后,服务器都会返回一个请求到客户端,表示我收到了你发送过来的请求。

就比如,当我去找我女神的时候,我在手机上向我女神发送了一个消息,说我想和她出来一起吃饭,然后女神通过手机回复我了一条短信就说,可以哇。就表示了一次通信,我像女神发的消息就表示了一条请求,女神的这次回复就是确认应答机制。 这样我就知道我女神是否愿意出来吃饭了。

但是其实确认应答机制只看上述条件的话,其实是存在一个弊端的。 还是同样的场景,我向我女神发送了2条消息

  1. 女神,节日快乐给你发个520红包哇
  2. 女神,我喜欢你,做我对象好不好哇

此时我按上述顺序给我女神发了2条信息,我女神收到后针对上述2条信息给了我2条回复

  1. 可以啊 
  2. 滚啊

不能看出此时的女神对我的红包还是有意思的,但是对我意思可能不怎么大,但是在网络通信传输的过程中,要是出现了一点意外导致2那条消息先传给我了,1那条消息后面才传过来,此时对我来说,我就很可能会理解为 我女神不接受我的红包,但是接收了我的表白(不愧是我看上的女神) 。但女神的本意并不是如此,所以传输和接收的顺序也尤为重要。 每条信息在通信过程中,所走的路线和节点并不相同,同时速度也和各个节点的硬件和其他很多物理因素所影响,所以后发先到,其实也是很常见的事情,但是后发先到会引起比如上述这样很麻烦的事。

tcp在解决上述问题中采用了一种很简单的方式,通过给每一条数据进行编号。就是在上述我向女神发送消息的前面加上编号,第一条就编号一,第二条就编号二,同时在我返回的数据中也带上编号,第一条就是编号一 第二条就是编号二,这样我就知道原来我女神是拒绝了我这样的现实了。但是在TCP中编号其实是蛮复杂的。

因为tcp协议是面向字节流的,不是一条一条传输的。所以在tcp实际编号的过程中,是按照字节来进行的编号。

在上图中 第一条数据是1000的大小,第二条也是1000的大小,但是在实际过程中,可以不用向上述那样发请求第一条可以使1-888 第二条889-1000也是可以的,这里的大小是自己定的。但是为了方面我们后面的内容就按照上述图片进行一个讲解。

上述图片中 在收到了第一条请求(1  -  1000)之后,返回了一个数据1001,这个1001表示的含义是表示1001编号以前的数据我都接收到了,下一次发送你应该从1001开始发送,并不是返回数据最后的下一位。
所以不难看出,tcp协议只需要在报头中将第一个数据的编号表示出来,然后结合报文长度,就可以知道当前tcp编号大小了

里面的32位序号所对应的就是报文第一个字节的编号。(超出了之后就重头开始计算不怎么影响,因为4个字节32比特位可以表示4gb的数据 所以一般不会超,超了也没事)

里面的32位确认序号就是专门给应答报文使用的。

所以为了区分这是普通报文还是应答报文,在tcp协议中还引入了标志位,要是其中ACK(第二位)(acknowledge)为0表示当前为无效位,表示当前是一个普通报文,1表示有效,表示当前是一个应答报文。同时应答报文也会携带一个32位序号,但这个32位序号和正常报文的序号是没有关系的,序号是针对于某一个主机发送的内容进行编号。

所以在上述内容中铺垫了这么多,核心一点就是 确保可靠性的核心机制是确认应答

2. 超时重传

但是上述情况都是一种很理想的情况,这里我们讨论一种常见的情况,就是存在丢包的情况。在一般我们信息和数据传输的过程中,数据是由一个一个的交互机/路由器进行传输的,要是数据量少,机器的负载可能相对较小,但是对于数据量大一点的场景,机器压力一点过大,机器开始摆烂了,就可能会造成数据丢失的情况,也就是丢包。

但是要是真的出现丢包了,比如我给我女神发送了一条信息(理想状态的女神就是每一条信息都会回复的那种),但是我女神没有收到,就没有给我回复,难到我就放弃了嘛。肯定不是哇。这时候我会先等一等,确认我女神是没有收到消息之后,我会尝试在发送一次。直到我女神收到了我发送的信息。

上述过程也就是TCP中的超时重传机制,就是我设置一个等待时间,要是时间到了我还没有收到确认应答,我就重新发送报文。

所以超时重传的前提是丢包,这里不难理解吧。此时,我们又需要进行分类讨论了

  • 发送消息的包丢失
  • 确认应答的包丢失

丢包的过程中,上述2中包都存在丢失的可能。 但其实站在发送方的视角来看,他并不知道是那种丢包的情况呀。 所以既然反正都区分不了,那我直接摆了,都进行超时重传。但是,都进行超时重传的话,站在第一种情况来讲,没啥大问题,但是要是站在下面那种情况来讲的话,就有问题了,要是确认应答的报文丢失,但我还去重传一个,那服务端此时不就收到2份数据了嘛。要是在平时的充值过程中,我充了6元钱他直接给我扣了12,就会引发我很大的经济危机。

所以在客户端接收数据的过程中其实是会进行一个去重操作的。

这里tcp协议可以直接使用序列号进行一个去重,tcp会在内核中给每一个socket对象安排一个内存空间,就等于一个队列类似的数据结构,也被称为“ 接收缓冲区 ”收到的数据都会放进去,并且排好顺序,所以可以很容易的判断当前数据是否重复了。同时使用一个数据就在队列中进行删除。

有人可能会想到,要是这个数据已经被删除了,但是以前超时重传发送的一条数据又来了,这样会重复发起请求吗。其实在上述确认序号中已经解决这个问题了,因为队列中的数据是按顺序排列的,同时要是新来的请求序号小于已经返回的序号,则可以直接判断为已经接收过的数据了。

同时,队列中的数据有序排列,其实也很好的解决了上述的后发先到问题。在数据到达之后会进行一个重新排序的过程。

我们假设我们服务器丢包率为2%(已经很高很高了)在超时重传的过程中第一次丢包的过程是2%要是触发了超时重传第二次传递还是丢包的概率为2%*2%了 已经很小了,所以随着次数的增加,传输成功率是会增大的,但是也存在因为一定的原因,一直丢包,此时,要是一直丢包,每一次超时重传的等待时间会越来越长,直到一定次数之后,就放弃重传了。此时就会尝试重置TCP的连接。此时客户端就会发起一个复位报文 

也就是上图的RST会表示为1,也就是复位报文,就表示希望重置连接。但是丢包率这么严重的情况下,一般复位报文也不会收到回应了,此时客户端就会删除对端信息,放弃连接。

所以超时重传也是对TCP机制可靠性的一个重要补充。 


http://www.ppmy.cn/devtools/23670.html

相关文章

【Spring AI】02. AI 概念

文章目录 概述模型(Models)提示词(Prompts)提示词模板(Prompt Templates)嵌入向量(Embeddings)令牌(Tokens)输出解析(Output Parsing)…

.NET WinForm开放中的 窗体的 Designer.cs作用

一般窗体窗体 在资源管理器中会呈现 xxx.cs xxx.Designer.cs xxx.resx 》》》 .resx 是存放资源文件的,没啥好说的 xxx.cs 和 xxx.Designer.cs 都是partial类,而他们类名是一样的,所以在编译会生成一个文件。 xxx.Designer.cs 代码中有两…

stm32程序死机怎么回事

STM32程序死机可能由多种因素导致,以下是一些常见的原因及解决方法: 供电问题:供电电压不稳定或不在正常工作电压范围内,电源纹波大小过大,都可能导致STM32死机。此外,供电电源中的干扰杂讯也可能对单片机…

Kafka(十二)Streams

目录 Streams1 什么式是流式处理2 流式处理的相关概念2.1 拓扑2.2 时间2.2.1 输入时间2.2.2 输出时间 2.3 状态2.4 流和表2.5 时间窗口2.5.1 测试时间窗口 2.6 处理保证 3 流式处理设计模式3.1 单事件处理3.2 使用本地状态3.3 多阶段处理和重分区3.4 使用外部查找:流…

简单实现日期计算器

目录&#xff1a; Date.h实现函数声明Date.c实现函数功能 构造函数六个比较函数日期 天数日期 - 天数日期 - 日期操作符操作符--获取每月的天数 &#x1f698;正片开始 Date.h头文件中实现函数声明 #pragma once #include<iostream> using namespace std; class Dat…

【spark】spark使用sql读取elasticsearch es索引,使用keystore配置用户密码

参考文章 spark配置elasticsearch属性汇总(基于es7) es-offical-doc Spark多方案读取Es性能比较 Spark读写ES数据时遇到的问题总结 es 查询多个索引的文档 spark table中使用明文密码 set es.index.auto.createtrue drop table if exists default.test_es01; create table d…

模块三:二分——153.寻找旋转排序数组中的最小值

文章目录 题目描述算法原理解法一&#xff1a;暴力查找解法二&#xff1a;二分查找疑问 代码实现解法一&#xff1a;暴力查找解法二&#xff1a;CJava 题目描述 题目链接&#xff1a;153.寻找旋转排序数组中的最小值 根据题目的要求时间复杂度为O(log N)可知需要使用二分查找…

C语言仿写strlen函数以及编程常见的错误、以及,打印菱形、空瓶换水、水仙花数、反转字符串等小案例

文章目录 前言一、仿写strlen函数二、编程常见的错误1. 编译型错误(语法错误)2. 链接型错误(链接期间)3. 运行时错误(最难找) 三、小案例1. 打印菱形2. 两个空瓶换一瓶水的实现3. 打印 aaaaaaaaaaaaaaa......的和4. 打印0-100000"水仙花数"5. 反转字符串 总结 前言 …