[1]. 阿里二面,面试官居然把 TCP 三次握手问的这么细致
[2]. TCP3次握手和4次挥手详解
主要要能够验证客服端与服务段的收发能力均为正常,则必须要有三次
(一)确认双方的收发能力
TCP 建立连接之前,需要确认客户端与服务器双方的收包和发包的能力。
第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
所以,只有三次握手才能确认双方的接收与发送能力是否正常。
(二)序列号可靠同步
如果是两次握手,服务端无法确定客户端是否已经接收到了自己发送的初始序列号,如果第二次握手报文丢失,那么客户端就无法知道服务端的初始序列号,那 TCP 的可靠性就无从谈起。
(三)阻止重复历史连接的初始化
客户端由于某种原因发送了两个不同序号的 SYN 包,我们知道网络环境是复杂的,旧的数据包有可能先到达服务器。如果是两次握手,服务器收到旧的 SYN 就会立刻建立连接,那么会造成网络异常。
如果是三次握手,服务器需要回复 SYN+ACK 包,客户端会对比应答的序号,如果发现是旧的报文,就会给服务器发 RST 报文,直到正常的 SYN 到达服务器后才正常建立连接。
所以三次握手才有足够的上下文信息来判断当前连接是否是历史连接。
(四)安全问题
我们知道 TCP 新建连接时,内核会为连接分配一系列的内存资源,如果采用两次握手,就建立连接,那会放大 DDOS 攻击的。
第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。
我们可以思考一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,疯狂着重复发 SYN 报文,这会让服务器花费大量的内存空间来缓存这些报文,这样服务器就更容易被攻击了。
对于第三次握手,此时客户端已经处于连接状态,他已经知道服务器的接收、发送能力是正常的了,所以可以携带数据是情理之中
当客户机处于ESTABLISHED状态时,TCP客户机就能发送报文段和接收包含有效载荷(即应用层产生的数据)的TCP报文段。
客户机建立好连接和服务器建立好连接是有时间差的,在连接的过程中,第一个报文段和第二个报文段是不用担心丢失的问题,因为这两个报文段有确认应答和超时重传机制,可以确保前两个报文段对方收到,但是,当客户机发送第三个报文段的时候,是不能保证服务器能够接收到该报文段的,因为第三个报文段没有应答的报文,所以当第三个报文段丢失时,此时就只有客户机建立好了连接 (创建接收缓冲区和发送缓冲区资源),由于服务器在一段时间内没有收到ACK,那么服务器此时就超时了,就会給客户机发送一个RST报文段給客户端,断开连接,进入CLOSED状态,让客户端重新发起连接请求。(同样,如果接收到超过服务端在第二次握手时发送seq的ack,则会认为是第三次握手包丢失,将发送RST报文)。
只有established建立了以后,才会创建接收缓冲区和发送缓冲区资源,如果服务器没有收到第三个握手包,在还没有建立上述两个缓冲区资源的情况下收到超过服务器第二次握手时ack的数据包,将会发送RST要求重新建立连接。
当我们的应用程序不需要数据通信了,就会发起断开 TCP 连接。建立一个连接需要三次握手,而终止一个连接需要经过四次挥手。
问:为什么建立连接握手三次,关闭连接时需要是四次呢?
答:其实在 TCP 握手的时候,接收端发送 SYN+ACK 的包是将一个 ACK 和一个 SYN 合并到一个包中,所以减少了一次包的发送,三次完成握手。
对于四次挥手,因为 TCP 是全双工通信,在主动关闭方发送 FIN 包后,接收端可能还要发送数据,不能立即关闭服务器端到客户端的数据通道,所以也就不能将服务器端的 FIN 包与对客户端的 ACK 包合并发送,只能先确认 ACK,然后服务器待无需发送数据时再发送 FIN 包,所以四次挥手时必须是四次数据包的交互。
问:为什么TIME_WAIT 状态需要经过 2MSL 才能返回到 CLOSE 状态?
答:MSL 指的是报文在网络中最大生存时间。在客户端发送对服务器端的 FIN 的确认包 ACK 后,这个 ACK 包是有可能不可达的,服务器端如果收不到 ACK 的话需要重新发送 FIN 包。
所以客户端发送 ACK 后需要留出 2MSL 时间(ACK 到达服务器 + 服务器发送 FIN 重传包,一来一回)等待确认服务器端确实收到了 ACK 包。
也就是说客户端如果等待 2MSL 时间也没有收到服务器端的重传包 FIN,说明可以确认服务器已经收到客户端发送的 ACK。
还有第 2 个理由,避免新旧连接混淆。
在客户端发送完最后一个 ACK 报文段后,在经过 2MSL 时间,就可以使本连接持续的时间内所产生的所有报文都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文。
你要知道,有些自作主张的路由器会缓存 IP 数据包,如果连接重用了,那么这些延迟收到的包就有可能会跟新连接混在一起。