本篇博文参考以下文章
https://blog.csdn.net/qq_38950316/article/details/81087809
https://blog.csdn.net/qq_25948717/article/details/80382766
https://baijiahao.baidu.com/s?id=1654225744653405133&wfr=spider&for=pc
阅读本篇文章前,可以先了解一下客户端如何与服务端建立连接
讲个故事
老规矩,用一个比较形象的例子来描述一下四次挥手,帮助大家了解
网购(后续):
- 上回讲到付完款了,卖家安排发货去了,这个时候,你突然不想要商品了,于是便对商家说:不想要了,申请退款。
- 商家回复你:“好的”,然后赶紧去操作,处理你的退款申请。
- 处理完申请之后,告诉你:“退款已经原路返回了”。
- 你收到款之后对商家说:“好嘞,我已经收到款了”。
上面的红字就是四次挥手。
概念理解
数缺图时少直观,图缺数时难入微,上图来看
先不考虑为什么挥手需要四次,我们先分析每一次挥手都进行了什么
第一次挥手
客户端:
- 客户端主动发出关闭请求,并发送“请求释放连接”标志位
FIN = 1
,附带序号seq = u
。 - 随后进入
FIN-WAIT-1
半关闭状态.(停止向服务端发送非确认报文,但是可以接受服务端传递过来的数据)
服务端:
- 准备被动关闭
- 通知应用进程
第二次挥手
服务端:
- 进入
CLOSE-WAIT
关闭等待阶段。 - 发送报文,表示“接收到客户端发送的释放连接请求”(ACK = 1),生成序列号 seq = v,确认号为 ack = u + 1(客户端发送来的序号 + 1)。
- 准备释放服务器到客户端上的链接。
客户端:
- 收到报文后,进入
FIN-WAIT-2
状态
第三次挥手
服务端:
- 服务端完成关闭准备,发送报文向客户端确认,是否真的要关闭
- 标记位
FIN = 1
,ACK
表示一切就绪,只等下令,生成序号seq = w
确认号为ack = u + 1
. - 结束
CLOSE-WAIT
,进入LAST-WAIT
阶段(停止向客户端发送数据,但是可以接收)。
客户端: - 接收到服务端的最后确认消息后,结束
FIN-WAIT-2
状态。
第四次挥手
客户端:
- 收到服务端最后确认的消息,随即进入
TIME-WAIT
时间等待阶段。 - 向服务端发送报文,高速服务器“接收到服务器确认关闭的信号了”
ACK
,发送序号seq = u + 1
(将上次挥手的确认号,作为本次序号),发送确认号ack = w + 1
(收到上次挥手的序号,+ 1 后作为确认号发送)。 TIME-WAIT
时间等待阶段持续2MSL(最大报文段生成时间)后,若没有收到服务端发送过来的消息,那么时间到后进行关闭处理。
服务端:
- 收到客户端发送来的报文后,立即进行关闭。
面试常见问题
为什么要等待2MSL?
MSL为最大报文生成时间,也就发送一次,接收一次所需要的最大时间,如果不等待,直接关闭,那么会存在以下情况
发送最后一次挥手的时候,如果报文丢失,服务端在三次挥手后超时没收到消息,就会重新发送,这时候客户端已经关闭了,无法接收消息,就会造成服务端一直在发请求,无法关闭。
等待2MSL是为了确保服务端收到了关闭确认的报文
为什么“握手”是三次,“挥手”却要四次?
- 服务器从关闭到启动阶段不需要进行准备,直接就可以开始。
- 但是服务器如果在启动阶段进行关闭,还有很多应用进程在运行,不能直接关了,得先去通知那些进程,我要关闭了,大家赶紧收拾。收拾完成后,再告诉客户端,我们准备好了,可以随时关闭了。
建立连接后,客户端出现故障怎么处理?
TCP设有保活计时器,客户端如果出现故障,长时间收不到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。