首先,这是一个每个面试官都会几乎提到的问题,那么这就问题来了,如何回答这个看似简单的问题呐?
先行阅读:
讲述原因和过程:
原因就是:
- 因为来的路(client->server)和去的路(server->client)可能不会是同一条路.我们需要确保两条路都是通的.
假设客户端发起连接
- 客户端 connect 函数发送 SYN ,进入 SYN_SNED 状态,处于 connect 中,这是第一次握手
- 服务器(原本LISTEN状态),发送SYN+ACK(前SYN+1),进入 SYN_RECV 状态,并将其放入 SYN 队列,这是第二次握手
- 客户端收到(SYN+ACK),进入 ESTABLISHED,connect函数返回,connect函数中完成了两次握手.发送 ACK 给服务器,这是第三次握手
- 服务器受到ACK,将其从SYN队列放入ACCEPT队列,这时accept函数返回,三次握手与accept完全无关,进入 ESTABLISHED
假设客户端主动关闭(原本状态自然都是ESTABLISHED):
- 客户端close函数发送FIN,进入 FIN_WAIT_1 状态
- 服务器收到FIN,只发送ACK,进入 CLOSE_WAIT 状态,这时 read 函数会返回0
- 客户端收到ACK,进入FIM_WAIT_2状态,之后等待服务端要发送的 FIN
- 服务器 close 函数发送 FIN,进入 LAST_ACK 状态.
- 客户端收到FIN,向服务器回复 ACK,进入TIME_WAIT状态.
- 服务器收到ACK,进入CLOSED状态
- 客户端持续 2MSL后进入CLOSED状态
如图:
提问:
- 为什么需要四次握手?
虚拟连接,分组交换,为了处理半连接(发送完毕”是指将数据写入操作系统的缓冲区,将由 TCP 协议栈负责数据的发送与重传,不代表对方已经收到数据。见陈硕的文章)
- 在第二步和第三步时,为什么ACK和FIN不同时发送,而是分步?
当客户端向服务器端发送FIN时,如果服务器端不作出回应,客户端会一直向服务器端发送FIN,所以一定要先做出ACK回应,再做响应的处理
然后服务器端再向客户端发送请求关闭的 FIN,同理,客户端结接收到之后再向服务器端做出ACK响应,服务器端收到ACK响应后就进行关闭。
- time_wait状态是干嘛的?为什么要持续2MSL后再关闭?
首先需要确定的是只有主动关闭的一方才会经过这个状态!!!
MSL :最长分节生命期
两个主要原因:
- 1.可靠的实现 TCP 全双工连接的终止
假设上图中的最后一个ACK丢失,服务器会重新发送FIN,因此客户端必须维持一种可以接受数据的状态.如果不维护,客户端会发送一个RST报文给服务器,服务器会认为这是一个错误!!!
- 2.允许老的重复分节在网络中消失
TCP必须防止来自某个连接的老的重复分组在该连接已经终止后再现的问题!!!
为什么需要2MSL 呐???因为:
- 服务器端向客户端发送FIN这步需要1MSL
- 客户端向服务器端发送ACK需要1MSL
这样就会让最后的FIN和ACK都是在网络上消失了的!!!
更深入请阅读:
关于三次握手和四次挥手,面试官想听到怎样的回答?
RST报文产生的三种情况:
作用就是通知对方关闭连接或者重新建立连接,不应该回应该报文
1. 访问不存在的端口/处于time_wait的端口
2. 异常终止连接
直接给对方发送一个RST,一旦发送,就会丢弃所有排队等待发送的数据
SO_LINGER
3. 处理半打开连接
什么是半打开连接?
- A端关闭/异常关闭了连接,B端由于其他原因没有收到结束的报文,还维持着原来的连接的状态
- B 端如果还写入数据,就会收到一个RST报文