画图带你理清TCP协议三次握手和四次挥手(三)
面试题:
1)两次握手可以吗??
不可以。
防止已失效的请求报文又传送到了服务端,建立了多余的链接,浪费资源
两次握手只能保证单向连接是通畅的 (为了实现可靠数据传输, TCP 协议的通信双方,都必须维护一个序列号,以标识发送出去的数据包中,哪些是已经被对方收到的;三次握手的过程即是通信双方相互告知序列号起始值,并确认对方已经收到了序列号起始值的必经步骤;如果只是两次握手,至多只有连接发起方的起始序列号能被确认,另一方选择的序列号则得不到确认)
2)为什么是三次??
主要是为了建立可靠的通信通道,保证客户端与服务端同时具备发送、接收数据的能力
.
3)四次握手可以吗??
可以,但没必要
四次握手可以验证双方的发送接收能力正常,但是这样做效率比较低
4.断开连接 - 四次挥手
三次握手: 双方各自向对方发起建立连接的请求,再各自给对方回应,只不过,中间的 SYN 和 ACK 能合并在一起
四次挥手: 双方各自向对方发起建立连接的请求,再各自给对方回应,只不过,中间的 FIN 和 ACK 不一定能合并在一起
仍以打电话为例,如下图:
TCP 中真实的断开连接过程:(假设主机 A 主动断开连接)
第一次挥手: 客户端向服务器端发送断开 TCP 连接请求的 [FIN,ACK] 报文,在报文中随机生成一个序列号 SEQ=u,表示要断开 TCP 连接
此时,客户端进入FIN_WAIT_1 (终止等待1) 状态
第二次挥手: 当服务器端收到客户端发来的断开 TCP 连接的请求后,回复发送 ACK 报文,表示已经收到断开请求。回复时,随机生成一个序列号 SEQ=v;由于回复的是客户端发来的请求,所以在客户端请求序列号 SEQ=u 的基础上加 1,得到 ack=u+1
此时,服务端就进入了CLOSE_WAIT (关闭等待) 状态,客户端收到ACK后,就进入FIN_WAIT_2 (终止等待2) 状态
第三次挥手: 服务器端在回复完客户端的 TCP 断开请求后,不会马上进行 TCP 连接的断开。服务器端会先确认断开前,所有传输到客户端的数据是否已经传输完毕。确认数据传输完毕后才进行断开,向客户端发送 [FIN,ACK] 报文,设置字段值为 1。再次随机生成一个序列号 SEQ=w;由于还是对客户端发来的 TCP 断开请求序列号 SEQ=x 进行回复,因此 ack 依然为 x+1
此时,服务器就进入了LAST_ACK (最后确认) 状态
第四次挥手: 客户端收到服务器发来的 TCP 断开连接数据包后将进行回复,表示收到断开 TCP 连接数据包。向服务器发送 ACK 报文,生成一个序列号 SEQ=u+1;由于回复的是服务器,所以 ACK 字段的值在服务器发来断开 TCP 连接请求序列号 SEQ=w 的基础上加 1,得到 ack=w+1
此时,客户端就进入了TIME_WAIT (时间等待) 状态;注意此时TCP连接还没有释放,必须经过2MSL (最长报文段寿命) 的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态
两个重要的状态:
CLOSE_WAIT: 表示在等待关闭;四次挥手挥了一半了,当前可能剩下的两次不挥了(接收方没调用 close 方法,就会导致四次挥手只挥两次,从而没有正确关闭连接)
TIME_WAIT: 谁主动断开连接,谁进入 TIME-WAIT 状态,此时该主机已经完成了四次挥手的过程,但仍不能立刻断开连接,而是要以 TIME-WAIT 状态来保持连接一段时间之后,再彻底释放连接 (处理最后一个 ACK 丢包之后重传的问题)
为了解决网络的丢包和网络不稳定所带来的其他问题,确保连接方能在时间范围内,关闭自己的连接
面试题:
1)四次挥手,三次挥完行不行?
通常情况下不行,若触发了延时应答机制,就可以三次挥完
"不行",即:上述的 ② ③ 为什么没有合并在一起??
因为中间两次操作的时机不一样
ACK 是收到 FIN 之后立刻由内核返回的数据报,FIN 是应用程序处理完接受缓冲区的数据之后,调用的 close 方法触发的
.
2)为什么四次?
因为要确保客户端和服务端的数据能够完成传输
3)为什么 TIME_WAIT 状态要等待 2MSL?
假设网络上传输数据的最大时间为 MSL
MSL 就是 ACK / FIN 从主机 A 到主机 B 的最大时间
TIME-WAIT 等待时间,需要分成两个部分:
①等待 ACK 经历一个最大时间到达主机 B
②万一 ACK 丢了,在等待一个最大时间,主机 B 重传 FIN 到达主机 A
因此,TIME_WAIT 就需要等待 2倍的MSL,即:2MSL
原因:
确保 ACK 报文能够到达服务端,从而使服务端正常关闭连接
第四次挥手时,客户端第四次挥手的 ACK 报文不一定会到达服务端;服务端会超时重传 FIN / ACK 报文,此时如果客户端已经断开了连接,那么就无法响应服务端的二次请求,这样服务端迟迟收不到 FIN / ACK 报文的确认,就无法正常断开连接
MSL 是报文段在网络上存活的最长时间,客户端等待 2MSL 时间,即「客户端 ACK 报文 1MSL 超时 + 服务端 FIN 报文 1MSL 传输」,就能够收到服务端重传的 FIN / ACK 报文,然后客户端重传一次 ACK 报文,并重新启动 2MSL 计时器;如此保证服务端能够正常关闭
如果服务端重发的 FIN 没有成功地在 2MSL 时间里传给客户端,服务端则会继续超时重试直到断开连接
防止已失效的连接请求报文段出现在之后的连接中
TCP 要求在 2MSL 内不使用相同的序列号;客户端在发送完最后一个 ACK 报文段后,再经过时间 2MSL,就可以保证本连接持续的时间内产生的所有报文段都从网络中消失;这样就可以使下一个连接中不会出现这种旧的连接请求报文段;或者即使收到这些过时的报文,也可以不处理它
文章转自公众号:三友的java日记