ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

计算机网络基础知识笔记:从TCP状态机到抓包排错实战

计算机网络基础知识笔记:从TCP状态机到抓包排错实战 简介这份PDF资料面向准备技术面试的IT求职者与网络方向在校生系统梳理计算机网络核心考点帮助读者在有限时间内建立完整的知识框架并应对面试追问。内容围绕OSI七层参考模型与TCP/IP四层模型展开涵盖TCP/IP协议族、TCP与UDP及SCTP协议对比、三次握手与四次挥手、TCP状态机、TIME_WAIT状态、端口号、超时重传与快速重传、TCP Header结构、可靠传输、流量控制与拥塞控制以及IPv4、IPv6、ICMP、ARP、IGMP等网络层协议并附有OSI与TCP/IP模型区别等常见面试问题解析。资源包共1个PDF文件约2.08MB结构按章节递进便于按知识点检索复习。目前已有969人学习适合作为面试冲刺阶段的速查笔记与知识查漏补缺材料。1. 从一次线上故障说起这份网络基础笔记到底能解决什么去年排查一个服务端偶发的连接堆积问题netstat里成片的CLOSE_WAIT应用日志却干干净净。翻遍代码没找到close漏调最后定位到是某个第三方 SDK 在异常分支里没释放 socket。那一刻我意识到很多玄学故障的根子不在业务代码而在对 TCP 状态机、TIME_WAIT、CLOSE_WAIT 这些底层机制的理解深度上。这份《计算机网络基础知识》笔记就是冲着这类问题来的——它把 OSI 七层、TCP/IP 四层模型、TCP 三次握手四次挥手、状态机、超时重传、流量控制、拥塞控制这些面试和实战都绕不开的点按模型 → 协议 → 机制 → 排错的顺序串了一遍。适合谁准备面试的初中级工程师、被网络问题卡住的后端开发、想补底层基础的运维。它不是科普是一份能对着抓包结果逐条核对的速查底稿。2. 网络模型选型OSI 七层和 TCP/IP 四层到底该背哪个2.1 两套模型的分层逻辑与合并理由OSI 七层是理论标准从下到上依次是物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。物理层管比特流和数模转换数据链路层管帧的可靠传输和 MAC 寻址网络层管 IP 路由传输层管端到端可靠传输上面三层分别管会话管理、数据格式化和应用通信。TCP/IP 模型是工程事实标准只有四层数据链路层、网络层、传输层、应用层。合并方式很直接——下两层物理数据链路合成数据链路层上三层会话表示应用合成应用层。为什么这么合笔记里给了两条理由我觉得比很多教材讲得清楚上三层处理应用业务细节差异大、变化快下四层处理通信细节更通用、更稳定。而且上三层通常跑在用户进程里下四层是 OS 内核的一部分。这个划分直接决定了你排查问题时的切入点——应用层的问题看日志和协议格式传输层以下的问题看抓包和内核参数。面试常问OSI 和 TCP/IP 的区别标准答法是OSI 是标准模型TCP/IP 是事实模型对 OSI 做了简化。但光背这句不够你得能说出合并的具体层和理由以及 Socket 作为传输层以下的封装为上面三层提供了统一接口这个点。2.2 协议族清单与各层职责对照TCP/IP 协议族按层归类传输层有 TCP、UDP、SCTP网络层有 IPv4、IPv6、ICMP、IGMP、ARP、RARP数据链路层有 BPF、DLPI。这张清单建议直接背下来面试和排查都用得上。层次协议核心职责典型场景传输层TCP面向连接、可靠、全双工字节流文件传输、HTTP传输层UDP无连接、不可靠、数据报音视频、DNS 查询传输层SCTP面向连接、多流多宿、消息服务信令传输网络层IPv432 位地址、路由选择现有互联网主体网络层IPv6128 位地址、巨大地址空间新一代网络网络层ICMP主机与路由间消息通信ping、traceroute网络层ARPIPv4 地址映射 MAC 地址广播网络寻址网络层IGMP组播通信管理视频组播这里有个容易翻车的点笔记原文写 IPv6 使用 64 位地址这是笔误IPv6 是 128 位。实际工作中如果按 64 位去算地址空间会闹笑话。另外 SCTP 的多流和多宿是两个独立特性——多流指一个关联里可以并行多个逻辑流单流丢包不阻塞其他流多宿指一个端点可以绑定多个 IP 地址某个网络接口挂了能自动切换。这两个特性在电信信令领域用得比较多普通 Web 开发接触少但面试问到TCP 和 SCTP 的区别时能答出来是加分项。2.3 用 Python 验证分层与协议行为光看模型不够动手验证一遍印象才深。下面这段代码用 Python 的 socket 模块分别建 TCP 和 UDP 连接观察两者的行为差异。import socket # TCP面向连接需要先 connect 再 send tcp_sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcp_sock.settimeout(3) try: tcp_sock.connect((example.com, 80)) tcp_sock.sendall(bGET / HTTP/1.0\r\nHost: example.com\r\n\r\n) data tcp_sock.recv(1024) print(TCP 收到字节数:, len(data)) except socket.timeout: print(TCP 连接超时) finally: tcp_sock.close() # UDP无连接直接 sendto不保证到达 udp_sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp_sock.settimeout(3) try: udp_sock.sendto(bhello, (8.8.8.8, 53)) data, addr udp_sock.recvfrom(1024) print(UDP 收到响应来自:, addr) except socket.timeout: print(UDP 无响应正常UDP 不保证回复) finally: udp_sock.close()逻辑说明TCP 的connect会触发三次握手sendall把数据写入发送缓冲区recv阻塞等待对端数据。UDP 的sendto直接发出数据报不建立连接recvfrom等不到响应就超时这是正常行为。参数上SOCK_STREAM对应 TCPSOCK_DGRAM对应 UDPsettimeout控制阻塞时长。跑一遍这段代码再对照笔记里的协议族表格分层和协议行为就串起来了。3. TCP 连接管理三次握手、四次挥手与状态机排查3.1 握手挥手的分节交互与半关闭三次握手的过程服务端先socket、bind、listen启动被动打开进入 LISTEN 状态客户端connect发 SYN带自己的初始序列号进入 SYN_SENT服务端收到 SYN 后回 ACKSYN进入 SYN_RCVD客户端收到后回 ACK双方进入 ESTABLISHED。服务端把 ACK 和 SYN 合在一个分节里发省了一次往返。四次挥手主动关闭方 A 调close发 FIN进入 FIN_WAIT_1B 收到 FIN 回 ACK进入 CLOSE_WAIT同时把 EOF 传给上层应用B 上层处理完后调close发 FIN进入 LAST_ACKA 收到 FIN 回 ACK进入 TIME_WAIT等 2MSL 后变 CLOSED。为什么挥手要四次而握手只要三次因为 TCP 是全双工两端的关闭是独立动作。握手时服务端收到 SYN 后既确认对方又发自己的 SYN可以合并挥手时被动关闭方收到 FIN 后上层应用可能还有数据要发ACK 和 FIN 不能合并所以拆成两组。半关闭状态值得单独记A 发完 FIN 后B 还能继续给 A 发数据此时 A 能收不能发。这个状态在实际排查中很关键——如果看到连接卡在半关闭说明有一端没正确调close。3.2 状态机全路径与 CLOSE_WAIT 堆积定位TCP 状态机分三条路径建链、主动关闭、被动关闭。建链路径服务端是 CLOSED→LISTEN→SYN_RCVD→ESTABLISHED客户端是 CLOSED→SYN_SENT→ESTABLISHED。主动关闭路径是 ESTABLISHED→FIN_WAIT_1→FIN_WAIT_2→TIME_WAIT→CLOSED中间可能经过 CLOSING。被动关闭路径是 ESTABLISHED→CLOSE_WAIT→LAST_ACK→CLOSED。排查连接堆积时状态机是最直接的线索。CLOSE_WAIT堆积说明对端发了 FIN本端回了 ACK但本端应用一直没调close连接卡在被动关闭状态。常见原因是代码里 socket 没在finally块里释放或者线程池满了导致处理逻辑阻塞。TIME_WAIT堆积则是主动关闭方等 2MSL属于正常行为但如果量太大影响端口分配可以调net.ipv4.tcp_tw_reuse让新连接复用 TIME_WAIT 状态的端口。# 统计各状态连接数快速定位堆积状态 netstat -n | awk /^tcp/ {state[$NF]} END {for(k in state) print k, state[k]} # 查看 CLOSE_WAIT 连接对应的进程 ss -tanp | grep CLOSE_WAIT # 查看 TIME_WAIT 数量 ss -tan | grep -c TIME_WAIT逻辑说明第一条命令按最后一列连接状态聚合计数一眼看出哪个状态异常。第二条ss -tanp带进程信息能定位到具体 PID。第三条统计 TIME_WAIT 总量。参数上-t只看 TCP-a显示所有-n不做域名解析-p显示进程。这几个命令建议存成 alias排查时直接调。3.3 用 tcpdump 抓握手挥手全过程理论看完抓一次真实的三次握手和四次挥手比背十遍状态机都管用。# 抓取本机与目标主机的 TCP 握手挥手包-S 显示绝对序列号 sudo tcpdump -i eth0 -S -nn tcp and host 93.184.216.34 and port 80 -c 20 # 另开终端发起请求 curl -s -o /dev/null http://93.184.216.34/逻辑说明-i eth0指定网卡-S显示绝对序列号而非相对值方便核对 SYN 和 ACK 的序列号关系-nn禁止端口和 IP 解析-c 20抓满 20 个包停止。抓到的包按顺序看SYN → SYNACK → ACK 是握手FIN → ACK → FIN → ACK 是挥手。重点核对每次 ACK 的确认号是不是上一次序列号1以及 FIN 分节带的序列号。如果抓包看到 SYN 重传说明握手阶段就丢包了问题在网络链路而非应用层。4. 可靠传输与流量控制重传、滑动窗口与拥塞控制4.1 超时重传与快速重传的触发条件超时重传是发送方设一个定时器超时没收到 ACK 就重发。缺点是等待时间长效率低。快速重传是接收方收到乱序包后发重复 ACK发送方连续收到 3 个重复 ACK 就立即重传丢失的包不等超时。举个例子发送方发了 1、2、3、4、52 丢了接收方收到 3、4、5 后每次都回 ACK 1期望收到 2发送方收到 3 个重复 ACK 1 后立刻重传 2。RTT 和 RTO 的关系要理清RTT 是往返时延由链路传播时间、末端处理时间、路由器排队时间组成其中排队时间随网络拥塞变化所以 RTT 波动能反映拥塞程度。RTO 是重传超时时间根据 RTT 动态调整一般至少是 1.5 倍 RTT。RTO 太小会频繁重传太大会等太久。内核自动控制 RTO但你可以通过ss -i看当前连接的 RTT 和 RTO 估算值。4.2 ARQ 三种模式与滑动窗口机制ARQ 是可靠传输的基础协议有三种模式。停等 ARQ 发一个等一个 ACK性能差很少用。连续 ARQ 分两个变体Go-Back-N 在丢包时重传整个窗口内未确认的包窗口大时重传代价高Selective-Repeat 只重传丢失的那个包每个包有独立计时器效率高但接收方要缓存乱序包。反馈 ARQ 是接收方周期性发确认Kafka 在应用层保证消息一致性时用了类似机制。滑动窗口是流量控制的核心。发送方和接收方各维护一个窗口发送方窗口大小由接收方通告窗口决定窗口内的未确认分组需要重传。接收方通过通告窗口告诉发送方自己还能收多少数据窗口为 0 时发送方停止发送等接收方应用读走数据后窗口重新打开。这就是 TCP 的负反馈机制——用接收端速率限制发送端速率。4.3 流量控制与拥塞控制的区别及内核参数流量控制是端到端的解决接收方处理不过来的问题靠通告窗口实现。拥塞控制是全局的解决网络中间链路拥堵的问题靠拥塞窗口实现。发送方实际发送窗口取通告窗口和拥塞窗口的较小值。拥塞控制有慢启动、拥塞避免、快重传、快恢复四个阶段慢启动时拥塞窗口指数增长到阈值后线性增长检测到丢包后减半。# 查看当前连接的窗口、RTT、RTO 等内核估算值 ss -ti # 查看和调整 TCP 缓冲区相关内核参数 sysctl net.ipv4.tcp_rmem sysctl net.ipv4.tcp_wmem sysctl net.core.rmem_max sysctl net.core.wmem_max # 临时调大接收缓冲区上限重启失效 sudo sysctl -w net.core.rmem_max16777216逻辑说明ss -ti的-i显示 TCP 内部信息包括rtt、rto、cwnd拥塞窗口、rwnd接收窗口等排查传输性能时必看。tcp_rmem和tcp_wmem是接收/发送缓冲区的三个值最小、默认、最大。rmem_max和wmem_max是全局上限。调大缓冲区能提升高延迟高带宽场景的吞吐但会占更多内存。参数调整要结合ss -ti观察实际窗口使用情况盲目调大不一定有效。5. 避坑与排查那些年我们踩过的 TCP 坑5.1 现象服务端大量 CLOSE_WAIT连接数只增不减原因对端发了 FIN本端内核回了 ACK 进入 CLOSE_WAIT但本端应用代码没有调close释放 socket。常见于异常分支没走finally或者线程池满导致处理逻辑卡住socket 一直挂着。解决用ss -tanp | grep CLOSE_WAIT定位到 PID检查代码里 socket 的释放路径。所有 socket 操作必须放在try/finally里finally中调close。如果是线程池满检查任务队列和拒绝策略。临时缓解可以重启服务但根因在代码。5.2 现象TIME_WAIT 数量上万新连接报Cannot assign requested address原因主动关闭方等 2MSL 才释放端口高并发短连接场景下 TIME_WAIT 堆积可用临时端口耗尽。客户端侧尤其常见。解决调net.ipv4.tcp_tw_reuse1允许复用 TIME_WAIT 状态的端口建新连接仅对客户端主动发起有效。调net.ipv4.tcp_max_tw_buckets控制 TIME_WAIT 上限。更根本的方案是改用长连接或连接池减少短连接数量。注意tcp_tw_recycle在新版内核已移除不要再用。5.3 现象抓包看到 SYN 重传连接建立慢或失败原因握手阶段 SYN 或 SYNACK 丢包发送方按 RTO 重传。可能是网络链路问题、防火墙拦截、或者服务端 accept 队列满导致 SYN 被丢弃。解决先tcpdump确认是 SYN 重传还是 SYNACK 重传。SYN 重传说明客户端到服务端方向丢包检查中间网络设备。SYNACK 重传说明服务端回了但客户端没收到或者服务端 accept 队列满。调大net.core.somaxconn和net.ipv4.tcp_max_syn_backlog检查应用listen的 backlog 参数。5.4 现象传输大文件时吞吐上不去RTT 正常但窗口很小原因接收缓冲区太小通告窗口一直上不去发送方被限流。或者拥塞窗口因为早期丢包被压得很低一直没恢复。解决ss -ti看rwnd和cwnd。如果rwnd小调大net.ipv4.tcp_rmem和net.core.rmem_max。如果cwnd小且 RTT 正常可能是早期丢包触发了拥塞避免检查链路是否有间歇性丢包。高带宽高延迟场景可以启用net.ipv4.tcp_window_scaling默认开启让窗口突破 64KB 限制。5.5 现象进程崩溃后对端立刻感知断链但机器没宕机原因进程终止时无论自愿还是非自愿内核会关闭所有打开的文件句柄包括 socket这会触发内核发送 FIN 分节。所以对端能马上感知。解决这不是 bug是内核行为。但如果你不希望进程崩溃时连接被立刻关闭需要在应用层做连接保活和重连。理解这个机制有助于排查为什么进程挂了连接就断了这类问题——不是应用发的 FIN是内核代发的。6. 进阶技巧用 ping 估算传输时间与抓包验证习惯笔记里有个很实用的技巧用ping估算 TCP 数据传输时间。一次ping用 84 字节的 IP 数据报假设 30 次测量平均 RTT 是 175ms要发 2000 字节数据每次发 40 字节共 50 次。每次除了 40 字节数据还有 20 字节 IP 头和 20 字节 TCP 头共 80 字节。估算耗时就是 175ms × 50 8750ms。这个估算很粗没算拥塞控制和窗口机制的影响但用来快速判断这个传输大概要多久够用了。更靠谱的验证方式是抓包。我现在的习惯是任何网络相关的性能问题先tcpdump抓一轮用 Wireshark 打开看时序图。重点看几个东西——握手有没有重传、窗口有没有突然变小、有没有重复 ACK、RTT 有没有突增。这几个指标基本能覆盖大部分传输层问题。# 抓包并直接输出到文件供 Wireshark 分析 sudo tcpdump -i eth0 -w /tmp/tcp_capture.pcap tcp and port 8080 -c 500 # 用 tshark 命令行快速统计重传次数 tshark -r /tmp/tcp_capture.pcap -Y tcp.analysis.retransmission | wc -l # 统计重复 ACK 数量 tshark -r /tmp/tcp_capture.pcap -Y tcp.analysis.duplicate_ack | wc -l逻辑说明-w把原始包写入 pcap 文件-c 500抓满 500 个包停止。tshark -r读取 pcap-Y是显示过滤器tcp.analysis.retransmission过滤重传包tcp.analysis.duplicate_ack过滤重复 ACK。重传多说明链路丢包重复 ACK 多说明有乱序或丢包触发快速重传。这两个指标结合ss -ti的 RTT 和窗口数据基本能定位传输层问题的方向。从那以后我每次遇到网络性能问题都强制走一遍抓包 → 看重传和重复 ACK → 看窗口和 RTT → 对照状态机这个流程不再靠猜。这套笔记里的模型、状态机、重传机制、流量控制正好是这个流程的理论底稿。希望帮到你。本文还有配套的精品资源点击获取
返回列表