【计算机网络 | 第九篇】计网之传输层(三)—— TCP的拥塞控制与连接管理

⭐️在这个怀疑的年代,我们依然需要信仰。

个人主页:YYYing.

⭐️计算机网络系列专栏:

【从零开始的计算机网络】

⭐️ 其他专栏:【linux基础】【数据结构与算法】

系列上期内容:【计算机网络 | 第八篇】计网之传输层(二)

系列下期内容:暂无


目录

前言:

TCP拥塞控制

一、什么是拥塞控制?

[● 拥塞:在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏,这种情况就叫作拥塞(congestion)。](#● 拥塞:在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏,这种情况就叫作拥塞(congestion)。)

[● 拥塞控制是一个全局性的过程](#● 拥塞控制是一个全局性的过程)

[● 相比而言,流量控制是点对点通信的控制](#● 相比而言,流量控制是点对点通信的控制)

二、拥塞控制的常用算法

[● 方法概述](#● 方法概述)

[● 几个概念缩写](#● 几个概念缩写)

2.1、慢开始

2.2、拥塞避免

2.3、快重传

2.4、快恢复

TCP的建立连接和释放连接

一、三次握手

常见面试题:为什么必须是三次握手而不是二次握手

二、四次挥手

常见面试题:为什么此处TCP客户端(主动断开方)在TIME-WAIT状态必须等待2MSL的时间?

常见面试题:为什么关闭连接的需要四次挥手,而建立连接却只要三次握手呢?

结语

---⭐️封面自取⭐️---


前言:

在上一篇中我们学习了TCP的可靠传输与流量控制。那么这一篇也就是我们传输层的完结篇,将会带着大家学习TCP的拥塞控制与链接管理

(我们此专栏 目前会使用的软件是------Wireshark,如有需求,请移至官网进行下载,如有问题,请在评论区留言)

我们流量控制更多描述的是端到端之间的关系,那么是不是在网路中就只需要保证我们任意两个人之间的这种数据传输就可以了呢?那其实多少还差点意思 ,我们在网络传输中肯定是不能光在意我们两个人之间的通信,如果我们的整个链路如果上网的人多了,处理的网速本身就会下降所以我们的拥塞控制就来了,我们的拥塞控制描述地往往就是网络集合之间的相互关系。

那我们的连接管理更多关注的就是如何保证我们的本地连接资源进行正确的释放。

TCP拥塞控制

一、什么是拥塞控制?

● 拥塞:在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏 ,这种情况就叫作拥塞(congestion)

○ 计算机网络中的链路容量(带宽)、交换节点中的缓存和处理机等都是网络的资源

图中我们发现700那条路能跑600那条路也能跑但两路加一起700 + 600 = 1300 >= 1000,那就说明我们的宽带肯定会堵,从而丢失数据,甚至发生死锁那现在怎么搞呢,是不是我们可以适当让发送端减慢发送速度,当然我们后续肯定会讲到。

● 拥塞控制是一个全局性的过程

○ 涉及到所有的主机、路由器,以及与降低网络传输性能有关的所有因素

○ 是需要靠所有节点共同努力的结果

○ 防止过多的数据注入到网络中,使网络能够承受现有的网络负荷。

● 相比而言,流量控制是点对点通信的控制


二、拥塞控制的常用算法

● 方法概述

慢开始slow start,慢启动 )、拥塞避免congestion avoidance

快速重传fast retransmit )、快速恢复fast recovery

虽然重点是我们的后两个,但这四个中每个方法我们都必须要理解

● 几个概念缩写

cwnd(congestion window):拥塞窗口

rwnd(receive window):接收窗口

swnd(send window):发送窗口,swnd = min(cwnd, rwnd)


2.1、慢开始

由于我们发送方一开始并不知道我们的发送窗口该是多大 ,而且我们并不能确定我们的数据会不会在路上堵住,所以我们一开始要用cwnd也就是拥塞窗口充当我们的发送窗口 ,因为我们拥塞窗口通常来说一开始是很小的,然后我们不断的去试探从而不断扩大我们的发送窗口但也不能无限大,因为我们窗口大小不能大于接受窗口,所以发送窗口swnd = min(cwnd, rwnd)。

然后我们看下图,我们的MSS也就是数据最大段是100,当然此处cwnd=100与这个没关系,实际的cwnd可不一定等于100 ,然后当我们发出去后,我们发现没问题那么我们就继续扩大如果出现丢包了,那我们就缩小 ,同理一直如此。但我们不难发现一种规律,就是我们变大的速率是指数型增长的,因为我们要考虑到我们变大的速度和发送之间的效率关系,如果线路不是很拥塞用线性增长就大大减少了我们传输的效率

● cwnd的初始值比较小,然后随着数据包被接收方确认(收到一个ACK)

○ cwnd成倍增长(指数级)


2.2、拥塞避免

此方法我们以慢开始为起始,然后在中途设置了一个慢开始阈值达到这个阈值后我们就进行拥塞避免的操作------进行线性增长直到发生网络拥塞也就是丢包了,当时候科学家就想那我们得尽快的让这条马路上的车变少啊,那么就让其再进行一次慢开始,并将ssthresh的值缩小为网络拥塞值的1/2这就是我们拥塞避免的一套通用流程

● ssthresh(slow start threshold):慢开始阈值,cwnd达到阈值后,以线性方式增加

● 拥塞避免(加法增大):拥塞窗口缓慢增大,以防止网络过早出现拥塞

● 乘法减小:只要网络出现拥塞,把ssthresh减为拥塞峰值的一半,同时执行慢开始算法(cwnd又恢复到初始值)

当网络出现频繁拥塞时,ssthresh值就下降的很快


2.3、快重传

那么在出现拥塞的情况下,我们怎么能够快速的知道丢包了呢,就得靠我们快重传了。

● 接收方

每收到一个失序的分组后就立即发出重复确认

○ 使发送端及时知道有分组没有到达,而不是等待自己发送数据时才进行确认

● 发送方

只要连续收到三个重复确认(总共4个相同的确认),就应当立即重传对方尚未收到的报文段

○ 而不必继续等待重传计时器到期后再重传

可以看到,图中我们M3丢失,我们M4,5,6到了之后连续确认了三次我们的M2,这样我们发送端一看三次确认就知道M3丢了,那么我们发送端就会直接进行重传。(而如果是个别非拥塞原因丢的包,我们的机制是不会降低cwnd的)。

对于个别丢失的报文段,发送方不会出现超时重传,也就不会误认为出现了拥塞而错误地把拥塞窗口cwnd的值减为1实践证明,使用快重传可以使整个网络的吞吐量提高约20%


2.4、快恢复

在刚才我们看完了我们的拥塞避免机制**,那有没有人会觉得这个窗口变化的幅度是不是有点太大了?我们每次拥塞都要再进行慢开始,这样的效率未免还是有些不如意,那么当时我们的科学家就研究出了快恢复的机制来解决此问题。**

● 当发送方连续收到三个重复确认,说明网络出现拥塞

○ 此时就执行"乘法减小"算法,把ssthresh减为拥塞峰值的一半

与慢开始不同之处是现在不执行慢开始算法,即cwnd现在不恢复到初始值

○ 而是把cwnd值设置为新的ssthresh值(减小后的值)

○ 然后开始执行拥塞避免算法("加法增大"),使拥塞窗口缓慢地线性增大

如下图,在前面都与我们的拥塞避免没什么区别,但当出现拥塞避免之后,我们的cwnd值并没有跟慢开始一样进行归1 ,**而是直接等于了新的ssthresh的值,然后直接再进行"拥塞避免"操作,**这样就可以增加我们的传输效率

当然,其实自己在实际开发过程中就会发现,上面的四种方法其实还是有很多版本的,这就根据需求来看了。


TCP的建立连接和释放连接

一、三次握手

我们服务器先有状态后,我们客户端才能向服务器发起连接,这个状态也就是此处右边的LISTEN监听 ,那么我们客户端发来的第一次握手,我们服务器是怎么区分的呢,很简单,我们可以看,此处的SYN=1而ACK=0(图中未标),这就是我们第一次握手的标志,后面的所有数据包ACK都会是1。那我们第一个包的目的就是为了告诉对方自己数据包的起始序号,那有些人会问,那这不是常识吗,有什么必强调的呢?其实这里面还是有点说法的,我们在第一次握手这我们的序号其实是有两个的,其中一个是为了防止别人拿到我这个包后并不知道这是第一个包,如下图这是我们TCP三次握手的第一次握手的包,可以看到虽然我们序号是0,但看下面的16进制数怎么都不像0啊,所以在此处我们的0只是相对序号罢了,而在真实网络上传输的数据就是我们下一行,而这个0你也只能在抓包软件看到了,当然对方也能自动识别出。

那么当我们服务器接收到这个包后我们服务器就会进入STN-RCVD同步已接收状态,在我们发完第二次握手到收到第三次握手之间一直都会是这个状态,**在这个状态我们服务器和客户端还并没有连接,这也是我们待会就要提及的,为什么不能两次握手就结束了呢,按理来说两次握手不应该足以连接了吗?**没事,我们待会就要讲。

然后我们看第二次握手,给的信息都是正常信息,服务器需要客户端缓存区 x + 1 位的数据,而服务器发送缓存区的第一个序号为 y ,然后就发给客户端来看。**抓包出来看,也是跟我们说的一样。**包括此处的ack = 1也是相对确认号。

那么客户端确认完第二次握手后,就会发第三次握手,其中的信息也是没有多少变化的,因为服务器发来的序号是 y ,所以我们客户端的确认号是 y + 1 ,而序号也仍是 x + 1,代表着我要发 x + 1 位的数据了,但注意此时仍是没有数据的状态,只是一种告知信。

然后到服务器接收到第三次握手后,服务器才正式建立连接。

依旧是来几道考研题帮大伙练练手:

1、首先我们看我们乙向甲发的应该是第二个包,所以首先排除A,D那么再看我们甲发的是11220,所以我们乙要确认为11221,所以选C。

2、上题和上面的东西都能看懂的话,这题应该问题不大,答案是D。

常见面试题:为什么必须是三次握手而不是二次握手

采用"三报文握手"而不是"两报文握手"来建立TCP连接

一、一方面是为了防止已失效的TCP连接请求报文段突然又传送到了TCP服务器进程,也就是传的太慢了,客户端和服务器已经断联了,但服务器又收到了连接请求报文段,因而导致错误。

二、另一方面是因为怕我们客户端第二次握手并没有收到,但此时服务器却自认为建立了连接且客户端也不知道服务器准没准备好,此时客户端会自认为服务器没有准备好,那么服务器发来的任何数据,客户端也不会收。


二、四次挥手

当然不论是客户端还是服务器,都可以是主动断开方,这里我们默认按客户端为主动段开方来讲

● 第一次挥手

○ 序号seq字段的值设置为u,它等于TCP客户进程之前已经传送过的数据的最后一个字节的序号加 1。

○ TCP规定终止标志位FIN等于 1 的TCP报文段即使不携带数据,也要消耗掉一个序号。

○ 确认号ack字段的值设置为 v,它等于TCP客户进程之前已收到的数据的最后一个字节的序号加 1。

发送完成后,主动断开方进入FIN_WAIT_1状态,这表示我们客户端没有更多数据要发送给服务器,准备关闭socket连接了。
● 第二次挥手

○ 序号seq字段的值设置为v,它等于TCP服务器进程之前已传送过的数据的最后一个字节的序号加1。这也与之前收到的TCP连接释放报文段中的确认号v匹配。

○ 确认号ack字段的值设置为u+1,这是对TCP连接释放报文段的确认。

第二次挥手后,服务器就进入了CLOSE-WAIT(关闭等待)状态,TCP协议服务会通知高层的应用进程,TCP客户端向本地方向的连接已经关闭,TCP客户端已经没有数据要发送了。但TCP服务器进程如果还有数据要发送,TCP客户端仍要接收,也就是从TCP服务器进程到TCP客户进程这个方向的连接并未关闭。

● 第三次挥手

○ TCP连接释放报文段首部中的终止标志位FIN和确认标志位ACK的值都被设置为1。表明这是一个TCP连接释放报文段,同时也对之前收到的TCP报文段进行确认。

○ 序号seq字段的值假定被设置为w,这是因为在半关闭状态下TCP服务器进程可能又发送了一些数据。

○ 确认号ack字段的值被设置为u+1,这是对之前收到的TCP连接释放报文段的重复确认。

○ TCP服务器进入LAST-ACK状态
● 第四次挥手

○ 序号seq字段的值设置为u+1,这是因为TCP客户进程之前发送的TCP连接释放报文段虽然不携带数据,但要消耗掉一个序号。

○ 确认号ack字段的值设置为w+1,这是对所收到的TCP连接释放报文段的确认。

○ TCP客户端进入TIME-WAIT状态,等待2MSL,最长报文段寿命(Maximum Segment Lifetime),一个MSL[RFC793] 建议为2分钟。也就是说,TCP客户 进程进入TIME-WAIT(时间等待) 状态后,还要经过4分钟才能进入**关闭(CLOSED)**状态。


让我们来一道考研题帮大家练练手:

也就是甲发给乙的第一次挥手的结束序号是5001,而主动连接的第一次握手的序号是1000。这块可能有点绕。我来讲讲:

在三次握手中,我们最后一次握手的序号是多少?是这个1000吗?我其实想说的就是这个我们第三个包的序号****其实是1001,因为第一个包的序号只是在给对方做提示罢了,目的就是为了让对方的确认号与我们的序号对上 ,**那么就说明,我们是从1001开始发的,那么挥手时的第一次连接也是一样的,我们的结束序号是在最后一个包的最后一个序号上+1的,那么我们最后数据包的最后那个序号其实是5000。**到这,答案也就出来了就是我们的C。

常见面试题:为什么此处TCP客户端(主动断开方)在TIME-WAIT状态必须等待2MSL的时间?

一、一方面是因为我们TCP客户端在给对方发这第四次握手的时候,这个包是有可能丢失的,那么我们TCP服务器在等待第四次握手的时候如果超时,是会重新发第三次握手的,然后TCP客户端再发一次第四次握手,然后将2MSL的计时归0重新计,那么这样就保证了TCP服务器 能收到第四次握手,从而正确进入关闭(CLOSED)状态。反过来说,如果没有这2MSL的时长,因关闭过早的TCP客户端没发收到TCP服务器重发的第三次握手,那么TCP服务器就无法正常进入**关闭(CLOSED)**状态。

二、另一方面防止"已失效的数据报文"出现在新连接中。TCP客户端在发送完最后一个ACK报文后,再经过2MSL,才能最终关闭和释放端口,这就意味着,相同端口的新TCP新连接,需要在2MSL的时间之后,才能够正常的建立。2MSL这段时间内,旧连接所产生的所有数据报文,都已经从网络中消失了,从而,确保了下一个新的连接中不会出现这种旧连接请求报文。

常见面试题:为什么关闭连接的需要四次挥手,而建立连接却只要三次握手呢?

那么在建立连接场景中,服务器的应答可以稍微简单一些。当服务器收到客户端的SYN连接请求报文后,其中ACK报文表示对请求报文的应答,SYN报文用来表示服务器的连接也已经同步开启了,因为是在建立连接而不能通信,所以ACK报文和SYN报文之间,不会有其他报文需要发送,故而可以合二为一,可以直接发送一个SYN+ACK报文。所以,在建立连接时,我们只需要三次握手即可。

而关闭连接时,服务器在收到对方的FIN结束请求报文时,很可能服务器 数据没有发送完成,并不能立即关闭连接,服务器只能先回复一个ACK响应报文,告诉客户端:"你发的FIN报文我收到了,但必须得等到我所有的业务报文都发送完了,我才能真正的结束,在快结束之前,我会发你FIN+ACK报文的,你先等着"。所以,服务器的确认报文,需要拆开成为两步,故总体就需要四步挥手。

也就是说我们四次挥手是要比三次挥手多了一次那服务器没发完数据的情况。

结语

那么关于计网之传输层第三部分有关TCP拥塞控制与连接管理的讲解就到这里了。

这也是传输层的最后一篇文章了

我是YYYing, 后面还有更精彩的内容,希望各位能多多关注支持一下主包。

**无限进步,**我们下次再见。


---⭐️ 封面自取**⭐️---**

相关推荐
LcVong2 小时前
基于C#实现斑马ZT411打印机TCP通讯与打印状态精准判定
网络·tcp/ip·c#
willhuo2 小时前
程序这东西,想的即使在完善,也有想不到的地方。。
linux·服务器·网络
EverydayJoy^v^2 小时前
RH134学习进程——三.分析与存储日志
运维·服务器·网络
定偶2 小时前
Ubuntu 20.04 网络与软件源问题
网络·ubuntu·php·系统优化
ZePingPingZe2 小时前
深入理解网络模型之Spring Cloud微服务通信、Socket、HTTP与RPC
网络协议·spring cloud·rpc·dubbo
一路往蓝-Anbo2 小时前
【第48期】:嵌入式工程师的自我修养与进阶之路
开发语言·网络·stm32·单片机·嵌入式硬件
终端域名2 小时前
网络架构的变革将如何影响物联网设备的设计和开发?
网络·物联网·架构
郝学胜-神的一滴2 小时前
深入理解网络分层模型:数据封包与解包全解析
linux·开发语言·网络·程序人生·算法
门思科技2 小时前
ThinkLink 基于 RPC 的 LoRaWAN 告警通知机制
网络·网络协议·rpc