TCP协议的三次握手和四次挥手

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

目录
  1. TCP 报文
  2. 三次握手
    1. 过程
    2. 过程简析
    3. 问题解答
    4. 半连接队列
    5. SYN 攻击
  3. 四次挥手
    1. 过程
    2. 过程简析
    3. 问题解答

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

TCP 报文#

image.png

在讲解之前先了解下 TCP 报文的格式,上图是报文的首部,这里不需要了解它全部字段的作用,只需要了解 3 个字段即可。

  • 序号“sequence number”:下文简称 seq,占用 4 个字节。

  • 确认号“acknowledgement number”:下文简称 ack,占用 4 个字节,用于确认序号,并返回序号+1。如序号是 1626544836,则确认号是 1626544837。

  • 标志域“tag flags”:如图所示有 6 位,这里只用到其中 3 位,分别是:

    • SYN(SYNchronization):同步标志。
    • ACK(Acknowledgement):确认标志。
    • FIN(Finis):终结标志。

    当其中哪个标志有效时,它的值为 1。 如SYN=1时,表示标志域中 SYN 标志生效,这段报文也可以叫做“ SYN 报文”或者“同步报文”。

三次握手#

过程#

image.png

第一次握手:

客户端发送(SYN=1,seq=J)的同步报文给服务器,然后进入SYN_SEND状态,等待服务器发回确认报文;

第二次握手:

服务器收到 SYN 报文后,如果同意连接,则发送(ACK=1,ack=J+1,SYN=1,seq=K)SYN-ACK 报文给客户端, 然后进入SYN_RECV状态,等待客户端发回确认报文。

第三次握手:

客户端收到服务器的 SYN-ACK 报文后,还需要向服务器给出确认,发送一个(ACK=1,ack=K+1)确认报文给服务器,然后进入ESTABLISHED状态。

服务器收到客户端的确认报文后,也会进入ESTABLISHED状态。此时,TCP 连接建立成功。

过程简析#

大白话讲就是

  • 第一次握手:客户端对服务器说:“喂,你能听到我说的话吗?”
  • 第二次握手:服务器回答客户端说:“嗯,我能听到,那你能听到我说的话吗?”
  • 第三次握手:客户端回答服务器说:“嗯,我也能听到了!”然后,客户端继续对服务器说:“好,现在你听我说...”, 然后就是 BALABALA 一堆话了(HTTP 请求)。

第三次握手后,客户端就可以对服务器发起 HTTP 请求了。

说明:TCP 协议是传输层协议,HTTP 是应用层协议,HTTP 协议是建立在 TCP 协议上的。还有个 IP 协议是网络层协议。TCP 协议建立在 IP 协议上。

OSI 模型把网络通信分为七层,层层递进。

问题解答#

为什么是三次握手而不是二次握手呢?

是为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。

假设二次握手成立,那么存在下面几种情况:

  • 情况一,客户端对服务器发起请求,服务器接收到请求后,发送确认建立连接的信息,连接建立完成。

  • 情况二,客户端对服务器发起请求,但是这个请求突然丢失了,服务器没有收到。之后,客户端又发送一条请求,服务器这次接收到请求了,发送确认建立连接的信息,连接建立完成。

  • 情况三,客户端对服务器发起请求,这个请求没有丢失,它在某个网络节点处滞留了,过了很久后,服务器才收到这个其实已经失效了的请求,但是服务器不知道这个请求失效了,还是向客户端发送确认建立连接的信息,于是连接建立完成。但是客户端现在并不需要发送请求啊,于是就不理睬服务器已经建立起来的连接,也不会向服务器发送任何请求数据。服务器就一直在等呀等,等着客户端发送请求数据过来,然而客户端现在压根就不想鸟他,因此服务器浪费了很多的资源。

如果是三次握手的情况下,就不会存在情况三了。在服务器发出连接确认的信息后,

如果没有收到客户端的确认信息,就不会建立连接了,不会傻傻的等着,浪费资源了。

半连接队列#

通过上面分析,我们知道三次握手的必要性,需要确认两次才会建立连接。

其实,在服务器收到客户端 SYN 报文后,会维护一个半连接队列,该队列为每个客户端的 SYN 包(syn=j)开设一个条目,该条目表明服务器已收到 SYN 报文,并向客户端发出确认,正在等待客户端的 ACK 报文。

如果等待一段时间后没有收到客户段的 ACK 报文,服务器会重新发送 SYN-ACK 报文给客户端,然后继续等待着。如果等待一段时候后还是没收到客户端的 ACK 报文,服务器会进行第二次重传 SYN-ACK 报文给客户端...

如此循环,注意:每次等待的时候不一定相同。直到达到服务器设置的最大重传次数之后,服务器会将连接信息从半连接队列中删除掉。这样的连接信息也叫做“半连接请求”。

SYN 攻击#

SYN 攻击利用 TCP 协议漏洞,通过发送大量半连接请求,耗费服务器 CPU 和内存资源。

通常,SYN 攻击者会在短时间内大量伪造不存在的 IP 地址,然后向服务器发送 SYN 包,服务器回复确认包,并等待客户端回复。但是源地址不存在,不可能回复服务器,服务器需要不停重发确认包直至超时,这些伪造的 SYN 包将长时间占用未连接队列,正常的 SYN 请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。

四次挥手#

过程#

因为不管是客户端还是服务端,都可以发起挥手动作,所以下面以主机 A 和主机 B 区分。

image.png

第一次挥手

主机 A 向主机 B 发送(FIN=1,seq=M)FIN 报文,请求关闭连接,然后进入FIN_WAIT_1状态。

第二次挥手

主机 B 收到 FIN 报文并确认后,向主机 A 发送(ACK=1,ack=M+1)ACK 报文,表示同意主机 A,这时,主机 B 进入CLOSE_WAIT状态。主机 A 收到 ACK 报文后,进入FIN_WAIT_2状态。

第三次挥手

主机 B 也向发送(FIN=1,seq=N)FIN 报文,也请求关闭连接,然后进入LAST_ACK状态。

第四次挥手

主机 A 收到 FIN 报文确认后,向主机 B 发送(ACK=1,ack=N+1)ACK 报文,表示同意主机 B,然后进入TIME_WAIT状态,经过 2MSL 之后,自动进入CLOSED状态,四次挥手结束。主机 B 收到 ACK 报文后,也进入CLOSED状态。

说明:MSL“Maximum Segment Lifetime”(最长报文段寿命),根据 RFC 793 建议该值为 2 分钟。

过程简析#

大白话讲就是:

  • 第一次挥手:主机 A 对主机 B 说:“喂,兄弟!我这边没有数据要给你了,我们关闭连接吧”
  • 第二次挥手:主机 B 回答主机 A 说:“嗯,了解,我现在也不需要你的数据了。不过我要先看看我这边还有没有数据要发送给你”。主机 A 回答主机 B 说:“收到”
  • 第三次挥手:主机 B 又对主机 A 说:“嗯,我这边也没有数据要给你了,我们关闭连接吧”
  • 第四次挥手:主机 A 回答主机 B 说:“好勒!同意!”。主机 B 听到主机 A 的回答后,话都没说,直接就把连接给关闭了。

主机 A 没有听到主机 B 说话,有点不放心,等了一段时间后(4 分钟),才把连接关闭了。

问题解答#

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

握手的时候,服务器收到 SYN 报文后,服务器在 LISTEN 状态下,可以直接一次性发送 ACK+SYN 报文出去。

因为 TCP 连接是全双工的(可以同时发送和接收),因此在关闭连接的时候,需要关闭发送和接受两个接口在能关闭连接。

在挥手的时候,发出 FIN 的主机 A 只是表示没有数据发送了,但是它还可以接受数据,所以不能马上关闭连接,要等到收到 FIN 的主机 B 确认也没有数据传出去的时候,才能关闭连接。通俗一点来讲,就是:主机 B 确认自己已经不需要主机 A 的数据后,发生 ACK 报文给主机 A,告诉主机 A,我不用接受你的东西了,你可以关闭发送接口了。

然后再确认自己所有的东西都已发送完毕,又发送 FIN 报文给主机 A,告诉主机 A,我也没有东西要发送给你了,你可以关闭接收接口了(此时,主机也想要关闭发送接口

综上所述,挥手需要四次。

为什么主机 A 第四次挥手的时候,需要经过 2MSL 后,才关闭接口?

因为网络是不可靠的,主机 A 第四次挥手发送的 ACK 报文无法保证主机 B 一定能收到,有可能丢失,所以主机 A 发送 ACK 报文后,进入TIME_WAIT的状态,这个状态的作用就是用来重发可能丢失的 ACK 报文。