文章目录
三次握手建立连接
四次挥手断开连接
注意:TCP是一个有状态的协议,每个状态表示只能进行特定的事情
比如:
- 发送了FIN请求进入FIN_WAIT1状态表示不再发送数据了,如果这时候继续send发送数据就会报错。
- 如果服务端处于SYN_RCVD,表示等待一个ACK回复,但如果收到的是一个SYN连接请求,则认为连接异常,会发送RST要求客户端重置连接
常见面试题
1. 建立连接握手为什么是三次?
tcp是面向连接的,通信两端都必须确认对方具有数据收发能力。
四次没必要,两次不安全
两次握手只能保证一方具有收发数据的能力,有可能客户端上来发送一个SYN请求就挂了,所以不安全。必须进行三次握手保证双方都具备数据收发能力。
四次没必要,因为可以在第二次将ACK回应和SYN请求一次发送,多发送一次没必要。
底层的话看可以看看下面那个链接,那里更详细~
2. 断开连接挥手为什么是四次?
主动关闭方发送FIN只能表示自己不再发送数据,而自己还可以接收数据。
而被动关闭方收到断开连接请求后,还可以发送数据。只有被动关闭方确认自己也不会再发送数据了(也就是调用了shutdown或close),才会给主动关闭方发送FIN。因此是四次挥手
因为TCP是全双工的,必须两边都关闭连接才算彻底断开连接,所以是四次挥手
想了解更完整答案的小伙伴请猛戳:吊打面试官 | BAT都在问-为什么TCP建立和释放连接需要三次握手和四次挥手
3. 主动关闭方进行最后一次回复后为什么会进入TIME_WAIT状态进行等待,而不是直接CLOSED释放资源?
我们先来想一个场景:
主动关闭方进行最后一次回复后,如果最后一次回复的ACK丢了,被动关闭方没收到ACK回复就会重新发送FIN包。
假如此时主动关闭方已经CLOSED释放资源了,释放资源之后这个地址信息就可以被新的socket连接使用。当新的连接请求连接时,对面还在等待最后一次ACK,他接收到SYN请求就会认为连接异常,这无疑对新连接很不友好。
综上,如果主动关闭方直接CLOSED,最后一次ACK丢了,重传的FIN会对新的socket连接造成影响。如果请求的是相同的服务器,(这时候服务器还在等待ACK,接收到SYN连接请求,就会认为连接异常,发送RST重置连接报文)
而主动关闭方最后一次回复后进入TIME_WAIT等待一段时间就是为了避免新启动一个socket使用相同的地址信息,但是收到原有连接的影响。
TIME_WAIT等待的时间是2MSL(报文最大生存周期),确保自己可以处理有可能重传的FIN,避免重传的FIN对新连接的影响。
两个MSL:
- 第一个MSL内自己的ACK可能到达,也可能丢失
- 第二个MSL内,如果ACK丢失,对方的FIN必然会到达(或者丢失)
要知道,TIME_WAIT更多的是为了保护客户端(因为服务端就算重启也会使用相同的地址信息,不然之前的用户全都访问不到了,gg)
4. 一台主机上出现大量CLOSE_WAIT状态的socket是什么原因?
CLOSE_WAIT是被动关闭方收到FIN进行回复之后进入的状态,会在调用shutdown或close后进入下一个状态LAST_ACK。
因此,一旦一台主机上出现大量CLOSE_WAIT状态的socket则表示你的程序可能在对方关闭连接的时候没有关闭套接字释放资源
这是一种编程上的错误,连接断开但是没有关闭套接字
5. 一台主机上出现大量TIME_WAIT状态的socket是什么原因?
TIME_WAIT是主动关闭方进行最后一次回复后进入的状态。
因此,一台主机上出现大量TIME_WAIT状态的socket,意味着这台主机上有大量的套接字主动关闭,常见于爬虫服务器。
解决方案:调整TIME_WAIT等待时间,也可以设置套接字选项——地址重用
TCP连接管理中的保活机制
通信双方长时间没有数据通信,则服务端会间隔向客户端发送保活探测数据包,连续多次没有回复之后认为连接断开。
Linux中默认设置:
保活时间:默认7200秒(2小时)
保活时间间隔:默认75秒
保活探测数:默认9次
这个时间可以通过套接字选项进行设置
连接断开对于编程的影响
- 连接断开,则recv返回0,不再阻塞。(所以当recv返回0时,则认为连接断开,可以关闭套接字)
- 连接断开,继续send发送数据会触发SIGPIPE异常,导致进程退出。(如果不想退出,可以自定义SIGPIPE信号处理)