面试中经常被问到”三次握手,四次挥手”的问题。很多时候,我们都能条件反射式地背出标准答案:第一次怎样,第二次怎样…但背后的原理和逻辑,却很少有人能真正说清楚。
本文将从本质出发,用通俗易懂的语言,为大家揭开TCP连接建立和断开过程的神秘面纱。
一、什么是TCP连接?
在谈论”三次握手,四次挥手”前,我们需要理解什么是TCP连接。
想象一下,TCP连接就像是两个人之间的电话通话。在通话前,你需要拨号、等待对方接听、互相确认身份,这个过程就是”三次握手”;通话结束时,双方互相道别、挂断电话的过程,就是”四次挥手”。
从技术角度看,TCP是一种面向连接的单播协议,所谓的”连接”,其实只是通信双方内存中存储的一些信息,比如对方的IP地址、端口号等。这些信息让TCP能够提供可靠的、有序的数据传输服务。
二、为什么需要”三次握手”?
很多人困惑:为什么建立连接需要三次握手,而不是两次或四次?
通俗理解三次握手
想象你(客户端)和朋友(服务器)隔着一条嘈杂的马路喊话:
- 第一次握手:你大喊:“嘿!能听到我说话吗?“(客户端发送SYN)
- 第二次握手:朋友回应:“听到了!你能听到我说话吗?“(服务器返回SYN+ACK)
- 第三次握手:你回答:“我也听到你的了!“(客户端发送ACK)
至此,你们确认了一个重要事实:双方都能发送信息,也都能接收信息。这正是通信的基础条件。
技术解释
从技术角度,三次握手的目的是:
- 确认双方的接收、发送能力正常
- 第一次握手:客户端证明自己的发送能力正常,服务端证明自己的接收能力正常
- 第二次握手:服务端证明自己的发送能力正常,客户端证明自己的接收能力正常
- 第三次握手:客户端再次证明自己的发送能力正常,服务端再次证明自己的接收能力正常
- 交换初始序列号(ISN)
- 序列号用于追踪TCP数据流中的字节,确保数据有序传输
- 随机选择序列号可以增强安全性,防止攻击者猜测序列号
如果只有两次握手,服务端无法确认客户端的接收能力;如果有四次握手,则会造成额外的网络开销。因此,三次握手是最优选择。
三、四次挥手:为什么关闭连接需要四步?
关闭连接为什么需要四次挥手,而不是像建立连接那样三次就够了呢?
通俗理解四次挥手
继续用电话通话来比喻:
- 第一次挥手:你说”我这边说完了,没什么要说的了”(客户端发FIN)
- 第二次挥手:朋友回答”好的,我知道你没话说了,但我可能还有话要说”(服务端回ACK)
- 第三次挥手:朋友又说”我也说完了,可以挂电话了”(服务端发FIN)
- 第四次挥手:你回答”好的,拜拜”(客户端回ACK)
关键在于:当你表示要结束通话时,对方可能还有话要说。这就是为什么需要四次挥手—因为TCP是双向通信的,每个方向都需要单独关闭。
技术解释
从技术角度:
- TCP连接是全双工的,数据可以在两个方向上独立传输
- 当一方发送FIN时,仅表示它不再发送数据,但仍可接收数据
- 接收到FIN的一方必须先回复ACK,然后等待上层应用程序决定何时关闭自己的发送方向
- 因此,关闭连接需要双方各自发送一个FIN和一个ACK,总共四个报文段
这就解释了为什么建立连接是三次握手,而关闭连接是四次挥手。
四、深入理解TCP头部和状态转换
理解三次握手和四次挥手的核心,是了解TCP头部中的关键字段和连接状态转换。
TCP头部的关键字段
TCP头部包含许多重要信息,但在握手和挥手过程中,最关键的是:
- 序列号(Sequence Number):标识发送的数据字节流的位置
- 确认号(Acknowledgment Number):期望收到的下一个字节的序列号
- 标志位:
- SYN:请求建立连接
- ACK:确认收到的数据
- FIN:请求关闭连接
- RST:重置连接
连接状态转换
TCP连接从创建到关闭,会经历以下状态:
1. 连接建立(三次握手)
- 客户端开始(CLOSED) 客户端一开始是“关闭”状态,什么都没做。
- 服务端开始(LISTEN) 服务端在“监听”状态,等着有人来联系。
第一步
- 客户端发送SYN 客户端想要和服务端说话,发出“我想和你建立连接!”(SYN)。
第二步
- 服务端收到SYN,回复SYN+ACK 服务端收到了请求,回复“我收到了你的请求,也准备好了!”(SYN+ACK)。
第三步
-
客户端收到SYN+ACK,回复ACK
客户端收到了服务端的回复,再发一个“我也准备好了!”(ACK)。
-
现在,两边都进入“ESTABLISHED”(已建立)状态,可以正常通信了。
2. 连接关闭(四次挥手)
主动关闭方(通常是客户端)
- 客户端在通信中(ESTABLISHED) 聊天正在进行。
- 客户端想挂电话,发送FIN(FIN_WAIT_1) 客户端说:“我这边没什么要说的了,我要挂电话了!”(发送FIN)
- 收到服务端的ACK(FIN_WAIT_2) 服务端回复:“我知道你要挂了!”(ACK)
- 收到服务端的FIN,回复ACK,进入TIME_WAIT 服务端说:“我也说完了,现在可以挂电话了!”(发送FIN),客户端最后说“好的,再见!”(ACK), 然后等一会儿,确保对方收到了,才彻底挂断(TIME_WAIT)。
- 等待超时后,进入CLOSED状态 客户端正式挂断,回到关闭状态。
被动关闭方(通常是服务端)
- 服务端在通信中(ESTABLISHED) 正常通信。
- 收到客户端的FIN,回复ACK(CLOSE_WAIT) 客户端说要挂了,服务端回复“我知道了!”(ACK)
- 服务端也说完了,发送FIN(LAST_ACK) 服务端说:“我也说完了,也要挂了!”(发送FIN)
- 收到客户端的ACK后,进入CLOSED状态 客户端说“好的,再见!”(ACK),服务端彻底挂断。
五、常见问题
SYN Flood攻击
SYN Flood是一种常见的DoS攻击,攻击者发送大量SYN包却不完成握手,导致服务器资源耗尽。
防御措施包括:
- SYN Cookie:不在收到SYN时立即分配资源
- 减少SYN+ACK重试次数:调整tcp_synack_retries参数
- 增大半连接队列:调整tcp_max_syn_backlog参数
连接队列溢出
服务器维护两种队列:
- 半连接队列:存放SYN_RECEIVED状态的连接
- 全连接队列:存放ESTABLISHED但未被应用accept的连接
当这些队列满时,新连接可能被丢弃或重置,可通过以下命令检查:
netstat -s | egrep "listen|LISTEN"
ss -lnt
六、总结
TCP的”三次握手,四次挥手”看似复杂,实际是保证可靠通信的必要设计。三次握手确保双方都具备收发能力并交换序列号;四次挥手则保证双向数据流都能完整关闭。
理解这些机制的本质,比单纯记住步骤更重要。作为程序员,我们应该培养”刨根问底”的精神,不仅知其然,更要知其所以然。
下次面试官再问你”三次握手,四次挥手”,希望你能自信地从本质出发,展示你深刻的理解,而不只是机械地背诵步骤。