image.png
四次挥手即终止 TCP 连接,就是指断开一个 TCP 连接时,需要客户端和服务端总共发 送 4 个包以确认连接的断开。在 socket 编程中,这一过程由客户端或服务端任一方执行 close 来触发。
由于 TCP 连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当甲方完成数据发送任务后,发送一个 FIN 给乙方来终止这一方向的连接,乙方收到一个 FIN 只是 意味着不会再收到甲方数据了,但是乙方依然可以给甲方发送据,直到这乙方也发送了 FIN 给甲方。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。

由此可见,TCP 建立一个连接需 3 个分节,终止一个连接则需 4 个分节。
(1)某个应用进程首先调用 close,我们称该端执行主动关闭(active close)。该端的 TCP 于是发送一个 FIN 分节,表示数据发送完毕,应用进程进入 FIN-WAIT-1(终止等待 1)状态。
(2)接收到这个 FIN 的对端执行被动关闭(passive close),发出确认报文。这个 FIN 由 TCP 确认。因为 FIN 的接收意味着接收端应用进程在相应连接上再无额外数据可接收。接收端进 入了 CLOSE-WAIT(关闭等待)状态,这时候处于半关闭状态,即主动关闭端已经没有数据 要发送了,但是被动关闭端若发送数据,主动关闭端依然要接受。这个状态还要持续一段时间,也就是整个 CLOSE-WAIT 状态持续的时间。主动关闭端收到确认报文后进入 FIN-WAIT-2 (终止等待 2)状态。
(3)一段时间后,被动关闭的应用进程将调用 close 关闭它的套接字。这导致它的 TCP 也 发送一个 FIN。
(4)接收这个最终 FIN 的原发送端 TCP(即执行主动关闭的那一端)确认这个 FIN 发出一 个确认 ACK 报文,并进入了 TIME-WAIT(时间等待)状态。注意此时 TCP 连接还没有释放, 必须经过 2∗MSL(最长报文段寿命/最长分节生命期 max segement lifetime,MSL 是任何 IP 数据报能够在因特网中存活的最长时间,任何 TCP 实现都必须为 MSL 选择一个值。RFC 1122[Braden 1989]的建议值是 2 分钟,不过源自 Berkelcy 的实现传统上改用 30 秒这个值。 这意味着 TIME_WAIT 状态的持续时间在 1 分钟到 4 分钟之间)的时间后,当主动关闭端撤 销相应的 TCB 后,才进入 CLOSED 状态。(5) 被动关闭端只要收到了客户端发出的确认,立即进入 CLOSED 状态。同样,撤销 TCB 后,就结束了这次的 TCP 连接。可以看到,被动关闭端结束 TCP 连接的时间要比主动关闭 端早一些。
既然每个方向都需要一个 FIN 和一个 ACK,因此通常需要 4 个分节。我们使用限定词“通 常”是因为:某些情形下步骤 1 的 FIN 随数据一起发送;另外,步骤 2 和步骤 3 发送的分节 都出自执行被动关闭那-一端,有可能被合并成一个分节。

为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

为什么 TCP 的挥手需要四次?

:::tips 因为全双工, 发送方和接收方都需要FIN报文和ACK报文 ::: TCP 是全双工的连接,必须两端同时关闭连接,连接才算真正关闭。
如果一方已经准备关闭写,但是它还可以读另一方发送的数据。发送给 FIN 结束报文给对方,对方收到后,回复 ACK 报文。当这方也已经写完了准备关闭,发送 FIN 报文,对方回复 ACK。两端都关闭,TCP 连接正常关闭。

为什么需要 TIME-WAIT 状态?

TIME_WAIT 状态存在的原因有两点

  1. 可靠的终止 TCP 连接。(等待一段时间用来确认服务器没有重发FIN报文)
  2. 保证让迟来的 TCP 报文有足够的时间被识别并**丢弃**。(让迟到的报文在网络中消失,避免因为IP和端口号相同而传递给别的程序)

根据前面的四次握手的描述,我们知道,客户端收到服务器的连接释放的 FIN 报文后, 必须发出确认。如最后这个 ACK 确认报文丢失,那么服务器没有收到这个 ACK 确认报文, 就要重发 FIN 连接释放报文,客户端要在某个状态等待这个 FIN 连接释放报文段然后回复确认报文段,这样才能可靠的终止 TCP 连接。
在 Linux 系统上,一个 TCP 端口不能被同时打开多次,当一个 TCP 连接处于 TIME_WAIT状态时,我们无法使用该链接的端口来建立一个新连接。反过来思考,如果不存在 TIME_WAIT 状态,则应用程序能过立即建立一个和刚关闭的连接相似的连接(这里的相似,是指他们具 有相同的 IP 地址和端口号)。这个新的、和原来相似的连接被称为原来连接的化身。新的化身可能受到属于原来连接携带应用程序数据的 TCP 报文段(迟到的报文段),这显然是不该发生的。这是 TIME_WAIT 状态存在的第二个原因。

服务器出现大量CLOSE_ WAIT状态的原因

原因:对方关闭socket连接,我方忙于读或写, 没有及时关闭连接

  • 检查代码,特别是释放资源的代码
  • 检查配置,特别是处理请求的线程配置 ```bash netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’

netstat -nat |wc -l ``` image.png