本文共 1849 字,大约阅读时间需要 6 分钟。
TCP是基于链接的,所以在传输数据前需要先建立链接,TCP在传输上是双工传输,不区分Client端与Server端,为了便于理解,我们把主动发起建连请求的一端称为Client端,把被动建立链接的一端称作Server端。
如下图,建连的时序是从上到下,左右两边的绿字代表Client端与Server端当时的链接状态。
首先建立链接前需要Server端先监听端口,因此Server端建立链接前的初始状态就是LISTEN状态,这时Client端准备建立链接,先发送一个SYN同步包,发送完同步包后,Client端的链接状态变成了SYN_SENT 状态。Server端收到SYN后,同意建立链接,会向Client端回复一个ACK。由于TCP是双工传输,Server端也会同时向Client端发送一个SYN,申请Server向Client方向建立链接。发送完ACK和SYN后,Server端的链接状态就变成了SYN_RCVD。
Client收到Server的ACK后,Client端的链接状态就变成了ESTABLISHED状态,同时Client向Server端发送ACK,回复Server端的SYN请求。
Server端收到Client的ACK后,Server端的链接状态也就变成了ESTABLISHED状态,此时链接完成,双方随时可以进行数据传输。
我们需要明白三次握手是为了建立双向链接,需要记住Client端和Server端的链接状态变化。另外回答建连的问题时,可以提到SYN洪水攻击发生的原因,就是Server端收到Client端的SYN请求后,发送了ACK和SYN,但是Client不进行回复,导致Server端大量的链接处在SYN_RCVD状态,进而影响其他正常请求的建连。可以设置tcp_synack_retries=0加快半链接的回收速度,或者调大tcp_max_syn_backlog来应对SYN洪水攻击。
再来看看 TCP 的断连,如下图所示。
TCP 链接的关闭,通信双方都可以先发起,我们暂且把先发起的一方看作 Client,从图中看出,通信中 Client 和 Server 两端的链接都是 ESTABLISHED 状态,然后 Client 先主动发起了关闭链接请求,Client 向 Server 发送了一个 FIN 包,表示 Client 端已经没有数据要发送了,然后 Client 进入了 FIN_WAIT_1 状态。Server 端收到 FIN 后,返回 ACK,然后进入 CLOSE_WAIT 状态。此时 Server 属于半关闭状态,因为此时 Client 向 Server 方向已经不会发送数据了,可是 Server 向 Client 端可能还有数据要发送。
Server 端收到 FIN 后,返回 ACK,然后进入 CLOSE_WAIT 状态。此时 Server 属于半关闭状态,因为此时 Client 向 Server 方向已经不会发送数据了,可是 Server 向 Client 端可能还有数据要发送。
Client 端收到 Server 端的 FIN 后,回复 ACK,然后进入 TIME_WAIT 状态。TIME_WAIT 状态下需要等待 2 倍的最大报文段生存时间,来保证链接的可靠关闭,之后才会进入 CLOSED 关闭状态。而 Server 端收到 ACK 后直接就进入 CLOSED 状态。
为什么需要等待 2 倍最大报文段生存时间之后再关闭链接,原因有两个:
1.保证 TCP 协议的全双工连接能够可靠关闭; 2.保证这次连接的重复数据段从网络中消失,防止端口被重用时可能产生数据混淆。从这个交互流程可以看出,无论是建连还是断链,都是需要在两个方向上进行,只不过建连时,Server 端的 SYN 和 ACK 合并为一次发送,而断链时,两个方向上数据发送停止的时间可能不同,所以不能合并发送 FIN 和 ACK。这就是建连三次握手而断链需要四次的原因。
另外回答断链的问题时,可以提到实际应用中有可能遇到大量 Socket 处在 TIME_WAIT 或者 CLOSE_WAIT 状态的问题。一般开启 tcp_tw_reuse 和 tcp_tw_recycle 能够加快 TIME-WAIT 的 Sockets 回收;而大量 CLOSE_WAIT 可能是被动关闭的一方存在代码 bug,没有正确关闭链接导致的。
转载地址:http://ekhgi.baihongyu.com/