核心机制:TCP 断开连接(四次挥手)
断开连接的四次挥手,可能是客户端主动发起的,也可能是服务端主动发起的
而三次握手,一定是客户端先发起的(倒果为因)(先发起的一方定义为客户端)
1.客户端告诉服务器,我要和你断开连接,请你把我删了
2.服务器回应"收到"
3.服务器告诉客户端,我也要和你断开连接,请你把我也删了
4.客户端回应"收到"
客户端和服务器都会把对方的信息(之前保留的对方的 IP 和 端口)删除掉,删除完了,连接就断开了
三次握手和四次挥手本质的区别在于四次挥手不是完全由操作系统内核完成的,而是和应用层代码有关系
四次挥手之所以叫四次挥手,是因为中间的两次交互,是不一定触发合并的(有的时候会合并,有的时候不会)
1.不合并
对于服务器来说,从 hasNext 返回 false 到调用 close 中间,其实要经历多少代码,多少时间,是不确定的!!! 看程序员的代码是怎么写的
2.合并
TCP 有机制:延时应答(后面会介绍),应答 ack 的时机,往后拖一段时间(不会很久)
比如,收到了 FIN 触发 close 之间,间隔的时间本身就不长,再加上上一个 ack 延时应答了,就可以把这两个数据合并了.
合并相比于不合并来说,更好,合并提高效率,一系列的封装和分用,都是需要有开销的
合并场景是属于 特殊情况 优化手段,还是把 TCP 的断开连接称为"四次挥手"
面试的经典面试题:
建立连接的时候,必须握手三次吗?
四次:可以但,是没有必要
中间两次的传输,时间是同时的,合并成一次,有利于提高效率(封装分用 2 次 > 1次)
两次:不可以
两次握手,无法完成"通信双发发送能力,接受能力的验证"
断开连接的时候,必须挥手 四次吗? 三次?
四次挥手的意义,就是为了"释放空间"
建立连接需要保存信息,连接不用了,保存的信息自然就要释放
CLOSE_WAIT:被动断开连接的一方进入的状态
收到对方发来的 FIN 的时候,就会返回 ACK,同时进入 CLOSE_WAIT 状态(可以理解成 wait close, 等待关闭,等待应用程序代码,调用 close),正常情况下,存在的时间比较少
TIME_WAIT:类似于线程 TIMED_WAITNG, 有时间的等待)
等待连接的释放
进入时机:主动发起断开连接的一方,进入
我方发送 FIN,对方返回 ACK
对方发送 FIN,我方返回 ACK ,同时进入 TIME_WAIT
按道理说,我方返回 ACK 相当于是四次挥手结束了,就可以释放连接了
实际上不能立即释放连接,而是要等等
等等,是为了应对最后一个 ACK 丢包的情况
TIME_WAIT
如果客户端没有等,直接释放连接,此时重传的 FIN 就无人处理了, 因此就需要客户端先等待一段时间,看这个时间里面,对方是否会重传 FIN
TIME_WAIT 等待时间,称为 2 * MSL
MSL 网络上任意两点之间,传输消耗的最大时间,实际上 MSL 给的值是非常宽裕的值,比如 Linux 上默认是60s
TCP 协议中的几个关键状态
LISTEN:手机开机,信号良好,随时可以打电话
ESTABLISHED:连接已经建立,电话接听,可以说话了
CLOSE_WAIT:等待代码调用 close
TIME_WAIT:等待 FIN 重传,应对最后一个 ACK 丢包的情况
面试非常高频的问题
实际上,三次握手,四次挥手,大部分工作都是内核来完成的,其实和实际代码关系不是很大存在的意义,更多的是让我们理解/调试程序
TCP 可靠传输,更多的还是靠前面的确认应答,超时重传