• TCP三次握手四次挥手


    TCP是什么?

    TCP是面向连接的协议,它基于运输连接来传送TCP报文段,TCP运输连接的建立和释放,是每一次面向连接的通信中必不可少的过程。

    TCP运输连接有以下三个阶段:

    • 建立TCP连接,也就是通过三报文握手来建立TCP连接。
    • 数据传送,也就是基于已建立的TCP连接进行可靠的数据传输。
    • 释放连接,也就是在数据传输结束后,还要通过四报文挥手来释放TCP连接。

    TCP的运输连接管理就是使运输连接的建立和释放都能正常的进行。

    TCP的一些控制位

    SYN:同步位,为1时,表示希望建立连接,并且初始化序列号

    ACK:确认位,为1时,确认有效,除了建立连接开始的syn包时,其他包该位必须为1

    seq:序号,第一个seq是随机产生的,后面的序号为前面的序号+1

    ack:确认号,为序号+1

    RST:复位位,为1时,表示出现异常,强制连接断开

    FIN: 终止位,为1时,希望正常断开连接,通信双方互相交换FIN,表示可以断开

    TCP三次握手

    三次握手是 TCP 连接的建立过程。在握手之前,主动打开连接的客户端结束 CLOSE 阶段,被动打开的服务器也结束 CLOSE 阶段,并进入 LISTEN 阶段。随后进入三次握手阶段:

    ① 首先客户端向服务器发送一个 SYN 包,并等待服务器确认,其中:

    标志位为 SYN=1,表示请求建立连接;
    序号为 Seq = x(随机产生);
    随后客户端进入 SYN-SENT 阶段。

    ② 服务器接收到客户端发来的 SYN 包后,对该包进行确认后结束 LISTEN 阶段,并返回一段 TCP 报文,其中:

    标志位为 SYN=1 和 ACK=1,表示确认客户端的报文 Seq 序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接
    序号为 Seq = y(随机产生);
    确认号为 ack = x + 1,表示收到客户端的序号 Seq 并+ 1 作为自己确认号 ack 的值,随后服务器端进入 SYN-RVCD 阶段。

    ③ 客户端接收到发送的 SYN + ACK 包后,明确了从服务器到客户端的数据传输是正常的,从而结束 SYN-SENT 阶段。并返回最后一段报文。其中:

    标志位为 ACK=1,表示确认收到服务器端同意连接的信号;
    序号为 Seq = x + 1,表示收到服务器端的确认号 ack,并将其值作为自己的序号值;
    确认号为 ack= y + 1,表示收到服务器端序号 seq,并将其值加 1 作为自己的确认号 Ack 的值。
    随后客户端进入 ESTABLISHED。
    当服务器端收到来自客户端确认收到服务器数据的报文后,得知从客户端到服务器的数据传输是正常的,从而结束 SYN-RECV 阶段,进入 ESTABLISHED 阶段,从而完成三次握手。

    注意:这里的第三次握手是可以携带数据的,因为发送端已经确认了服务端有接受和发送的能力。而其他两次并没有确认对方的接受能力

    是否可以使用“两报文握手”建立连接?

    为什么TCP客户进程最后还要发送一个普通的TCP确认报文段?

    避免旧的重复连接初始化造成混乱

    考虑这样一种情况,TCP客户进程发出一个TCP连接请求报文段,但该报文段在某些网络节点长时间滞留了,这必然会造成该报文段的超时重传

    假设重传的报文段被TCP服务器进程正常接收,TCP服务器进程给TCP客户进程发送一个TCP连接请求确认报文段,并进入连接已建立状态。

    现在,TCP双方都处于连接已建立状态,他们可以相互传输数据,之后可以通过四报文挥手来释放连接,TCP双方都进入了关闭状态。

    一段时间后,之前滞留在网络中的那个失效的TCP连接请求报文段到达了TCP服务器进程,TCP 服务器进程会误认为这是TCP客户进程又发起了一个新的TCP连接请求,于是给TCP客户进程发送TCP连接请求确认报文段并进入连接已建立状态。

    该报文段到达TCP客户进程,由于TCP客户进程并没有发起新的TCP连接请求,并且处于关闭状态,因此不会理会该报文段。

    但TCP服务器进程已进入了连接已建立状态,他认为新的TCP连接已建立好了,并一直等待TCP客户进程发来数据。这将白白浪费TCP服务器进程所在主机的很多资源。

    三次握手的主要目的是确认自己和对方的发送和接收都是正常的,从而保证了双方能够进行可靠通信。若采用两次握手,当第二次握手后就建立连接的话,此时客户端知道服务器能够正常接收到自己发送的数据,而服务器并不知道客户端是否能够收到自己发送的数据。

    网络往往是非理想状态的(存在丢包和延迟),当客户端发起创建连接的请求时,如果服务器直接创建了这个连接并返回包含 SYN、ACK 和 Seq 等内容的数据包给客户端,这个数据包因为网络传输的原因丢失了,丢失之后客户端就一直接收不到返回的数据包。由于客户端可能设置了一个超时时间,一段时间后就关闭了连接建立的请求,再重新发起新的请求,而服务器端是不知道的,如果没有第三次握手告诉服务器客户端能否收到服务器传输的数据的话,服务器端的端口就会一直开着,等到客户端因超时重新发出请求时,服务器就会重新开启一个端口连接。长此以往, 这样的端口越来越多,就会造成服务器开销的浪费。

    参考

    深入浅出TCP三次握手 (多图详解)_tcp三次握手具体报文-CSDN博客

    TCP三次握手(含常见面试题)详解-CSDN博客

    TCP四次挥手

    在断开连接之前客户端和服务器都处于ESTABLISHED状态,双方都可以主动断开连接,以客户端主动断开连接为优。

    第一次挥手:客户端打算断开连接,向服务器发送FIN报文(FIN标记位被设置为1,1表示为FIN,0表示不是),FIN报文中会指定一个序列号,之后客户端进入FIN_WAIT_1状态。

    也就是客户端发出连接释放报文段(FIN报文),指定序列号seq = u,主动关闭TCP连接,等待服务器的确认。

    第二次挥手:服务器收到连接释放报文段(FIN报文)后,就向客户端发送ACK应答报文,以客户端的FIN报文的序列号 seq+1 作为ACK应答报文段的确认序列号ack = seq+1 = u + 1。

    接着服务器进入CLOSE_WAIT(等待关闭)状态,此时的TCP处于半关闭状态(下面会说什么是半关闭状态),客户端到服务器的连接释放。客户端收到来自服务器的ACK应答报文段后,进入FIN_WAIT_2状态。

    第三次挥手:服务器也打算断开连接,向客户端发送连接释放(FIN)报文段,之后服务器进入LASK_ACK(最后确认)状态,等待客户端的确认。

    服务器的连接释放(FIN)报文段的FIN=1,ACK=1,序列号seq=m,确认序列号ack=u+1。

    第四次挥手:客户端收到来自服务器的连接释放(FIN)报文段后,会向服务器发送一个ACK应答报文段,以连接释放(FIN)报文段的确认序号 ack 作为ACK应答报文段的序列号 seq,以连接释放(FIN)报文段的序列号 seq+1作为确认序号ack。

    之后客户端进入TIME_WAIT(时间等待)状态,服务器收到ACK应答报文段后,服务器就进入CLOSE(关闭)状态,到此服务器的连接已经完成关闭。

    客户端处于TIME_WAIT状态时,此时的TCP还未释放掉,需要等待2MSL后,客户端才进入CLOSE状态。

    由客户端到服务器需要一个FIN和ACK,再由服务器到客户端需要一个FIN和ACK,因此通常被称为四次握手。

    客户端和服务器都可以主动关闭连接,只有率先请求关闭的一方才会进入TIME_WAIT(时间等待状态)。

    为什么挥手需要四次?

    首先根据四次挥手的过程可以知道每一方都要发送一个FIN报文和一个ACK报文

    客户端发送FIN报文表示请求断开连接,仅仅表示客户端不在发送数据了,但是还能够接收数据
    服务端收到客户端的FIN报文后,内核会自动的回复ACK报文,等待服务端进程调用close函数,也就是等待服务端进程处理完数据之后,服务端会向客户端发送FIN报文,表示要断开连接.
    可以看出,服务端发送的ACK报文并不能和FIN报文结合在一起发送,因为要等待服务端进程处理完数据之后才可以关闭连接, 所以是四次挥手.

    第一次挥手丢失了,会发生什么?

    我们知道首先客户端会向服务端发送FIN报文表示客户端想要断开连接,之后客户端进入FIN_WAIT1状态,服务端接收到客户端的FIN报文之后内核立即会返回一个ACK报文.

    如果第一次挥手丢失,也就是客户端发送给服务端的FIN报文丢失,那么当客户端没有收到服务端发送的ACK报文,就会触发超时重传机制,如果之后接收到了ACK,报文会继续进入四次挥手过程,如果没有会继续重传,达到重传次数 客户端还没有收到ACK报文,那么会等待一段时间(一般是上一次报文发送时间的2倍) 如果还没有收到,客户端会主动关闭连接

    第二次挥手丢失了,会发生什么?

    服务端收到客户端发送的FIN报文之后,会回复给客户端ACK报文,如果ACK报文丢失,客户端没有收到服务端的ACK报文,那么客户端就会超时重传FIN报文,如果之后接收到了ACK,报文会继续进入四次挥手过程,如果没有 会继续重传,达到重传次数 客户端还没有收到ACK报文,那么会等待一段时间一般是上一次报文发送时间的2倍, 如果还没有收到,客户端会主动关闭连接

    接收到第二次挥手后的两种场景

    当客户端接收到服务端返回的ACK报文之后,就会等待服务端发送FIN报文,这段时间客户端处于FIN_WAIT_2的状态.

    如果是close函数关闭的连接,表示不能够发送和接收数据,所以处于FIN_WAIT_2状态有时长限制,一般为60s,如果超过这个时长客户端还没有收到服务端的FIN报文,那么客户端就会主动关闭连接.

    如果是shutdown函数关闭的连接,表示只是不能发送数据,但是可以接收数据,如果一直没有收到服务端发送的FIN报文也就是第三次挥手,会一直死等下去.

    第三次挥手丢失了,会发生什么?

    当服务端进程处理完数据之后就会进行关闭连接,内核就会向客户端发送FIN报文表示请求关闭连接,之后服务端就会进入LAST_ACK状态,会等待客户端返回的ACK报文.

    当服务端进程调用close函数的时候->数据处理完之后才可以关闭连接,内核没有权利替代服务端关闭连接,必须由服务端主动调用close函数来关闭连接

    如果服务端一直没有收到客户端返回的ACK报文,那么就会触发超时重传,服务端会重传FIN报文,如果之后接收到了ACK,服务端进入closed状态,如果没有 会继续重传,达到重传次数 客户端还没有收到ACK报文,那么会等待一段时间一般是上一次报文发送时间的2倍, 如果还没有收到,服务端会主动关闭连接.

    另外呢,如果是使用的close函数关闭的连接,如果超过时长tcp_fin_timeout限制一般为60s,不会一直处于FIN_WAIT_2状态,客户端会主动关闭连接.

    第四次挥手丢失了,会发生什么?

    当服务端处理完数据之后就会调用close函数来关闭连接,会给客户端发送FIN报文,服务端进入LAST_ACK状态,客户端收到之后会返回一个ACK报文,客户端进入Time_WAIT状态等待2MSL后进入close状态,服务端接收到最后一个ACK报文之后进入closed状态.

    如果第四次挥手丢失,也就是客户端返回的ACK丢失,服务端就会触发超时重传机制,重传FIN报文,如果之后服务端接收到了ACK,服务端就会进入closed状态,如果没有, 会继续重传,达到重传次数服务端还没有收到客户端发来的ACK报文,那么会等待一段时间一般是上一次报文发送时间的2倍, 如果还没有收到,服务端会主动关闭连接

    另外,客户端收到第三次挥手之后,客户端返回给服务端ACK报文,客户端就会进入TIME_WAIT状态,会等待2MSL,如果中途再次收到第三次挥手,会重置定时器, 当等待2MSL之后,客户端就会主动关闭连接进入closed状态.

    为什么 TIME_WAIT 等待的时间是 2MSL?

    首先说什么是MSL,TTL,以及两者的关系

    MSL就是最大的报文生成时间,MSL是网络报文生存的最长时间,超过这个时间,报文将会被丢弃,因为TCP是基于IP协议的,TTL是经过路由器的最大跳数,每经过一个路由器,TTL就减一,当减到0的时候报文就会被丢弃,同时发送ICMP报文给源主机.

    TTL 与 MSL的区别 : TTL是经过路由的最大跳数,MSL是报文生存的最长时间,要确保MSL>=TTL才能保证报文是正常消亡.

    为什么要等待2MSL呢 ?

    原因是 在网络中可能来自发送方发来的数据报,然后接收方要给对方一个响应. 这样报文一来一回的时间就是2MSL.

    比如当最后一个ACK报文丢失(也就是第四次挥手丢失),服务器那边就会触发超时重传机制,重传FIN报文,当FIN报文到达客户端,客户端再回一个ACK响应,这样一来一回等待的时间是2MSL.

    2MSL时长允许报文至少丢失一次,当ACK报文丢失的时候,重发的FIN会在第二个MSL到达客户端,这样TIME_WAIT状态就可以应对.2MSL是当第三次挥手到达客户端的时候就会开始计时,当中途FIN报文再次到达客户端,定时器就会被重置为2MSL.

    为什么不是 4MSL或者8MSL呢 ?

    由于丢包概率很小,加入丢包概率为1/100,那么第二次丢包就是1/10000, 所以我们可以忽略,性价比会更高.

    为什么需要 TIME_WAIT 状态?
    主要有两个原因

    防止旧的报文进入了相同的四元组连接中
    能够保证被动关闭方正常关闭.
     

  • 相关阅读:
    CentOS7和CentOS8 Asterisk 20.0.0 简单图形化界面6--JSSIP呼叫demo
    信息学奥赛一本通:1104:计算书费
    项目实战第三十六讲:基于 Sharding-JDBC 的商品分库⽅案
    vue3+elementPlus el-select组件同时支持label和value模糊查询
    基于SSM+Vue的医院医患管理系统
    Mac安装虚拟机和CentOS
    LeetCode 2609. 最长平衡子字符串
    【GIT】如何列出2个branch/tag/commitId 之间的所有commit
    【Python】-- Turtle绘图(使用代码画喜欢的图形!)
    Exchange Office 365邮箱报表
  • 原文地址:https://blog.csdn.net/weixin_47755728/article/details/140000496