TCP在建立连接时,客户端首先发送一个带有SYN标志的数据包,这个SYN包中会有一个序列号,用于标识数据流的起始位置。服务器接收到SYN包后,会回传一个ACK包,这个ACK包会有确认号,告诉客户端服务器期待从哪个序列号开始接收数据。同时,服务器还会再发送一个SYN包给客户端,确认双方的连接请求。客户端收到服务器的SYN包后,就会再发回一个ACK包给服务器。这就完成了TCP的三次握手过程。 在TCP连接建立的过程中,客户端和服务器会有不同的状态:CLOSED表示两端都处于等待状态,LISTEN表示服务器端正在等待客户端的连接请求,SYN-SENT表示客户端已经发送了SYN包并等待回应,SYN-RCVD表示服务器端已经收到了客户端的SYN包并准备回传ACK包,ESTABLISHED表示双方已经建立了可靠的连接,可以开始传输数据了。 为什么TCP不能“缩水”成两次握手呢?原因是因为两次握手会存在安全隐患和性能问题。如果攻击者恶意发送大量伪造的SYN包给服务器,那么服务器就会不断地回传ACK包,最终导致服务器资源耗尽。此外,如果在第二次握手时允许携带应用层数据,那么攻击者就可以利用这个机会把垃圾数据灌进来。因此为了安全起见,TCP规定前两次握手只能用于确认连接请求和序列号同步。 而第三次握手时就可以携带应用层数据了。这时候客户端已经进入ESTABLISHED状态,可以愉快地传文件了。第三次握手时客户端把ACK包和应用层数据一起发给服务器。这样就节省了一次往返时延和带宽资源。 为什么偏偏是三次而不是两次或者四次呢?因为两次握手解决不了历史遗留问题。比如客户端因为网络拥堵导致发送给服务器的SYN包迟到了一段时间,而服务器刚好在这段时间内收到了一个ACK包并进行了处理。因为只有两次握手,客户端就无法判断这个ACK包是给之前迟到的SYN包还是当前发送给它的新连接请求。这样就会导致客户端误判并再次尝试连接相同端口,造成资源浪费和系统崩溃。 三次握手则能够完美解决这个问题。第一次和第二次握手分别完成了客户端和服务器端的序列号同步;第三次再确认一遍双方对表成功后才会进入ESTABLISHED状态。这样就能保证双方都能正确接收对方发送过来的数据。 而四次握手则显得过于繁琐且浪费资源。四次握手虽然可以增加一次确认机会,但会浪费更多的往返时延和带宽资源。因此TCP世界讲究“能省则省”,所以四次方案直接被淘汰掉了。 总结起来就是:两次太少容易出现误认问题;四次太多纯粹是浪费资源;三次刚刚好既避免了历史连接堆积如山又确保了序列号同步还能省掉无谓资源消耗——这就是TCP坚持了三十余年的“握手礼仪”。下次再看到“三次握手”别急着吐槽——它不是流程繁琐而是恰到好处的可靠保障。