tcp协议的超时重传(去重,确定时间),通信建立机制(三次握手,通信,四次挥手的细节和图解),为什么是3次/4次,肉机,全/半连接队列,SYN洪水,TIME

您所在的位置:网站首页 tcp连接的建立过程图解 tcp协议的超时重传(去重,确定时间),通信建立机制(三次握手,通信,四次挥手的细节和图解),为什么是3次/4次,肉机,全/半连接队列,SYN洪水,TIME

tcp协议的超时重传(去重,确定时间),通信建立机制(三次握手,通信,四次挥手的细节和图解),为什么是3次/4次,肉机,全/半连接队列,SYN洪水,TIME

2024-07-16 23:37| 来源: 网络整理| 查看: 265

目录

超时重传

引入

介绍

去重

引入

解决

如何确定超时时间

介绍

如何设置

通信建立机制

三次握手

图解

connect

accept

通信

图解

介绍

四次挥手

图解

介绍

为什么是三次握手,四次挥手呢?

本质

为什么握手是3次

可合并

 可靠验证全双工通路的畅通 (建立连接的本质)

让客户端承担失败成本 (SYN洪水)

为什么挥手是4次

半连接

介绍

问题

肉机

介绍

查看状态

三次握手

listen后,accept前

全连接队列

引入

介绍

listen的第二个参数

全队列连接满时 

引入

原理

双方认知不一致

半连接队列

引入

介绍

重新建立连接 (并没有触发RST)

SYN洪水 

四次挥手

一方断开连接后 

另一方也断开 

TIME_WAIT -> CLOSE

TIME_WAIT状态

如何解决 -- setsockopt()

设置好后

为什么一般是服务端出现TIME_WAIT状态

TIME_WAIT状态

介绍

MSL

为什么会有TIME_WAIT状态

保证四次挥手的正确性

处理旧数据

 总结

下面介绍的这些都属于通信细节,在应用层的角度是感觉不到的

超时重传 引入

在之前介绍tcp报头字段时,我们提到,tcp有确认应答机制 --tcp协议介绍,协议段格式(端口号,首部长度,窗口大小,序号,确认序号,6个标志位),流量控制,确认应答机制,捎带应答,三次握手的双方认知不一致问题-CSDN博客

当我们收到应答后,就可以确定对方已经收到了自己发送的数据但也仅此而已如果客户端没有收到应答,是无法确定具体情况的 -- 自己发出的数据丢了 / 发出去了,但在某个设备下排队 / 对方收到了,但应答丢了

所以,只能规定出一个时间段,如果超过这个时间段还没有收到,那就判定是丢包了,这时客户端将会超时重传

介绍

是一种网络通信协议中的错误控制机制,用于确保数据包在不可靠的网络环境中能够正确传输

去重 引入

但仔细想想:

如果因为数据丢了 / 因网络问题阻塞在某处(这就是真正的超时了)而重传还能理解只是应答丢了就重传,岂不是服务器会收到重复报文(报头一样,数据也一样)?

而收到重复报文,也属于不可靠的一种,需要去重

解决

通过序号判断是否重复

每个报文的序号基本不会重复 -- 序列号32位,轻易不会重复 / 采用随机初始序列号,即使在缓冲区的偏移量相同,序号也不会一样即使重复也不会冲突 -- 如果重复也是不同的连接之间,而不同连接的ip/port是不同的,所以不会冲突

所以,我们的序号不仅可以用来排序/允许少量应答丢失,还可以去重

那么,现在我们已经知道了数据丢失/阻塞可以重传,且不会重复

那么,回到最关键的问题,这个特定的时间间隔如何确定呢?

如何确定超时时间 介绍

和网络状态相关

网络好

数据传输速度很快,如果设定的时间较长,丢包的数据需要等待很久才能补发,可能会影响通信效率

网络不好

设定的时间不能太短假设正常的单向通信至少需要100ms,设定的间隔却优先创建结构体

而服务器则根据是否接收到最后一条应答,来判断是否建立成功也就是,由客户端来承担建立失败的成本

所以,奇数次握手可以确保在一般情况下,握手失败的连接成本是嫁接在客户端的

而3次是满足奇数次握手+没有明显硬伤(指只进行1次握手,很明显非常不合理)的最小次数

为什么挥手是4次

断开连接不像建立连接那样,服务器必须马上回应

可能在客户端想要断开连接的时候,服务器还有数据没发送完呢在连接没有彻底断开前,服务器还是可以发送剩余数据给对方的,对方也能接收到

这种情况下,还需要再等一会,服务器才能给客户端发送带有FIN的报文(表明自己没有数据发送了)

刚好可以合并的情况反而是一种巧合,一种偶然

所以,之后我们也采用3次握手,4次挥手的称呼

半连接 介绍

是一种网络连接状态,通常出现在TCP连接的建立过程中

当一方试图与另一方建立连接,但另一方尚未完全响应时,就会产生半连接状态

半连接状态通常发生在三次握手的2-3步之间

即服务器已发送SYN-ACK包,但尚未收到客户端的ACK包此时,服务器端的连接状态称为“半连接” 问题

虽然三次握手将失败成本嫁接到客户端上,但服务端那边:

即使还没有建立成功,依然也要维护好结构体所以,依然会受到恶意攻击的影响 肉机

介绍

如果有多个客户端种了木马病毒后,可能会收到黑客影响,同一时间去和某个服务器建立连接

用专业术语来说,肉机 -- 被攻击者控制并用于执行恶意任务的计算机虽然是正常的建立连接的行为,但因为是同一时刻去做,并且是大量行为所以在一段时间内可能会将服务器的连接资源消耗殆尽

遇到这种情况,就得引入新策略

设置防火墙,限流啦什么的 查看状态 三次握手

listen后,accept前

当服务器启动后,即使没有accept,只要处于监听状态,就能用netstat查看,双方都建立好了连接

服务器所在主机的连接情况:

这里看到的local ip和我们显示的主机ip并不相同:

因为[云服务器上]为我们显示的公网ip是模拟出来的,这里看到的local ip才是真正的内网ip

连接是双向的,在客户端主机上也可以看到这个连接:

这里看到的local同理,是真实的内网ip

所以,可以看出来,tcp连接是否成功 与 是否调用accept 无关

三次握手是双方os内部自主完成的

全连接队列 引入

因为accept不参与三次握手的过程,它只是会将新建立好的连接拿上去使用

那么,就必然存在还未来得及被拿上去的连接结构体,且不止一个那么os就得维护这些已经建立好的连接,以供上层来获取  

要管理,就得先描述再组织,组织的方式是队列

有新的连接了就入队列上层调用accept,从队列中拿出连接和特定文件相关联,返回给用户新创建的fd

而以上过程就很像cp模型,上层取,底层入队列

介绍

上面介绍的队列就是全连接队列

因为能入队列的都是成功建立连接的

队列为什么不能太长?

如果太长,当服务器因忙碌而无法获取全连接队列中的连接,就会平白占据大量资源来维护连接,且短时间内不会释放服务器都这么忙了,万一它正在处理的上层逻辑中需要这些空间呢?这些却久久不释放,反而会耽误服务器的效率所以,没有必要设置的太长

如果没有这个全连接队列呢?

忙的时候还好,不会平白占用资源那万一之后服务器闲下来了呢?它还得等有客户端连接,经过三次握手后,才能拿到连接那服务器就会闲置下来,多亏啊创建全连接队列,相当于用空间换时间了,不让服务器处于空闲状态,一有空位就补上,可以提高整体的效率,让资源得到充分利用

比如:

就像去饭馆吃饭,生意非常火爆,会有老板给顾客说"我这人满了,你们去别处吃吧"吗不会的吧一般老板会在门口摆点椅子啥的,先让顾客拿着号等待,等有顾客出来就直接让等待的按照号码进去 listen的第二个参数

listen()的第二个参数值+1,描述的就是这个队列最大容纳量

刚刚已经讨论过了,这个参数不能太大,也不能没有那适合的长度是多少呢?由服务器资源状态,也就是主机的资源决定

全队列连接满时  引入

当我们将这个参数值改为1,并增加第三个客户端时,会出现建立失败的情况:

服务器:

客户端:

原理

注意:

状态的改变是一定要在完成下一个状态的前置条件的情况下发生

客户端是建立成功的状态

所以第三条ACK它是一定发送了的

服务器这边状态维持在了SYN_RCVD

因为队列容量的限制,它不允许有超出数量的连接建立成功,但又不能修改客户端已经发送了应答的事实所以只能将应答直接丢弃,从而让连接无法建立成功所以状态无法修改到下一个状态 双方认知不一致

这就是之前在介绍tcp报头中的标志位提到的,此时双方对是否建立完成的认识不一致:

--tcp协议介绍,协议段格式(端口号,首部长度,窗口大小,序号,确认序号,6个标志位),流量控制,确认应答机制,捎带应答,三次握手的双方认知不一致问题-CSDN博客

并且承担建立失败的成本在客户端

因为只有客户端是连接成功的

半连接队列 引入

当然,服务端也是付出了成本的,即使没有连接成功,也有在维护结构体

但这个结构体会在一段时间后自动释放

 这个与服务器未完成连接的客户端,在下次查看时,已经查不到了:

 而客户端仍在运行,且还是连接成功的状态:

可以知道,在服务器视角,未完成连接的连接维持的时间很短,它并不会一直保存状态为SYN_RECV

介绍

被连接的一方处于SYN_RECV,这种状态叫做半连接

它也有对应的结构体,也是需要维护它的,所以也就有了半连接队列半连接的长度由系统自主决定这里面的结点不会长时间被维护,定期会清理

而连接成功的结构体维持时间较长

重新建立连接 (并没有触发RST)

 当我们发送数据时,会重新触发三次握手,但依然卡在SYN_RECV状态:

所以数据并没有被成功发送,还停留在客户端的发送缓冲区中因为连接并没有建立成功

在连接不成功的情况下,如果客户端发来的ACK丢包了,导致服务器无法确认自己发送的SYN+ACK是否成功

这种情况下,服务器才会判断握手不成功,从而触发RST机制

而这里的服务器实际上是收到了应答的(因为客户端是建立成功的状态)

它只是不做处理,可以认为是收到后丢弃掉了所以这里的重新连接,并不是因为触发了RST机制

还有一点:

如果数据能发送给对方,才会触发对方的RST机制,发送给我带有RST的报文,来重新建立连接如果不能,对方直接去重连了这里的数据压根没发出去,所以不是触发了RST SYN洪水 

半连接队列不会太吃资源

因为数量有限,存在使用短

一个连接,会先进入半连接状态,连接成功后才会进入全连接状态

所以,当有大量客户端一直发起连接请求时,我们并不担心吃资源的问题而是要考虑它会一直占用半连接队列的资源,使得其他正常的连接无法进入半连接队列,从而无法进入全连接队列,也就无法被accept获取,这个客户端就无法连接这才是真正意义上的SYN洪水,前面我们讨论的并不全面

就像选课/抢票啥的

大量客户端向服务器发送请求,会显示网页繁忙 就是因为有大量其他用户占用了队列资源,而你没挤进去,所以无法建立连接不断刷新的话,可能一会就进去了 -- 是因为你很幸运地在半连接队列中,并被挑中进入了全连接队列,从而连接上服务器

四次挥手

一方断开连接后 

  我们将服务器作为先断开的一方,然后可以看到他的状态变为FIN_WAIT2:

客户端这边变为CLOSE_WAIT:

情况与图中的流程相符

另一方也断开 

先断开连接的一方,在对方也断开连接时,会进入TIME_WAIT状态

但要看到上面这种情况,我们首先得让连接被上层获取到 不然就直接释放了(后面会说原因)

在连接被获取后,客户端再退出,服务器这边的连接状态:

虽然符合我们说的,但是为什么呢?

客户端和服务器都相继退出,肯定是已经完成了四次挥手的,怎么主动断开的一方仍然维持着TIME_WAIT的状态呢?说明连接还未完全释放并且一段时间后,这个连接就查看不到了,这时连接才被释放

所以TIME_WAIT->CLOSE之间会发生什么呢?

TIME_WAIT -> CLOSE TIME_WAIT状态

如果我们在这个期间重新启动服务器,会出现端口号绑定失败的情况:

也就是说,当我们的服务器先主动断开时.会进入TIME_WAIT状态

此时虽然进程已经退出了,但连接并没有被彻底断开 -> ip和port依然被使用

如果我们立即重启进程,仍然使用上次的端口号

而端口号无法被两个进程同时绑定,所以会绑定失败

但是,如果是紧急需要重启的服务,比如双十一时的购物平台的服务器,可能会被大量客户端的连接搞挂了

这样服务器就成为了主动断开连接的一方,且会强制进入TIME_WAIT而TIME_WAIT状态会维持30~60s,这取决于底层timeout的时间,也取决于用户层的设置在这样的情景下,如果服务器不能立即重启,它将会损失大量交易订单 ,这是我们不能允许的 如何解决 -- setsockopt()

通过设置套接字属性,让它可以允许地址复用 

level

一般设置为SOL_SOCKET,表示在套接字层

optname

要设置的具体选项。不同的level有不同的选项名第一个:表示ip和port都复用,第二个,老版本的

optval

指向包含选项值的缓冲区不同的选项可能需要不同类型的数据,比如int,struct timeval等就是用来设置标志是否有效,我们定义一个int opt=1即可

optlen

optval缓冲区的大小 设置好后

设置好后,重新上面的操作,服务器就可以复用端口号了

这里是我们在代码里让服务器关闭连接的,所以是FIN_WAIT2状态并且上一个处于TIME_WAIT的连接依然存在 为什么一般是服务端出现TIME_WAIT状态

为什么我们都是在服务器看到无法复用端口的情况呢?

因为客户端使用的是系统分配的随机端口,客户端退出又启动时,使用的很少是上一个端口号而服务器一般是固定端口号

接下来,我们详细介绍一下TIME_WAIT状态

TIME_WAIT状态

介绍

也称为“连接终止定时等待状态”

tcp协议规定的,主动关闭连接的一方要处于TIME_WAIT状态(通常是 2 倍的最大报文段寿命,2*MSL)的时间后才能回到CLOSED状态 MSL

一个报文从一端发到另一端的过程中,就是处于网络里

如果是一个正常报文,那这个时间也就是毫秒级的,叫最大传送时长如果一个报文被阻塞住了,它在网络里有特定的存活时间,其最大存活时长就是MSL,这个时间不是固定的要区分这两个时间概念

MSL在RFC1122中规定为2分钟,但不同os上的实现不同,centos/ubuntu上是60s

查看:

这个时间可以修改,但是否起效,不好说

所以,一般这个timeout的时间是60-120s

为什么会有TIME_WAIT状态

当双方要断开连接时,可能数据发完了,但不一定收到了,网络中可能还残留有互相发送的数据

等待2个MSL正好是数据一来一回的最大时间

不能说是完全保证可以

而是较大概率可以让双方接收到还未收到的数据并处理返回 / 丢掉这些数据 保证四次挥手的正确性

在四次挥手中,前面两次双方连接都还未释放

所以如果这两次的报文丢失,还可以进行补发措施

当客户端一旦发出ACK后它这边的四次挥手就完成了 -> 释放了连接结构

一旦最后一个ACK丢失了,就没人能补发,就导致服务器只能一直处于LAST_ACK状态中因为没有收到应答

那这样,服务器可能认为是自己的数据对方没收到,就会补发FIN

但是,对方此时已经释放了,对方无法响应如果发送多次依然没有应答,自己也就退出了

但是,这属于异常处理

我们不能依赖异常处理来解决问题,尽量要让他们正确地完成四次挥手

所以,要让发送方停留一段时间

也就是维持在TIME_WAIT状态如果ACK丢失,发送方还能收到对方补发的FIN,并返回应答这样可以保证有较大的概率让四次挥手正确地挥手完 处理旧数据

因为tcp具有可靠性

这些一直没收到的数据很可能早就补发了而处理掉这些阻塞住的数据,是为了防止影响下一次的通信要是双方立即又开始通信,且ip,端口号均未改变,可能会在新一轮的通信中收到旧报文

那要是消散不彻底怎么办?

双方的报文都有序号,而起始序号是随机的这个随机序号可以规避黑客,规避历史报文对现在通信的影响也就是说,即使收到了,可以通过序号来判断是否是合法数据

这也能证明为什么没有被accept获取过的连接在释放时,并不会维持TIME_WAIT,直接就断开了

因为压根就没通信过,也就不需要将历史数据消散  总结



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3