TCP协议 - 3 连接管理
TCP建立连接
- 建立连接前,客户端和服务端都处于
CLOSED
状态。 - 先是服务端主动监听某个端口,处于
LISTEN
状态。 - 客户端主动发起第一次握手,之后处于
SYN-SENT
状态(SYN = 1, seq = x, ACK = 0, ack = 0
)。 - 服务端收到
SYN
报文后,返回SYN
+ACK
报文,之后处于SYN-RCVD
状态(SYN = 1, seq = y, ACK = 1, ack = x + 1
)。 - 客户端收到
SYN
+ACK
之后,返回ACK
报文,之后处于ESTABLISHED
状态(SYN = 0, seq = x + 1, ACK = 1, ack = y + 1
)。 - 服务端收到
ACK
之后,处于ESTABLISHED
状态。
TCP断开连接
- 断开连接前,客户端和服务端都处于
ESTABLISHED
状态。 - 客户端主动发起FIN报文,并停止再发送数据,之后处于
FIN-WAIT-1
状态(FIN = 1, seq = u
)。 - 服务端收到
FIN
报文后立即发出ACK
,然后进入CLOSE-WAIT
状态,此时TCP连接处于半关闭状态(seq = v, ACK = 1, ack = u + 1
)。 - 此时从服务端到客户端这个方向的连接并未关闭,服务端仍可以发送数据到客户端。
- 客户端收到
ACK
报文后,进入FIN-WAIT-2
状态,等待服务端发出的FIN
报文。 - 当服务端发送数据结束后,向客户端发送
FIN
报文,然后进入LAST-ACK
状态(seq = w, ACK = 1, ack = u + 1
)。 - 客户端收到
FIN
报文后,向服务端发送ACK
报文,然后进入TIME-WAIT
状态。此时TCP连接并没有释放,必须经过2MSL后,客户端会CLOSED
(seq = u + 1, ACK = 1, ack = w + 1
)。 - 服务端收到
ACK
报文后,进入CLOSED
状态。
对三次握手的理解
- 为了实现可靠数据传输,TCP协议的通信双方都必须维护一个序列号,以标识发送出去的数据包中,哪些是已经被对方收到的。
- 三次握手的过程即是通信双方相互告知序列号起始值,并且要确认对方已经收到了序列号起始值。
- 确认号的值是告诉发送端自己期待收到的下一个序号,虽然SYN报文不携带数据,但是也要消耗一个序列号,所以ACK报文都要+1。
- 第一次握手,是客户端告诉服务端的序列号起始值。
- 第二次握手,是服务端确认收到了客户端的序列号起始值,并且告诉客户端自己的序列号起始值。
- 第三次握手,是客户端确认收到可服务端的序列号起始值。
对四次挥手的理解
- FIN报文就是告诉对方,自己没有数据可以发送了。
- 在建立连接的三次握手中,服务端回复的SYN+ACK是一个合并包,因为它在接收到客户端SYN后,可以立即进行自己的SYN并发送ACK。而在四次挥手中,第二次的ACK和第三次的FIN并不能合并。因为服务端收到客户端的FIN后,它可能还有数据没有发送完毕。它会先回复ACK来确认收到关闭请求,然后继续发送数据。直到数据全部发送完毕后,它才会发出自己的FIN包。
- 在某些特殊情况下,如果服务端在收到客户端的FIN时,自己的数据也已经发送完毕,那么它可以将ACK和FIN合并在一起发送,这样就会变成三次挥手。但这只是一种特殊优化,标准的关闭流程依然是四次。
- 第一次挥手,客户端告诉服务端自己已经没有数据可以发送。
- 第二次挥手,服务端确认了客户端的关闭请求。
- 第三次挥手,服务端告诉客户端自己已经没有数据可以发送。
- 第四次挥手,客户端确认了服务端的关闭请求。
2MSL的作用
MSL(Maximum Segment Lifetime)是任何数据包在网络中的最大存活时间。RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。
-
保证最后一个ACK的可靠到达
主动关闭连接的一方发送完最后一个ACK报文后,如果这个ACK报文丢失,接收方(被动关闭方)会超时重发FIN报文。2MSL的时间足够让接收方重发的FIN报文到达发送方,并让发送方能够再次发送ACK报文进行确认,从而保证连接的正常关闭。
-
避免旧连接的报文段干扰新连接
在网络中,TCP报文段可能会因为各种原因延迟到达。如果主动关闭方不等待2MSL就立即关闭连接,那么迟到的旧报文段可能会被误认为是新连接的报文段,导致数据错误或连接混乱。2MSL的等待时间确保了这些旧报文段在网络中消失,避免了干扰。
TIME_WAIT
状态正是为了解决这个问题:- 在这段时间内,该端口不能被新的连接使用。
- 等待2个MSL,可以确保网络中所有与该旧连接相关的数据包(包括丢失的
ACK
和重传的FIN
)都彻底消失。