计算机网络基础知识实战:TCP状态机、三次握手与CLOSE_WAIT排障指南
2026/9/23 16:32:54 网站建设 项目流程

简介:这份PDF资料面向准备技术面试的IT求职者与网络初学者,系统梳理计算机网络核心考点,帮助读者在面试与工程实践中快速定位知识盲区。内容围绕OSI七层参考模型与TCP/IP四层模型展开,涵盖TCP/IP协议族、TCP/UDP/SCTP协议对比、三次握手与四次挥手、TCP状态机、TIME_WAIT状态、端口号、超时重传与快速重传、TCP Header结构、可靠传输、流量控制与拥塞控制,以及IPv4/IPv6、ICMP、ARP、IGMP等网络层协议,并附有常见面试问题解析。资源包为1个PDF文件,大小约2.08MB,结构清晰、便于按章节检索复习。目前已有969人学习下载,适合需要系统梳理网络知识体系、查漏补缺或冲刺面试的读者参考使用。

1. 从一次线上故障说起:为什么我又把计算机网络基础知识翻出来重啃

上周排查一个服务端连接数暴涨的问题,netstat里满屏的CLOSE_WAIT,第一反应是代码没关连接,翻到一半发现是上游超时策略和 TCP 四次挥手的状态迁移对不上。这种时候你才会意识到,OSI 七层模型、TCP/IP 四层模型、三次握手四次挥手这些计算机网络基础知识,不是面试前背一背就完事的考点,而是排障时真正能救命的东西。这份资源把网络模型、TCP/IP 协议族、TCP/UDP/SCTP 对比、TCP 状态机、TIME_WAIT、超时重传与快速重传、流量控制和拥塞控制串成了一条线,适合两类人:一是准备面试、需要把零散知识点串成体系的人;二是日常写后端、做网络编程,遇到连接异常想快速定位的人。它不教你写业务代码,但能让你在看抓包、读内核参数、调 socket 选项时,知道自己在改什么、为什么改。

2. 网络模型怎么选:OSI 七层与 TCP/IP 四层的映射关系和协议归属

2.1 两张模型图不是二选一,而是理论与工程的对照表

很多人背 OSI 七层背得滚瓜烂熟,一到实际抓包就懵,因为工程里用的是 TCP/IP 四层模型。这份资料把两者的映射关系讲得很直白:下两层物理层和数据链路层合并为数据链路层,上三层会话层、表示层、应用层合并为应用层,传输层和网络层保持不变。这么合并的理由也给了——上三层处理应用业务细节,差异大;下四层实现通信细节,更通用,而且上三层通常在用户进程内,下四层作为 OS 内核的一部分提供。

理解这个映射的实际价值在于:当你用tcpdump抓包时,看到的是以太网帧、IP 数据报、TCP 分节、应用层数据,这四层对应关系清晰了,排查问题时才能快速定位是哪一层出了状况。比如ping不通,先看网络层 ICMP 有没有回包;端口连不上,看传输层 SYN 有没有响应;HTTP 返回 502,那大概率是应用层的事。

2.2 协议族清单:每个协议解决什么问题

资料里把 TCP/IP 协议族按层拆开列了一遍,我整理成表格方便对照:

层次协议核心作用典型场景
传输层TCP面向连接、可靠、全双工字节流文件传输、HTTP 通信
传输层UDP无连接、不可靠、数据报音视频、DNS 查询
传输层SCTP面向连接、多流多宿、消息服务信令传输、高可靠场景
网络层IPv432 位地址、路由选择当前主流网络
网络层IPv6128 位地址、巨大地址空间新一代网络
网络层ICMP主机与路由间消息通信ping、traceroute
网络层ARPIPv4 地址映射为 MAC 地址广播网络寻址
网络层IGMP组播通信管理视频组播

这张表建议存下来,面试被问到“TCP/IP 包含哪些协议”时,按层说比按字母顺序背要清晰得多。资料里还特别提到,工程实现中只需要重点关注传输层和应用层,下两层由内核和网卡驱动处理,这个视角对后端开发很实用。

2.3 从套接字角度看模型分层

资料里有一句话很关键:套接字作为传输层以下网络的封装,为上面三层提供了统一接口。这意味着你写socket()bind()listen()accept()的时候,实际上是在和传输层打交道,下面的网络层和数据链路层被内核屏蔽了。理解这一点,就能明白为什么设置SO_RCVBUF会影响 TCP 通告窗口,为什么TCP_MAXSEG能控制分节大小——这些 socket 选项直接作用于传输层行为。

3. TCP 连接全生命周期:三次握手、四次挥手与状态机排障

3.1 三次握手和四次挥手的本质差异

资料用打电话的场景类比三次握手和四次挥手,很直观。但真正需要理解的是:为什么握手能合并 ACK 和 SYN,挥手却要分开发?

三次握手中,服务端收到 SYN 后,既需要确认客户端的 SYN(发 ACK),又需要发送自己的 SYN,这两个动作可以合在一个分节里完成,因为服务端此时已经知道了客户端的初始序列号,没有额外等待的必要。四次挥手不同,TCP 是全双工的,两端的关闭是独立动作。A 端发 FIN 表示“我没数据要发了”,B 端回 ACK 表示“知道了”,但 B 端可能还有数据要发给 A 端,所以 B 端的 FIN 必须等自己的数据发完才能单独发送。这就是半关闭状态存在的原因。

3.2 TCP 状态机:排障时最该盯住的几个状态

资料把服务端、客户端、主动关闭方、被动关闭方的状态迁移都列了出来。实际排障中,高频出现且容易出问题的是这几个状态:

  • SYN_RCVD:服务端收到 SYN 并发出 SYN+ACK 后进入此状态,如果大量堆积,通常是 SYN Flood 攻击或客户端 ACK 丢失。
  • FIN_WAIT_2:主动关闭方收到 ACK 后进入,如果长期停留,说明对端一直没发 FIN,可能是对端应用没调close()
  • CLOSE_WAIT:被动关闭方收到 FIN 并回 ACK 后进入,如果大量堆积,几乎可以确定是应用层没有正确关闭连接。
  • TIME_WAIT:主动关闭方最后经历的状态,持续 2MSL,用于保证最后一个 ACK 能重传、旧分节能在网络中消逝。

提示:CLOSE_WAIT堆积是代码问题,TIME_WAIT堆积是协议正常行为,两者排查方向完全不同,不要搞混。

3.3 用 ss 命令验证状态迁移

光看理论不够,实际验证一遍状态变化会记得更牢。开两个终端,一个用nc监听,一个用nc连接,然后用ss观察:

# 终端1:监听 9999 端口 nc -l 9999 # 终端2:连接 nc 127.0.0.1 9999 # 终端3:查看 TCP 连接状态 ss -tanp | grep 9999

执行后你会看到ESTABLISHED状态的连接。此时在终端2按Ctrl+C关闭,再执行ss -tanp | grep 9999,就能看到主动关闭方进入TIME_WAIT,被动关闭方短暂出现CLOSE_WAIT后消失。这个实验比背状态图有效得多。

参数说明:-t只看 TCP,-a显示所有状态,-n不解析服务名,-p显示进程信息。如果TIME_WAIT太多影响端口复用,可以调整内核参数net.ipv4.tcp_tw_reuse,但生产环境慎用,资料里也强调了TIME_WAIT存在的必要性。

4. 可靠传输与重传机制:从 ARQ 到快速重传的落地理解

4.1 ARQ 三种模式的递进关系

资料把 ARQ 协议拆成停等 ARQ、连续 ARQ、反馈 ARQ 三种模式,这个递进逻辑很清楚:停等 ARQ 发一个等一个,性能差;连续 ARQ 连续发多个再等确认,其中 Go-Back-N 出错时重传整个窗口,Selective-Repeat 只重传出错的分组;反馈 ARQ 则是接收方周期性发确认。

Kafka 在应用层确保消息一致性时也用了类似 Continuous ARQ 和 Feedback ARQ 的机制,这个类比很有价值——说明传输层的可靠传输思想会向上渗透到应用层设计中。

4.2 超时重传和快速重传的触发条件

超时重传靠定时器,RTO 根据 RTT 动态调整,一般至少为 1.5 倍 RTT。RTO 太小会导致频繁重传,太大会导致等待过久。快速重传靠重复 ACK,发送方连续收到 3 次重复确认就立即重传,不用等超时。

资料里给了一个数据传输时间估算的例子:用ping测平均 RTT 为 175ms,发送 2000 字节数据,每次 40 字节,共 50 次,估算耗时 8750ms。这个估算方法虽然粗糙,但在没有专业工具时能快速判断传输耗时是否合理。

4.3 用 Python 模拟滑动窗口发送

理解滑动窗口最好的方式是写一段模拟代码。下面用 Python 模拟一个简化的 Go-Back-N 发送方:

# 模拟 Go-Back-N 发送方滑动窗口 WINDOW_SIZE = 4 # 窗口大小 BASE = 0 # 窗口基序号 NEXT_SEQ = 0 # 下一个待发送序号 TOTAL = 10 # 总分组数 def send_packet(seq): """模拟发送分组,返回是否成功""" print(f"发送分组 {seq}") return True def receive_ack(ack): """模拟接收 ACK,返回确认序号""" print(f"收到 ACK {ack}") return ack # 发送窗口内的分组 while BASE < TOTAL: # 窗口未满时继续发送 while NEXT_SEQ < BASE + WINDOW_SIZE and NEXT_SEQ < TOTAL: send_packet(NEXT_SEQ) NEXT_SEQ += 1 # 模拟收到累积确认 ack = receive_ack(BASE + 1) if ack > BASE: BASE = ack # 窗口向前滑动 else: # 超时,重传所有未确认分组 print(f"超时,重传 {BASE} 到 {NEXT_SEQ-1}") for seq in range(BASE, NEXT_SEQ): send_packet(seq)

这段代码的逻辑说明:BASE是窗口基序号,NEXT_SEQ是下一个待发送序号,窗口大小固定为 4。发送方在窗口未满时持续发送,收到累积确认后窗口向前滑动。如果确认序号没有前进,说明超时,重传所有已发未确认的分组。参数WINDOW_SIZE可以调整,改大能提高吞吐但增加重传代价,改小则相反。

4.4 流量控制和拥塞控制的区别

资料里把流量控制和拥塞控制放在一起讲,但两者的目标不同:流量控制是接收方通过通告窗口限制发送方速率,防止接收缓冲区溢出;拥塞控制是发送方根据网络状况调整发送速率,防止网络过载。接收窗口和拥塞窗口取最小值,才是实际发送窗口。理解这一点,就能明白为什么SO_RCVBUF设置过小会导致吞吐下降——接收窗口小了,发送方被限制住了。

5. 避坑与常见问题:端口、TIME_WAIT 和状态异常的排查记录

5.1 端口号范围与连接数上限的误区

现象:以为一台客户端机器最多只能建立 65535 个连接。 原因:把端口号范围当成了连接数上限。资料里明确指出,连接由五元组(协议、源 IP、源端口、目标 IP、目标端口)唯一标识,端口是逻辑概念,连接数取决于五元组的组合数。 解决:如果要发起上百万连接,需要提供多个目标端口,每个端口承担 6 万多个客户端连接。同时注意文件句柄限制,每个 Socket 连接占用一个文件句柄。

5.2 TIME_WAIT 堆积导致端口耗尽

现象:服务端主动关闭大量短连接后,TIME_WAIT状态连接堆积,新连接无法绑定端口。 原因:主动关闭方进入TIME_WAIT后持续 2MSL,期间该四元组不能被复用。 解决:调整net.ipv4.tcp_tw_reuse允许复用TIME_WAIT状态的端口,或让客户端主动关闭连接,把TIME_WAIT转移到客户端侧。但资料里也提醒,TIME_WAIT的存在是为了保证可靠终止和旧分节消逝,不要盲目缩短。

5.3 CLOSE_WAIT 堆积是代码问题

现象:服务端大量CLOSE_WAIT,内存和句柄持续上涨。 原因:对端发了 FIN,本端回了 ACK,但应用层没有调用close(),连接一直停留在CLOSE_WAIT。 解决:检查代码中是否有未关闭的 Socket、连接池是否泄漏、异常分支是否遗漏了close()。这是代码 bug,不是内核参数能解决的。

5.4 快速重传误判为丢包

现象:抓包看到发送方连续收到 3 个重复 ACK 后立即重传,但接收方其实收到了数据。 原因:网络乱序导致接收方收到不按序的分组,触发重复 ACK。 解决:快速重传本身就是为了应对乱序,重传后如果接收方确认了所有数据,说明只是乱序而非丢包。如果频繁触发,检查网络路径是否存在多路径或拥塞。

5.5 SO_RCVBUF 设置过小导致吞吐下降

现象:TCP 传输大文件时吞吐远低于带宽上限。 原因:SO_RCVBUF设置过小,接收窗口受限,发送方被流量控制限制。 解决:根据带宽时延积(BDP)计算合适的缓冲区大小,公式为带宽 × RTT。例如 100Mbps 带宽、100ms RTT,BDP 约为 1.25MB,接收缓冲区至少应设置为这个量级。

6. 进阶技巧:用 tcpdump 和 ss 验证 TCP 状态迁移与重传行为

理论看再多,不如自己抓一次包。下面这套组合拳是我排查 TCP 问题的固定流程,分享给你。

第一步,用tcpdump抓取指定端口的 TCP 流量:

# 抓取 9999 端口的 TCP 包,保存到文件 tcpdump -i lo -nn -s 0 'tcp port 9999' -w tcp_test.pcap # 实时查看三次握手和四次挥手 tcpdump -i lo -nn -S 'tcp port 9999'

参数说明:-i lo指定回环网卡,-nn不解析 IP 和端口,-s 0抓完整包,-S显示绝对序列号。抓完后用 Wireshark 打开tcp_test.pcap,能看到完整的 SYN、SYN+ACK、ACK、FIN、ACK 序列。

第二步,用ss观察状态迁移:

# 每秒刷新一次,观察状态变化 watch -n 1 'ss -tan | grep 9999'

第三步,模拟丢包验证重传。用tc命令在回环网卡上注入丢包:

# 注入 10% 丢包率 sudo tc qdisc add dev lo root netem loss 10% # 测试完后删除规则 sudo tc qdisc del dev lo root netem

执行后重新传输数据,用tcpdump抓包,就能看到超时重传或快速重传的分节。这个实验能让你直观感受到 RTO 的动态调整和重复 ACK 的触发条件。

最后说一个我自己的习惯:每次遇到 TCP 连接异常,先ss -tan看状态分布,再tcpdump抓包确认握手挥手是否完整,最后对照状态机图定位是哪一步出了问题。这套流程走下来,大部分连接问题都能在十分钟内定位到方向。从那以后我每次上线新的网络服务,都强制走一遍状态检查和抓包验证,再也没被CLOSE_WAIT堆积坑过。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询