TCP四次挥手全过程
有几点需要澄清:
1.首先,tcp四次挥手只有主动和被动方之分,没有客户端和服务端的概念
2.其次,发送报文段是tcp协议栈的行为,用户态调用close会陷入到内核态
3.再者,图中的情况前提是双方程序正常运行,程序在挥手过程中崩溃的情况后面会讲到
过程详解(时间顺序)
1.A程序调用close关闭连接,陷入到内核态,tcp协议栈发送fin报文段,此时A进入fin_wait1状态,等待B的ack回复
2.B的tcp协议栈接收到fin报文,回复ack,进入close_wait状态,等待B的用户态程序调用close
3.A的tcp协议栈接收到ack,进入半关闭状态(B->A的单向通信)
4.A进入半关闭的同时,进入fin_wait2状态,等待B发送fin报文
分析B程序的状态:A发送fin报文(将标志位fin置1),B的协议栈接收并判断为fin报文,向该条连接的tcp控制块的接收缓冲区(在内核)写入EOF结束符,B程序调用recv返回0,判断对方断开连接
5.B程序判断A请求断开连接,正常的逻辑是B也调用close断开连接
6.B调用close,陷入到内核态,tcp协议栈发送fin报文,B进入last_ack状态,等待A的ack
7.A收到B的fin报文,结束fin_wait2状态,回复ack,进入time_wait状态
8.B收到A的ack后,断开连接
9.A在time_wait规定的2MSL时间到期时断开连接
状态详解
1.close_wait状态:被动断开方发送ack回复主动方的fin后进入close_wait状态,因为对方已经请求断开连接,本端也要进入断开连接的流程,而断开连接是由用户态程序控制的,在这个状态下,等待程序调用close陷入到内核态,协议栈才能发送本方的fin报文
如果被动方程序一直不调用close,该tcp连接会处于半关闭状态
2.fin_wait1和fin_wait2:1是主动方发送fin后等待ack的状态,2是等待对方发送fin的状态
重点!
3.time_wait状态:主动断开连接方在发送ack回复被动方的fin后进入time_wait状态,这个状态的内容就是等待2MSL的时间,随后关闭连接。
MSL是最大报文生存时间,在传输中超过此时间的报文会失效
等待2MSL时间的原因:
a.在进入time_wait前,有延迟未到达A的报文,最晚会在1MSL后到达,而在A进入time_wait状态时,B已经发送fin,不会在time_wait后有新的来自B的报文,第一个MSL可以确保网络中所有滞留的报文正确A被接收
b.在A向B发送fin后进入time_wait状态,如果该fin丢失或超时导致B没有收到fin,B会重传一个fin报文,告诉A需要重发fin报文。
最坏情况:在B重传fin时,过去了1MSL,A最晚接收到该fin并重发ack的时间是在B重传fin的1MSL后
所以,第二个MSL保证了最坏情况下A可以重发第二个ack给B,以处理B没有收到第一次ack的情况,大部分情况下,A可以处理并重发B的多个重发的fin
time_wait状态通过等待2MSL确保了:
1.所有在网络中滞留的旧报文段在此期间会被自然丢弃。
2.确保重传机制有足够的时间处理任何潜在的重传报文,如FIN和ACK报文。
3.防止旧的报文段干扰未来的连接。
特殊情况
一、A处于time_wait状态时,当B没有接收到第一次的ack时重发fin,A会在2MSL内收到这个重发的fin并重发ack,如果B一直没收到ack怎么办?
Q:如果B没有接收到A重发的ack怎么办?此时A已经关闭连接,B不会再接收到ack,那么B该怎么关闭连接?
A:如果主机B没有收到主机A的ACK,它会继续按照TCP的重传机制定期重传FIN报文,直到重传次数达到设定的上限(通常是TCP实现中的一个参数,RFC 1122规定是最多重传4次,每次重传都会等待一个时间间隔。这个时间间隔通常会指数增长(即所谓的“指数退避”))
A在TIME_WAIT
状态下会处理这些重传的FIN报文,并每次都重新发送ACK报文(1-4个,最坏情况下只能重发一次ack)
B在重传次数耗尽之后,如果仍未收到ACK报文,它会认为连接已经关闭并释放资源
二、在四次挥手过程中发生:1.主动断开方的程序崩溃 2.被动断开方的程序崩溃
一言以蔽之:协议栈会关闭该程序所有套接字并发送RST(reset)报文给对方,表示连接非正常关闭,收到RST报文的一方会通过recv和errno获知
前置细节:
1.在关闭TCP连接时,如果操作系统检测到连接正在进行中(比如在ESTABLISHED
、FIN_WAIT
、CLOSE_WAIT
等状态),它会发送RST报文给对方,通知对方连接被异常终止
2.对端在接收到RST报文后,会在后续的网络操作中感知到连接被重置:
- 对端的系统调用错误
- 对端的应用程序如果尝试在接收到RST报文后继续进行网络操作,会在相应的系统调用中收到错误。
- 这些系统调用会返回错误,并设置
errno
。例如,如果对端调用recv
,会返回-1,并设置errno
为ECONNRESET
,表示连接被重置。
详细分析
1. 主动断开方的程序崩溃
情景:
- 主动断开方(主机A)发送了FIN报文,进入
FIN_WAIT_2
状态,等待被动断开方(主机B)发送FIN报文来完成连接的关闭。 - 主机A的程序在此时崩溃。
结果:
- 主机A的TCP连接关闭:
- 主机A的程序崩溃导致操作系统关闭该程序相关的所有套接字,包括处于
FIN_WAIT_2
状态的套接字。 - 主机A的TCP栈会发送RST(Reset)报文给主机B,表示连接已被非正常关闭。
- 主机A的程序崩溃导致操作系统关闭该程序相关的所有套接字,包括处于
- 主机B的处理:
- 主机B收到RST报文后,立即终止连接并释放相关资源。
- 任何在此连接上的未完成数据传输都将被终止,应用程序将收到错误通知,表明连接被重置。
2. 被动断开方的程序崩溃
情景:
- 被动断开方(主机B)在接收到主机A的FIN报文后,发送ACK并进入
CLOSE_WAIT
状态,等待应用程序调用close
以发送FIN报文。 - 主机B的程序在此时崩溃。
结果:
- 主机B的TCP连接关闭:
- 主机B的程序崩溃导致操作系统关闭该程序相关的所有套接字,包括处于
CLOSE_WAIT
状态的套接字。 - 主机B的TCP栈会发送RST(Reset)报文给主机A,表示连接已被非正常关闭。
- 主机B的程序崩溃导致操作系统关闭该程序相关的所有套接字,包括处于
- 主机A的处理:
- 主机A收到RST报文后,立即终止连接并释放相关资源。
- 主机A的应用程序会收到错误通知,表明连接被重置。
总结:
无论是主动断开方还是被动断开方的程序崩溃,最终结果都是TCP连接被非正常关闭,两者的TCP栈会发送RST报文给对方,通知对方连接已被重置。收到RST报文的一方会立即终止连接并释放相关资源。应用程序会收到相应的错误通知,表明连接被异常终止。
点击获取更多Linux C/C++开发学习资料