三次握手与四次挥手
三次握手
状态位:
- SYN:请求连接。
- ACK:应答报文。
- RST:重新、中断连接。
- FIN:断开连接。
图文过程:
整体描述:
- 一开始,客户端和服务端都处于CLOSED(关闭)的状态。先是服务端主动监听某个端口,处于LISTEN(监听)状态。
- 然后客户端主动发起连接SYN,以后处于SYN_SENT(发送SYN)状态。
- 服务端收到发送的连接,返回SYN+ACK,之后处于SYN_RCVD状态。
- 客户端收到服务端发送的SYN+ACK,发送ACK,之后处于established(已建立)状态。
- 服务端收到ACK之后,也处于established(已建立)状态。
这里重点记住一下状态的变化,之前就一直没有特意关注,面试问到就蒙住了。
分析三次握手的原因:
-
防止旧的重复连接初始化造成混乱
主要是通过第三次握手:客户端通过上下文比较,判断自己期望收到的ACK Num是否是历史连接(序列号超时或者过期)。从而发送RST中断重新连接还是发送ACK连接建立。
-
同步双方初始序列号
-
防止建立多个冗余无效链接,造成不必要的资源浪费
三次握手期间的异常
第一次握手丢失
- 客户端发送SYN,进入SYN_SENT状态;迟迟收不到SYN_ACK,也就是第一次握手丢失,会触发超时重传机制;
- 客户端会重发SYN报文,每次超时重传时间是上次的2倍;达到最大重传次数后,任务响应就会断开TCP连接。
第二次握手丢失
服务端发送SYN-ACK进入SYN_RCVD状态,客户端迟迟没有收到SYN_ACK;会触发两端都超时重传机制;
包含两部分:
- ACK:客户端迟迟没有收到第二次握手的ACK报文,觉得可能是自己发送的SYN丢失,于是触发超时重传SYN报文;
- SYN:服务端迟迟没有收到第三次握手的ACK报文,觉得可能是自己发送的SYN丢失,于是触发超时重传SYN_ACK报文。
第三次握手丢失
- 客户端发送ACK,进入ESTABLISH状态,服务端迟迟没有收到ACK;会触发超时重传SYN_ACK报文。
四次挥手
图文过程:
整体描述:
- 客户端打算关闭连接,此时会发送一个FIN报文(TCP首部FIN标志位被置为1的报文),之后客户端进入FIN_WAIT_1状态。
- 服务端收到该报文后,就向客户端发送ACK应答报文,之后服务端进入CLOSE_WAIT状态。
- 客户端收到服务端的ACK应答报文,之后进入FIN_WAIT_2状态。
- 等待服务端处理完数据之后,服务端发送FIN报文,之后进入LAST_ACK状态。
- 客户端收到服务端的FIN报文之后,发送ACK应答报文,之后进入TIME_WAIT状态。
- 服务端收到ACK应答报文后,就进入了CLOSED状态,至此服务端完成了连接的关闭。
- 客户端在经过2MSL(报文最大存活时间),自动进入CLOSED状态图,至此客户端也完成了连接的关闭。
补充:
- 主动关闭连接的,才会有TIME_WAIT状态。因为需要等待另一方是否接受到ACK,如果没有接收到另一方就会一直处于LAST_ACK状态。之后2msl(已有的ACK发送时间+)等待另一方的重传FIN报文。
- 服务端通常需要等待完成数据的发送和处理,所以服务端的ACK、FIN一般都会分开发送,因此会比三次握手多了一次。
- 2MSL:是从客户端收到FIN后发送ACK开始计时的。
为什么需要TIME_WAIT?
-
防止旧链接的数据包
- 当关闭发送的报文被网络延迟了。等到这个TCP被复用后,被延迟的报文抵达并被接受这个过期的报文,造成数据错乱等严重的问题。
- 而经过2MSL时间之后,足以让两个方向的数据包被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接产生的。
-
保证连接的正常关闭
等待足够的时间以确保最后的ACK能够让被动关闭方接受,从而帮助其正常关闭。
- 服务端正常收到四次挥手的最后一次ACK报文,则服务端正常关闭连接。
- 服务端没有收到四次挥手的最后一次ACK报文,则会重发FIN关闭连接报文并等待新的ACK报文。
如果TIME_WAIT时间过短、或者没有,一旦最后的ACK报文丢失,客户端就直接进入关闭状态,而客户端则一直处于LAST_ACK状态。当重新连接的时候,客户端发送SYN请求报文之后,服务端会发送RST报文中断,连接会被终止。
hshuo的面试之路 文章被收录于专栏
作者目标是找到一份Java后端方向的工作 此专栏用来记录从Bilibili、书本、其他优质博客上面学习的内容 用于巩固、总结内容 主要包含Docker、Dubbo、Java基础、JUC、Maven、MySQL、Redis、SpringBoot、SpringCloud、数据结构、杂文、算法、计算机网络、操作系统、设计模式等相关内容