image-20220313035602395.png
这里需要注意,RFC793明确规定,除了第一个握手报文SYN除外,其它所有报文必须将ACK = 1。上图第一次挥手和第三次挥手省略了这两个部分,单凭ACK是不能确定成功接收的,还需要有ack码来确认成功接收多少数据。

为什么要四次挥手,三次挥手行不行?

第一次挥手是客户端主动发起的断开连接请求,第二次挥手服务端回复一个ACK代表同意客户端断开到服务端的连接;同意归同意,服务端可能还有数据没发完,这时候需要有application决定是否断开服务端到客户端的连接,如果只是三次挥手,那么这些还没发送的数据就丢失了,因此需要等服务端把要发的数据全部发送完毕,再进行第三次挥手,发送FIN代表我这边也可以了,接着第四次挥手客户端回复ACK,服务端收到之后就可以关闭连接了。
三次挥手的情况可能也会存在,主要原因是第二和第三次挥手合并了,也即是application没有数据发送了,可以直接FIN-ACK联合发送过去(延迟确认)。

为什么在Time-Wait阶段需要等待2MSL?

首先明确,MSL是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
那么等待2MSL有两个主要原因,一个是客户端发送ACK之后并不确定对端是否接受到了,客户端发送的包可能会丢失,如果ACK丢失或者是延迟了,那么服务端就会重发一个FIN包,这时客户端的计时器会重新计时2MSL;如果没有丢失他需要等待多少时间才能确定自己的包成功发送了呢?这里就有个等待的时间研判,2MSL代表的是数据包一来一回的时间(去向ACK消息最大存活时间 + 来向FIN消息的最大存活时间),如果数据包没丢失,那么在2MSL时间内是不会再收到服务端的重发包的,这时客户端就可以安心关闭连接了;
另一个原因,在数据传输的过程中,可能会有重发的数据包还在网络中游离,这时候等待一段时间可以让那些游离的数据包被接受-丢弃,而不会影响下一次同IP-同端口的连接。

Time-Wait过多会造成什么问题?

一个是内存占用过多,一个是端口资源消耗过多。首先端口资源是有限的,如果一直持续在Time-Wait阶段,那么连接无法释放,端口也就无法被复用,这样的连接多了,势必造成端口耗尽的危险。其次,服务端监听的端口确实只有一个,但是来新的连接会创建其他端口连接,如果这些连接一直保持在Time-Wait阶段,那么势必造成资源的耗尽,无法处理其他连接资源。

如果客户端没有四次挥手就断开了,服务端怎么办?

TCP利用计时器+超时重试实现了保活机制,服务端会维护一个计时器,每当收到客户端的包时就重新计数,如果达到计数阈值(Linux一般为2小时),就会触发超时重试,每75秒发送一个探测报文包(一般会很小),如果重发十次都没有收到回复,那么服务端会研判客户端已经异常断开了,这时候他就会断开他们的连接。