可以看到双方都主动发起断开请求所以各自都是主动发起方,状态会从 FIN_WAIT_1 都进入到 CLOSING 这个过度状态然后再到 TIME_WAIT。
挥手一定需要四次吗?假设 client 已经没有数据发送给 server 了,所以它发送 FIN 给 server 表明自己数据发完了,不再发了,如果这时候 server 还是有数据要发送给 client 那么它就是先回复 ack ,然后继续发送数据。
等 server 数据发送完了之后再向 client 发送 FIN 表明它也发完了,然后等 client 的 ACK 这种情况下就会有四次挥手。
那么假设 client 发送 FIN 给 server 的时候 server 也没数据给 client,那么 server 就可以将 ACK 和它的 FIN 一起发给client ,然后等待 client 的 ACK,这样不就三次挥手了?
为什么要有 TIME_WAIT?断开连接发起方在接受到接受方的 FIN 并回复 ACK 之后并没有直接进入 CLOSED 状态,而是进行了一波等待,等待时间为 2MSL。
MSL 是 Maximum Segment Lifetime,即报文最长生存时间,RFC 793 定义的 MSL 时间是 2 分钟,Linux 实际实现是 30s,那么 2MSL 是一分钟。
那么为什么要等 2MSL 呢?
就是怕被动关闭方没有收到最后的 ACK,如果被动方由于网络原因没有到,那么它会再次发送 FIN, 此时如果主动关闭方已经 CLOSED 那就傻了,因此等一会儿。
假设立马断开连接,但是又重用了这个连接,就是五元组完全一致,并且序号还在合适的范围内,虽然概率很低但理论上也有可能,那么新的连接会被已关闭连接链路上的一些残留数据干扰,因此给予一定的时间来处理一些残留数据。
等待 2MSL 会产生什么问题?如果服务器主动关闭大量的连接,那么会出现大量的资源占用,需要等到 2MSL 才会释放资源。
如果是客户端主动关闭大量的连接,那么在 2MSL 里面那些端口都是被占用的,端口只有 65535 个,如果端口耗尽了就无法发起送的连接了,不过我觉得这个概率很低,这么多端口你这是要建立多少个连接?
如何解决 2MSL 产生的问题?快速回收,即不等 2MSL 就回收, Linux 的参数是 tcp_tw_recycle,还有 tcp_timestamps 不过默认是打开的。
其实上面我们已经分析过为什么需要等 2MSL,所以如果等待时间果断就是出现上面说的那些问题。
所以不建议开启,而且 Linux 4.12 版本后已经咔擦了这个参数了。
前不久刚有位朋友在群里就提到了这玩意。
一问果然有 NAT 的身影。
现象就是请求端请求服务器的静态资源偶尔会出现 20-60 秒左右才会有响应的情况,从抓包看请求端连续三个 SYN 都没有回应。
比如你在学校,对外可能就一个公网 IP,然后开启了 tcp_tw_recycle(tcp_timestamps 也是打开的情况下),在 60 秒内对于同源 IP 的连接请求中 timestamp 必须是递增的,不然认为其是过期的数据包就会丢弃。
学校这么多机器,你无法保证时间戳是一致的,因此就会出问题。
所以这玩意不推荐使用。
重用,即开启 tcp_tw_reuse 当然也是需要 tcp_timestamps 的。
这里有个重点,tcp_tw_reuse 是用在连接发起方的,而我们的服务端基本上是连接被动接收方。
tcp_tw_reuse 是发起新连接的时候,可以复用超过 1s 的处于 TIME_WAIT 状态的连接,所以它压根没有减少我们服务端的压力。
它重用的是发起方处于 TIME_WAIT 的连接。
这里还有一个 SO_REUSEADDR ,这玩意有人会和 tcp_tw_reuse 混为一谈,首先 tcp_tw_reuse 是内核选项而 SO_REUSEADDR 是用户态选项。
然后 SO_REUSEADDR 主要用在你启动服务的时候,如果此时的端口被占用了并且这个连接处于 TIME_WAIT 状态,那么你可以重用这个端口,如果不是 TIME_WAIT,那就是给你个 Address already in use。
所以这两个玩意好像都不行,而且 tcp_tw_reuse 和tcp_tw_recycle,其实是违反 TCP 协议的,说好的等我到天荒地老,你却偷偷放了手?