网络编程 TCP四次握手和常见问题的总结
(1):描述TCP四次握手的过程
第一次挥手,客户端发送一个FIN标志位为1的数据包给服务器,表面自己数据发送完毕,进入FIN_WAIT_1状态。
第二次挥手,服务端收到FIN后,回复一个ACK确认包给客户端,并进入CLOSE_WAIT状态,告诉客户端自己准备关闭连接。客户端接收到ack应答报文后,进入FIN_WAIT_2状态
第三次挥手,服务端数据发生完毕,准备好关闭连接,向客户端发送一个FIN标志位为1的数据包,进入LAST_ACK状态。
第四次挥手,客户端收到服务器的FIN后,发送ACK确认包给服务器,进入到TIME_WAIT状态,等到2MSL后关闭连接,服务器收到ACK后进入CLOSED状态,完成四次挥手。
四次挥手优化
当主动关闭方接收到ACK报文时,关闭方式有以下:
- close函数关闭:孤儿连接,若在tcp_fin_timeout秒内没有收到对方的FIN报文,则连接直接关闭,同时tcp_max_orphans定义了最大孤儿连接的数量,超过直接释放
- shutdown函数关闭
当TIME_WAIT状态过多时,可通过tcp_tw_reuse和tcp_timestamps为1,将TIME_WAIT状态的端口复用于作为客户端的新连接(只适用于客户端)。
(2):四次挥手过程当中,客户端和服务器各自的状态变化是什么?
客户端 FIN_WAIT_1 --> FIN_WAIT_2 --> TIME_WAIT --> CLOSED
服务器 CLSOE_WAIT --> LAST_ACK --> CLOSED
(3):什么是TCP半关闭(Half-close)状态。
半关闭状态发生在四次挥手的第二次挥手后,此时客户端不能发数据但是能接收服务端发来的数据,服务器处于CLOSE_WAIT状态
直到它处理数据完毕!
(4): TCP握手为什么是四次,不能像握手那样三次?
因为TCP是全双工通信,所以TCP不管是建立连接,还是断开连接,本身是要求四次的,只不过在建立连接的时候,没有额外的数据包,所以第2次和第3次握手合并为一次!断开连接的时候,因为中间TCP处于半关闭的状态,客户端还要继续接收服务端没有发送完的数据,所以第2次和第3次不能合并,就是四次!
(5):TIME_WAIT状态的作用是什么?
主要有两个作用:
第一个:保证双方能够正常关闭
如果没有TIME_WAIT状态的话,客户端最后一次发送ack应答报文后就进入了close阶段。如果服务端没有收到ack报文的话,就会重传FIN报文,而此时客户端已经close了,就会返回一个RST错误报文。
而有了TIME_WAIT状态的话,客户端并不会着急关闭,而是等待了2MSL再关闭,这样的话就算重传的FIN报文也能正确响应,保证了服务端的正确关闭。
这里客户端重新接收到了重发的FIN报文,time_wait就会重置(也是为了重复报文的消失,不然的话ack报文可能就会到下一次连接中了),相当于可以重传多次FIN报文,直到FIN报文上限。
这里也有说不同的操作系统方案不一样,有的不会重置time_wait,个人认为还是重置比较合理一点。
第二个:防止错误接收历史报文
假设没有TIME_WAIT状态,那么在关闭连接前有个报文被延迟了。关闭后,又重新建立了相同四元组的连接,此时前一个被延迟的数据包到达了(没有到达最大生存时间),并且正好在接收窗口内,那么服务端就会错误接收,使得数据混乱。
有了TIME_WAIT状态,由于新的连接必须在前一个连接关闭 2MSL之后才能再次发起,且前一个连接的延迟报文段在 TIME_WAIT状态的第 1个MSL里就已经消失,因此我们可以保证前一次连接的延迟报文段不会在新的连接中出现,也就不可能被误认为是第二次连接的报文段.
(6):什么是MSL?为什么客户端在发送完最后一个ACK后还需要等待2MSL?为什么是2MSL?
TCP协议的运行基于一个基本的假设:互联网上的每一个IP报文都有一个生存期限。 我们将这个生存期限定义为报文段的最大生存时间(MSL)
等待2MSL是主动关闭的一端为了应对(可能出现最后的FIN或ACK丢失导致的)超时重传,而进入TIME_WAIT阶段以备超时重传。最终目的是为了正常关闭全双工的连接
因为一个报文是MSL,如果出现了ACK丢失,那么重传FIN占用一个MSL,发送ACK占用一个MSL,加起来就是2MSL。
(7):如果在四次挥手过程中,最后的ACK丢失会发生什么?
服务器会重新发送FIN,客户端在TIME_WAIT期间会接收到这个重传的FIN,并重新发送ACK,确保关闭过程完成
(8):TIME_WAIT状态下,如果客户端立刻重启并尝试与同一服务器建立新连接,会发生什么?
可能会遇到问题,因为TIME_WAIT期间,相同的四元组(源IP、源端口、目的IP、目的端口)不能被重新使用。客户端需要等待2MSL后才能建立新连接,或者使用不同的源端口。