连接
简要过程
A:发送SYN请求 告诉B "我要连接了"
B:SYN、ACK ack = x+1,告诉A "哦 我知道了,你能听到我的话"
A:ACK 告诉B "可以听到!我要发数据了!!"
A在发送ACK后,进入ESTABLISHED,B在接受到ACK后进入ESTABLISHED
为什么这样设计
保持通信双方的信息对称,使通信双方处于同步状态
保证双方都能发数据,也能收数据。如果当初的TCP设计的是两次握手,
- A发送SYN,B接收到 返回对A请求的确认,自身进入ESTABLISHED状态,A收到,进入ESTABLISHED (ok)
- A发送SYN,B接收到 返回对A请求的确认,自身进入ESTABLISHED状态,[SYN ACK]由于网络原因,而停滞。A以为SYN发送失败,则重传一个SYN,此时B再返回[SYN ACK],A收到,双方都进入ESTABLISHED ,进行传输数据。假设此连接在通信结束后,释放连接资源,而此时第一次在网络停滞但未被丢弃的SYN包抵达了,B又进入ESTABLISHED,而此时A已经CLOSE,B一直没有释放连接资源,苦苦等待,因此浪费了资源。
假设四次握手的过程是这样:
- A->B SYN
- B->A ACK
- B->A SYN
- A->B ACK
中间二三步可以合并一起,B既确认A(表示能收),发ACK给B,试试能不能发,四次没有必要,三步就可以确保双方的收发,建立连接。
知乎上看到的:
三次握手这个说法不好,其实是双方各一次握手,各一次确认,其中一次握手和确认合并在一起
终止
简要过程
- A主动关闭方,B被动关闭方
- A放送[FIN] 一个中断请求给B,"我要关闭啦!!!"
- B收到FIN,发送ACK,告诉A,"我知道了你要关闭了,你还需要等我下,可能还有要发送的数据",通知应用程序。
- A收到 进入FIN_WAIT_1
- B进入CLOSE_WAIT状态(被动关闭),已经没有要发送的工作了,发送一个[FIN ACK],"我没什么要发的,可以关闭啦"
- A收到进入FIN_WAIT_2,此时也得发送一个ACK确认,"收到了你的关闭请求",进入TIME_WAIT状态,等待一个2MSL时间
- B收到了ACK,进入CLOSED,A在2MSL的时间段内没有收到FIN,确保最后一次ACK发送到B,这下放心地关闭了
为什么要这样设计
设置TIME_WAIT的原因
MSL就是maximum segment lifetime(最大分节生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间将在网络中消失。
假设最终的 ACK 丢失 , server 将重发 FIN , client 必须维护 TCP 状态信息以便可以重发最终的 ACK ,否则会发送RST ,结果 server 认为发生错误。
若要TCP可靠地终止连接的两个方向 ( 全双工关闭 ) , client 必须进 TIME_WAIT状态。
现在我们考虑终止连接时的被动方发送了一个FIN,然后主动方回复了一个ACK,然而这个ACK可能会丢失,这会造成被动方重发FIN,这个FIN可能会在互联网上存活MSL。
如果没有TIME_WAIT的话,假设连接1已经断开,然而其被动方最后重发的那个FIN(或者FIN之前发送的任何TCP分段)还在网络上,然而连接2重用了连接1的所有的5元素(源IP,目的IP,TCP,源端口,目的端口),刚刚将建立好连接,连接1迟到的FIN到达了,这个FIN将以比较低但是确实可能的概率终止掉连接2。
摘录他人
摘自知乎的一段
作者:车小胖
链接:https://www.zhihu.com/question/67013338/answer/248375813
TCP四次挥手也遵循相似的套路。
主动断开的一侧为A,被动断开的一侧为B。
- 第一个消息:A发FIN
- 第二个消息:B回复ACK
- 第三个消息:B发出FIN
此时此刻:B单方面认为自己与A达成了共识,即双方都同意关闭连接。此时,B能释放这个TCP连接占用的内存资源吗?不能,B一定要确保A收到自己的ACK、FIN。所以B需要静静地等待A的第四个消息的到来:
- 第四个消息:A发出ACK,用于确认收到B的FIN
当B接收到此消息,即认为双方达成了同步:双方都知道连接可以释放了,此时B可以安全地释放此TCP连接所占用的内存资源、端口号。
所以被动关闭的B无需任何wait time,直接释放资源。
但,A并不知道B是否接到自己的ACK,A是这么想的:
1)如果B没有收到自己的ACK,会超时重传FiN那么A再次接到重传的FIN,会再次发送ACK
2)如果B收到自己的ACK,也不会再发任何消息,包括ACK
无论是1还是2,A都需要等待,要取这两种情况等待时间的最大值,以应对最坏的情况发生,这个最坏情况是:
去向ACK消息最大存活时间(MSL) + 来向FIN消息的最大存活时间(MSL)。
这恰恰就是2MSL( Maximum Segment Life)。
等待2MSL时间,A就可以放心地释放TCP占用的资源、端口号,此时可以使用该端口号连接任何服务器。
为何一定要等2MSL?如果不等,释放的端口可能会重连刚断开的服务器端口,这样依然存活在网络里的老的TCP报文可能与新TCP连接报文冲突,造成数据冲突,为避免此种情况,需要耐心等待网络老的TCP连接的活跃报文全部死翘翘,2MSL时间可以满足这个需求(尽管非常保守)!