
作为网络通信的基石,TCP协议的可靠连接建立与断开机制是每一位网络工程师必须深刻理解的内容。这不仅关乎基础理论,更是网络工程师软考、面试中的高频核心考点。
在深入剖析三次握手和四次挥手之前,我们需要先回顾一下TCP报文段头部中几个至关重要的字段,它们是理解整个流程的关键。
TCP报文段头部关键字段
理解握手与挥手过程,离不开以下几个核心控制位和序号:
- SYN:同步序列号(Synchronize),用于发起一个新的连接。
- ACK:确认(Acknowledgment),置1时表示“确认号”字段有效。
- FIN:终止(Finish),用于请求断开连接。
- seq(序列号):本报文段所发送数据的第一个字节的序号。
- ack(确认号):期望收到对方下一个报文段的第一个数据字节的序号,表示在此序号之前的数据已全部正确接收。
TCP三次握手:可靠连接的建立
为什么建立连接需要三次报文交互?其根本目的是同步双方的初始序列号(Initial Sequence Number, ISN),并协商通信参数,确保连接的可靠性,尤其是为了解决网络中可能存在的、已失效的旧连接请求报文突然到达服务器的问题。
详细流程
1. 第一次握手 (客户端 -> 服务器)
客户端发送一个TCP报文,请求建立连接。发送后,客户端进入 SYN_SENT 状态。
- 标志位:
SYN=1
- 序列号:
seq = x(x为客户端随机生成的初始序列号)
- 说明:这个报文不能携带应用层数据,但会消耗掉一个序列号。
2. 第二次握手 (服务器 -> 客户端)
服务器收到SYN报文后,如果同意建立连接,则发送确认报文。发送后,服务器进入 SYN_RCVD 状态。
- 标志位:
SYN=1, ACK=1
- 序列号:
seq = y(y为服务器随机生成的初始序列号)
- 确认号:
ack = x + 1(表示已收到客户端的序列号x,期望下次收到x+1)
- 说明:这个报文同样不携带应用层数据,也消耗一个序列号。
3. 第三次握手 (客户端 -> 服务器)
客户端收到服务器的SYN-ACK报文后,需要再次发送确认。发送后,双方都进入 ESTABLISHED 状态,连接建立成功。
- 标志位:
ACK=1
- 序列号:
seq = x + 1(因为第一次握手消耗了序列号x,所以这次从x+1开始)
- 确认号:
ack = y + 1(确认收到了服务器的序列号y)
- 说明:这次握手可以携带应用层数据了。如果不携带数据,则不消耗序列号。
为什么必须是三次?
两次握手看似可行,但无法防止已失效的连接请求报文造成的混淆。假设一个旧的SYN报文延迟很久才到达服务器,服务器会误以为这是新的连接请求并回应,如果只有两次握手,服务器会直接建立连接并等待数据,造成资源浪费。第三次握手让客户端有机会告诉服务器:“我确认了你的响应,但我们刚才发起的是一个新的、有效的请求。” 这有效地杜绝了历史报文引起的错误。
TCP四次挥手:有序连接的终止
连接断开为什么需要四次交互?因为TCP连接是全双工的,数据在两个方向上可以独立传输。因此,每个方向都必须单独进行关闭。
详细流程(假设客户端主动发起关闭)
1. 第一次挥手 (客户端 -> 服务器)
客户端应用进程调用close(),TCP发送一个终止报文。发送后,客户端进入 FIN_WAIT_1 状态。
- 标志位:
FIN=1, ACK=1
- 序列号:
seq = u(u的值为客户端已发送的最后一个字节的序号加1)
2. 第二次挥手 (服务器 -> 客户端)
服务器收到FIN报文,立即发出确认。服务器进入 CLOSE_WAIT 状态。客户端收到此确认后,进入 FIN_WAIT_2 状态。
- 标志位:
ACK=1
- 序列号:
seq = v
- 确认号:
ack = u + 1
- 关键点:此时连接处于半关闭状态。客户端到服务器的方向已经关闭,客户端不能再发送数据,但服务器如果还有未发完的数据,可以继续发送给客户端。
3. 第三次挥手 (服务器 -> 客户端)
当服务器也准备关闭连接(应用进程调用close())时,它会发送自己的FIN报文。服务器进入 LAST_ACK 状态。
- 标志位:
FIN=1, ACK=1
- 序列号:
seq = w(如果在CLOSE_WAIT状态期间又发送了数据,序列号会更新)
- 确认号:
ack = u + 1(通常保持不变)
4. 第四次挥手 (客户端 -> 服务器)
客户端收到服务器的FIN报文后,必须发出确认。客户端进入 TIME_WAIT 状态。服务器一旦收到这个最终确认,立即进入 CLOSED 状态。
- 标志位:
ACK=1
- 序列号:
seq = u + 1
- 确认号:
ack = w + 1
- 特别关键点:客户端在发送完最后一次ACK后,不会立即关闭,而是需要等待 2MSL(Maximum Segment Lifetime,最长报文段寿命)的时间后,才最终进入 CLOSED 状态。
为什么需要TIME_WAIT状态?
这是理解四次挥手的一个难点,主要有两个目的:
- 可靠地终止连接:确保最后一个ACK报文能够到达服务器。如果这个ACK丢失,处于LAST_ACK状态的服务器会超时重传FIN报文。仍在TIME_WAIT状态的客户端收到后,可以重发ACK,从而保证双方都能正常关闭。
- 让旧报文在网络中消逝:等待2MSL时间,足以让这个连接过程中产生的所有报文都从网络中消失。这样,下一个使用相同源端口和目的端口的新连接,就不会受到属于上一个连接的、延迟的旧报文的干扰。
软考与实战常见考点总结
掌握理论是为了解决实际问题。无论是备战软考还是进行日常运维,以下要点都值得重点关注。
1. 关键状态与故障排查
- SYN_RCVD:服务器收到SYN后等待ACK的状态。大量此状态连接可能是遭受了 SYN Flood攻击(攻击者只发第一次握手,不回复第三次,耗尽服务器资源)。
- CLOSE_WAIT:被动关闭方(服务器)收到FIN并回复ACK后的状态。如果该状态连接数量异常多,通常是应用程序Bug,例如没有正确调用
socket.close()来发起第三次挥手。
- TIME_WAIT:主动关闭方(如高并发短连接的服务器)在发送最终ACK后的状态。大量TIME_WAIT连接是正常现象,但过多可能耗尽端口资源,可通过调整内核参数(如
tcp_tw_reuse)优化。
2. 序列号与确认号计算规则
这是笔试中的常客,务必牢记:
- 当报文段携带SYN或FIN标志时,即使没有数据,也消耗一个序号。因此,对应的确认号
ack = 收到的 seq + 1。
- 对于普通数据报文,确认号
ack = 收到的 seq + 数据长度。
3. 连接建立与终止的特殊情况
- 同时打开:双方同时发送SYN,会交换四个报文,最终建立一条连接。
- 同时关闭:双方同时发送FIN,流程类似,也会经过TIME_WAIT状态。
理解TCP连接的生命周期,是诊断网络问题、进行性能调优和通过技术面试的基石。希望这篇结合理论与实战考点的详解,能帮助你更扎实地掌握这一核心网络协议机制。如果你想深入探讨更多网络技术细节或备考经验,欢迎来云栈社区与广大开发者交流。
|