2. TCP协议详解:三次握手、四次挥手、状态转换与滑动窗口
TCP协议,说白了就是网络通信里的「老大哥」。我做了这么多年网络编程,最深的感触就是:搞懂了TCP,你就搞懂了一半的网络世界。今天咱们就来聊聊TCP的核心机制——三次握手、四次挥手、状态转换,还有那个让人又爱又恨的滑动窗口。
2.1 三次握手:建立连接的艺术
为什么叫「三次」握手?不是两次,也不是四次?嗯,这个问题我当年面试时也被问过。先看流程,再聊原因。
三次握手流程:
- 第一次握手(SYN): 客户端发送SYN包(seq=x),进入SYN_SENT状态
- 第二次握手(SYN+ACK): 服务器收到后,回复SYN+ACK(seq=y, ack=x+1),进入SYN_RCVD状态
- 第三次握手(ACK): 客户端发送ACK包(seq=x+1, ack=y+1),双方进入ESTABLISHED状态
我习惯把三次握手比作「打电话」:
- 客户端:「喂,能听到吗?」(SYN)
- 服务器:「听到了,你能听到我吗?」(SYN+ACK)
- 客户端:「听到了,咱们开始聊吧」(ACK)
你想想看,如果只有两次握手,服务器怎么知道客户端已经准备好接收数据了?我曾经在项目中遇到过一个问题:客户端发了个SYN,服务器回了SYN+ACK,然后服务器就傻傻地开始发数据了。结果客户端根本没准备好,数据全丢了。这就是典型的「半连接」问题。
避坑指南: 我曾经在写高并发服务器时,遇到过SYN Flood攻击。攻击者只发SYN,不回复第三次握手,导致服务器维护大量半连接。解决方案是启用SYN Cookie,或者缩短SYN超时时间。
2.2 四次挥手:优雅地告别
断开连接比建立连接多了一次,为什么?因为TCP是全双工的,两边都要说再见。
四次挥手流程:
- 第一次挥手(FIN): 主动方发送FIN包,进入FIN_WAIT_1状态
- 第二次挥手(ACK): 被动方回复ACK,进入CLOSE_WAIT状态。主动方收到后进入FIN_WAIT_2
- 第三次挥手(FIN): 被动方发送FIN包,进入LAST_ACK状态
- 第四次挥手(ACK): 主动方回复ACK,进入TIME_WAIT状态,等待2MSL后关闭
说白了,就是「你先挂,我后挂」。我记得有一次调试一个网络服务,发现大量连接处于CLOSE_WAIT状态。一查代码,原来是服务端收到FIN后,忘了调用close()。这就是典型的「CLOSE_WAIT泄漏」。
注意: TIME_WAIT状态会持续2MSL(约2分钟)。如果服务器作为主动关闭方,短时间内大量连接进入TIME_WAIT,端口会被占满。我建议在高并发场景下,尽量让客户端主动关闭连接。
2.3 TCP状态转换图
TCP的状态机,说白了就是一张「状态流转图」。我当年背这张图背得想吐,后来发现理解了流程,根本不用背。
这张图我建议你多看几遍。核心就三点:
- 建立连接: CLOSED → SYN_SENT → ESTABLISHED(客户端)
- 断开连接: ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED(主动方)
- 被动方: ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED
2.4 滑动窗口机制:流量控制的精髓
滑动窗口,说白了就是「我能收多少,你就发多少」。没有它,网络早就被数据包撑爆了。
核心概念:
- 发送窗口: 发送方可以连续发送的数据量,不用等ACK
- 接收窗口(rwnd): 接收方告诉发送方「我还有多少缓冲区」
- 拥塞窗口(cwnd): 网络状况决定的发送量
- 实际发送量 = min(rwnd, cwnd)
我举个例子。假设接收窗口是1000字节,发送方可以一口气发1000字节,不用等ACK。收到ACK后,窗口向前滑动,继续发。这就叫「流水线作业」,效率比「发一个等一个」高太多了。
为什么会这样?你想想看,如果每次发一个包都要等ACK,那网络延迟高的时候,吞吐量就惨不忍睹。滑动窗口让发送方可以「批量发货」,接收方「批量确认」,效率翻倍。
避坑指南: 我曾经在项目中遇到过「窗口为0」的情况。接收方缓冲区满了,发送方还在拼命发。解决方案是启用TCP的「窗口探测」机制,发送方定期发一个字节的探测包,看看窗口有没有恢复。
2.5 实战经验总结
| 知识点 | 常见问题 | 我的建议 |
|---|---|---|
| 三次握手 | SYN Flood攻击 | 启用SYN Cookie,限制半连接数 |
| 四次挥手 | CLOSE_WAIT泄漏 | 检查代码中是否遗漏了close() |
| TIME_WAIT | 端口被占满 | 让客户端主动关闭,或调整tcp_tw_reuse |
| 滑动窗口 | 窗口为0死锁 | 启用窗口探测,或调整接收缓冲区大小 |
嗯,TCP协议的内容就聊到这儿。记住一句话:TCP不是完美的,但它是最可靠的。理解这些机制,你写网络程序时就能少踩很多坑。
最后提醒: 别以为TCP很复杂就害怕。我刚开始学的时候也一头雾水,后来在项目中调了几个bug,慢慢就通了。多动手,多抓包分析,比死记硬背强一百倍。
可可精品资料库