三次握手与四次挥手
三次握手和四次挥手是计算机网络中鼎鼎大名的面试题,今天主播也来回答下
三次握手
首先是握手的过程
1.客户端随机生成序列号,然后把序列号seq=x放进SYN报文,发送给服务端,客户端进入syn_sent阶段
2.服务端接收到SYN报文之后,确认ack=x+1,以及自己的seq=y,发送syn+ack报文,服务端进入syn_rcvd阶段
3.客户端收到syn+ack报文之后,将ack=y+1,发送ack报文,自身进入established阶段
4.服务端收到ack报文,也进入established阶段
tips:第三次握手是可以携带数据的,前两次不可以(因为通信能力和连接还未确定可靠,而第三次携带一些数据可以减少RTT次数,比如发送http请求)
那么为什么需要三次握手呢?
1. 确保双向通信能力
TCP 是全双工协议,允许数据在客户端和服务器之间双向传输。三次握手通过以下步骤验证双方的发送和接收能力:
- 第一次(SYN):客户端发送 SYN 包(seq=x),证明客户端能发送数据,服务器能接收。
- 第二次(SYN-ACK):服务器响应 SYN-ACK 包(seq=y, ack=x+1),证明服务器能接收客户端的包(通过 ack=x+1 确认),也能发送数据(通过 SYN 和 seq=y)。
- 第三次(ACK):客户端发送 ACK 包(ack=y+1),证明客户端能接收服务器的包,并确认服务器的发送能力。
三次交互确保双方都能发送和接收数据,建立可靠的双向通信通道。如果只有两次握手(SYN 和 SYN-ACK),客户端无法确认服务器是否正确接收了自己的确认,导致单向通信可能未完全验证。
2. 同步序列号
TCP 使用序列号(Sequence Number)和确认号(Acknowledgment Number)来跟踪数据段的顺序,确保数据不丢失、不重复、不乱序。三次握手用于同步双方的初始序列号(ISN,Initial Sequence Number):
- 客户端发送 seq=x(其初始序列号)。
- 服务器确认 ack=x+1,并发送自己的 seq=y。
- 客户端确认 ack=y+1。
通过三次交互,双方明确对方的初始序列号,并为后续数据传输建立基准。这防止数据错序或重复,尤其在网络不稳定时(如包丢失或延迟)。
为什么需要同步?
序列号是 TCP 可靠性的核心。例如,若客户端发送数据段 seq=1000,服务器需通过 ack=1001 确认收到。若序列号未同步,服务器可能误解数据位置,导致传输错误。
3.防止旧连接的干扰
由于拥塞问题,网络中可能存在延迟或者重复的旧连接,三次握手可以通过序列号和状态机管理防止建立重复建立连接
比如客户端向服务端发送syn报文由于延迟得不到ack回应,就多次发送syn报文
服务端可能率先接收到旧报文,就会发送syn+ack报文作为响应。ack=old_x,seq=y
客户端收到后发现这是旧连接,就发送rst报文表示中止这次连接。服务端就释放此次连接,等待新的syn报文送达
如果这里是两次握手,客户端无法向服务端说明这是旧连接,服务端每接受到一个syn报文,就得发送一个syn+ack报文,导致建立多个冗余无效链接,造成资源浪费
半连接队列(SYN队列)
服务端对于客户端发送的SYN报文,会建立一个连接,但因为没有完全建立,就存储在这个队列,方便后续可能的连接释放或者移入全连接队列(accept 队列)。也有针对syn队列的攻击
四次挥手
在挥手之前,双方都处于established状态,而且我们假设主动关闭方是客户端,被动关闭方是服务端
1.第一次挥手:客户端发送FIN报文,seq=x,表示无数据发送,通知服务端可以关闭了,客户端变为final_wait_1状态
2.第二次挥手:服务端接收到FIN报文,发送ack报文,ack=x+1,seq=y,服务端变为close_wait状态,继续进行最后的发送,客户端接收后变为final_wait_2状态
3.第三次挥手:服务端发送FIN报文,seq=y,ack=x+1,表示自己也没有数据发送,变为last_ack状态
4.第四次挥手:客户端接受到FIN报文后,发送ack报文,ack=y+1,客户端进入TIME_WAIT状态,并开始等待。服务端接收到ack报文后变为close状态。
客户端在等待2MSL之后,也进入close状态。
特点
- 双向关闭:每个方向的关闭需单独确认,确保数据传输完成。
- TIME_WAIT:主动关闭方发送最后 ACK 后进入 TIME_WAIT 状态(通常 2MSL,约 2-4 分钟),防止旧包干扰。
- 可靠终止:确保双方都完成数据发送,避免数据丢失。
为什么挥手需要四次?
客户端处于final_wait_1/2时,只是不发送数据了,但是依然能接收到来自服务端的最后的数据,而服务端的关闭不是一蹴而就的,需要把手头上最后的事务处理完之后,才能休息。所以比起握手,挥手要多一次
为什么要有TIME_WAIT状态?
主动发起关闭的一方,才会有TIME_WAIT状态。
之所以主动关闭的一方,发送完ack报文之后不直接进入close状态,而是在TIME_WAIT状态等待2个WSL,有两个原因:
第一,防止历史连接中的数据,被后面相同四元组的连接错误的接收
如果发生了网络拥塞或者延迟,来自服务端的数据包可能在网络中滞留,如果直接关闭,没有客户端接收,这个数据包就会发给新连接,导致新连接被旧连接干扰。
第二,保证服务端能正确的接收到最后的ack报文。
如果客户端直接进入close状态,而最后的ack报文丢失,服务器一直等待,最后超时,就会重发FIN报文。而已经close的客户端,接受到这个FIN报文,就会发送RST报文,服务端接收到这个RST报文,就会把其解释为一个错误。为了避免这个情况,客户端就必须等待足够长的时间,确保服务端接收到,如果没有接收到,就重新发送FIN和ACK报文,一来一回的时间正好是2个MSL(即客户端到服务端的时间,指客户端第一次发送给服务端的ack加上服务端重传的FIN一共两个msl)
为什么是两个MSL?
MSL是报文最大生存时间,超过这个时间任何报文均会被丢弃