
1. 从“学海无涯”说起这个实验系列到底在解决什么问题做计算机网络实验做到第三卷还能起出“学海无涯”这种名字的人大概率已经过了照着实验指导书一步步敲命令的阶段了。第一卷通常是ARP、ICMP、ping这些基础抓包第二卷开始碰TCP三次握手、四次挥手、滑动窗口到了第三卷你会发现指导书上的内容已经不够用了——因为网络协议栈本身就是个无底洞越学越觉得自己知道的少。这个“第三卷”的核心定位我理解下来是这样的它不是一个从零开始的教程而是一个持续迭代的实验笔记合集。标题里那句“学到多的就加”说白了就是作者在表达一种态度——这个实验系列没有终点每学到新的网络知识点就往里塞一块内容。这种做法在自学者圈子里其实很常见但能坚持到第三卷的人不多。那它到底适合谁看我梳理了一下大概三类人最需要已经做完基础网络实验但不知道下一步该学什么的同学。你可能已经会配静态路由、会用Wireshark抓包看三次握手但面对“为什么TCP要有TIME_WAIT状态”“为什么MTU是1500”“NAT到底怎么工作的”这些问题时还是说不出个所以然。准备面试网络相关岗位需要把零散知识串成体系的人。面试官问一个“从输入URL到页面加载发生了什么”你能背出来但追问一句“DNS查询用的是UDP还是TCP为什么”就卡住了。想自己搭一个小型网络环境做实验但不知道从哪下手的人。手头有虚拟机、有抓包工具但不知道怎么设计实验拓扑怎么构造流量怎么验证协议行为。这个系列的价值就在于它把“学”和“做”绑在一起。不是先学完理论再动手而是动手过程中遇到问题反过来去查协议规范、去抓包验证、去调整实验参数。这种学习路径比单纯看书效率高得多因为每一个知识点都是你“撞”出来的记忆深度完全不一样。接下来我会把这个实验系列第三卷涉及的核心内容拆开从整体设计思路、关键协议细节、实操流程、常见问题四个维度展开。每个部分都会补充我在实际做实验时踩过的坑和总结的技巧尽量让你看完就能自己复现。2. 实验整体设计与思路拆解2.1 为什么选择“协议分层抓包验证”作为主线计算机网络实验最容易犯的一个错误就是把它做成“配置命令练习”。比如老师给一个拓扑图让你配OSPF你照着敲完命令邻居起来了实验就结束了。但问你OSPF的Hello包多久发一次、为什么要有DR/BDR、LSA有几种类型你答不上来。这个第三卷的思路明显不一样。它是以协议行为为核心而不是以设备配置为核心。具体来说它的实验设计遵循这样一条链路提出一个协议问题 → 设计最小实验拓扑 → 构造特定流量 → 抓包观察 → 对照协议规范分析 → 修改参数再验证举个例子如果你想搞清楚TCP的拥塞控制传统实验可能是“用iperf打流看吞吐量曲线”。但这个系列的做法会更细先构造一个会产生丢包的环境比如用tc命令加延迟和丢包然后用Wireshark抓包观察cwnd的变化、ssthresh的调整、快速重传和快速恢复的触发条件。你会亲眼看到拥塞窗口从1个MSS开始指数增长遇到丢包后砍半然后线性增长。这种设计的好处是你看到的不是结论而是过程。结论可以背过程只能理解。2.2 实验环境的选型逻辑为什么用虚拟化而不是物理设备这个系列第三卷的实验环境我推测大概率是基于虚拟化平台的。原因很简单物理设备做网络实验成本太高而且不灵活。你想改个MTU值物理交换机可能不支持你想模拟丢包物理链路做不到。用虚拟机做网络实验有几个明显优势拓扑灵活想加一台路由器就加一台想改链路带宽就改不用重新布线。可重复性强实验做砸了快照回滚几秒钟恢复。物理设备配错了可能得恢复出厂设置。抓包方便在虚拟链路上抓包不用额外接分光器或镜像端口。成本低一台性能还行的电脑就能跑四五个虚拟机组成一个小型网络。但虚拟化环境也有坑。最大的问题是虚拟网卡和虚拟交换机的行为跟物理设备不完全一样。比如Linux bridge和OVSOpen vSwitch在处理VLAN tag时的行为就有差异VMware的虚拟交换机对某些异常帧的处理也跟物理交换机不同。所以做实验时如果发现抓包结果跟理论不符先别怀疑协议先怀疑虚拟化层。提示如果你用GNS3或EVE-NG做实验注意它们的“云”节点Cloud节点连接的是宿主机的物理网卡流量会真的发出去。如果不想影响真实网络记得把云节点连到一个不用的网卡上或者用host-only模式。2.3 “学到多的就加”背后的知识管理策略标题里“学到多的就加”这句话其实透露了一个很重要的学习方法增量式知识管理。很多人学网络的问题是学一点丢一点今天搞懂了ARP下周学BGP的时候已经把ARP忘了。这个系列的做法是每学到一个新知识点就把它加到已有的实验体系里跟旧知识建立关联。比如你学了VLAN就把它加到之前的交换实验里学了NAT就把它加到之前的路由实验里。这样知识不是孤立的点而是一张不断扩大的网。具体操作上我建议你维护一个“实验索引表”类似这样实验编号核心协议前置实验新增知识点抓包文件3-01TCP拥塞控制2-03 TCP基础cwnd/ssthresh变化tcp_congestion.pcapng3-02NAT/PAT2-05 路由基础地址转换表nat_session.pcapng3-03DNS递归查询1-02 UDP递归vs迭代dns_recursive.pcapng3-04HTTP/2多路复用3-01 TCP流优先级http2_stream.pcapng这张表的作用是当你学新东西时能快速找到它跟旧知识的连接点。比如学HTTP/2的多路复用你会发现它解决的是HTTP/1.1的队头阻塞问题而队头阻塞又跟TCP的字节流特性有关。这样一路追溯下去知识就串起来了。3. 核心协议细节与实操要点3.1 TCP拥塞控制从理论公式到抓包实证TCP拥塞控制是网络实验里最容易“纸上谈兵”的部分。教科书上画个图cwnd从1开始指数增长到ssthresh后线性增长遇到丢包砍半。但实际抓包时你会发现事情没那么简单。先说一下实验设计。要观察拥塞控制你需要构造一个有丢包的环境。最直接的方法是用Linux的tc命令# 在接收端或中间路由器上执行 tc qdisc add dev eth0 root netem delay 50ms loss 1%这条命令给eth0接口加了50ms延迟和1%的丢包率。然后你用iperf3打TCP流同时在发送端抓包。抓包时要注意Wireshark默认不显示cwnd。你需要打开TCP协议首选项启用“Relative sequence numbers”和“Window scaling”然后在“Statistics → TCP Stream Graphs → Time Sequence (Stevens)”里看。或者更直接的方法用ss -ti命令查看当前连接的TCP内部状态ss -ti dst 192.168.1.100输出里会有cwnd:10、ssthresh:100这样的字段实时反映拥塞窗口大小。我实测下来有几个点跟教科书不太一样慢启动的初始窗口不是1个MSS。现代Linux的初始cwnd是10个MSSRFC 6928所以你会看到cwnd从10开始而不是从1开始。快速恢复不一定触发。如果丢包是超时引起的走的是超时重传路径cwnd直接降到1而不是砍半。SACK选择性确认会改变行为。如果双方都支持SACK丢包后的恢复过程会更精细不会像传统Reno那样一刀切。注意做拥塞控制实验时尽量用有线连接不要用WiFi。WiFi的链路层重传会干扰你对TCP层行为的判断。另外虚拟机的虚拟网卡可能不支持某些TCP选项建议用物理机做发送端。3.2 NAT/PAT地址转换表的维护与超时机制NAT实验看起来简单——内网地址出去变成公网地址回来再变回去。但真正做实验时你会发现NAT表项的维护有很多细节。实验拓扑可以这样设计一台内网主机192.168.1.10一台NAT路由器内网口192.168.1.1外网口10.0.0.1一台外网服务器10.0.0.100。内网主机访问外网服务器的Web服务。在NAT路由器上你需要配置# 启用IP转发 sysctl -w net.ipv4.ip_forward1 # 配置SNAT源地址转换 iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 -j MASQUERADE然后在内网主机上curl外网服务器同时在NAT路由器上查看NAT表conntrack -L你会看到类似这样的条目tcp 6 431998 ESTABLISHED src192.168.1.10 dst10.0.0.100 sport54321 dport80 src10.0.0.100 dst10.0.0.1 sport80 dport12345 [ASSURED]这里的关键信息是内网的54321端口被转换成了外网的12345端口。这个映射关系不是永久的有超时时间。TCP ESTABLISHED状态的默认超时是5天但UDP只有30秒。做这个实验时我踩过几个坑conntrack表满了会丢包。默认的nf_conntrack_max是65536如果做压力测试很容易打满。打满后新连接会被丢弃现象是“有时候能通有时候不能通”。MASQUERADE和SNAT的区别。MASQUERADE会自动检测外网口IP适合动态IP场景SNAT需要手动指定IP适合静态IP。实验里用哪个都行但生产环境要区分。NAT不影响分片。如果内网发了一个大包NAT路由器不会重新分片只是改地址。但如果是PAT端口复用分片后的后续包没有端口信息NAT路由器可能无法正确转换。这是NAT和分片交互的一个经典问题。3.3 DNS查询递归与迭代的抓包区分DNS实验的核心是搞清楚递归查询和迭代查询的区别。很多人背了概念但抓包时看到一堆DNS包分不清哪个是递归哪个是迭代。实验设计一台客户端192.168.1.10一台本地DNS服务器192.168.1.1一台根DNS服务器模拟比如用bind搭建一个只返回NS记录的服务器。客户端配置DNS为192.168.1.1然后查询www.example.com。在客户端抓包你会看到客户端 → 本地DNS查询www.example.com标志位RD1期望递归本地DNS → 根DNS查询www.example.com标志位RD0不期望递归根DNS → 本地DNS返回.com的NS记录本地DNS →.com的DNS查询www.example.com.com的DNS → 本地DNS返回example.com的NS记录本地DNS →example.com的DNS查询www.example.comexample.com的DNS → 本地DNS返回A记录本地DNS → 客户端返回A记录关键观察点第1步的RD1第2步的RD0。这就是递归和迭代的分界线。客户端期望本地DNS帮它查到底递归本地DNS自己则是一步步问路迭代。提示做DNS实验时记得关闭DNS缓存。Linux上可以用systemd-resolve --flush-caches或者直接重启nscd服务。不然第二次查询直接走缓存抓不到完整的查询过程。3.4 HTTP/2多路复用从队头阻塞到流优先级HTTP/2的实验需要你先搭一个支持HTTP/2的服务器。Nginx从1.9.5开始支持HTTP/2配置很简单server { listen 443 ssl http2; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; root /var/www/html; }然后准备一个包含多个资源的页面比如一个HTML引用了10张图片。用Chrome访问在开发者工具的Network面板里你可以看到所有请求都走同一个TCP连接而且有Stream ID列。抓包时Wireshark可以解析HTTP/2帧。你需要关注几种帧类型HEADERS帧携带请求头或响应头DATA帧携带实际数据SETTINGS帧协商连接参数WINDOW_UPDATE帧流量控制RST_STREAM帧取消某个流HTTP/2解决的核心问题是HTTP/1.1的队头阻塞。在HTTP/1.1里一个TCP连接同时只能处理一个请求前一个请求没响应完后面的请求就得等着。HTTP/2把请求拆成帧不同流的帧可以交错发送接收端根据Stream ID重新组装。但HTTP/2并没有完全解决队头阻塞——它解决的是应用层的队头阻塞TCP层的队头阻塞还在。如果有一个TCP包丢了后面所有流的数据都得等重传。这就是为什么后来有了QUICHTTP/3QUIC基于UDP每个流独立控制彻底解决了TCP层的队头阻塞。做这个实验时我建议你对比一下HTTP/1.1和HTTP/2的加载时间。用Chrome的Performance面板录制你会看到HTTP/2的瀑布图更“扁平”因为多个资源并行加载HTTP/1.1的瀑布图是阶梯状的因为请求排队。4. 完整实操流程与核心环节实现4.1 实验环境搭建从零开始构建可复现的网络拓扑这一节我以“TCP拥塞控制实验”为例把完整的环境搭建和实验流程走一遍。你可以照着做也可以根据自己的环境调整。第一步准备虚拟机你需要至少三台虚拟机发送端SenderUbuntu 22.042核2G接收端ReceiverUbuntu 22.042核2G路由器RouterUbuntu 22.042核2G开启IP转发网络连接方式Sender和Router之间用Host-Only网络1Router和Receiver之间用Host-Only网络2。Router有两个网卡分别连两个网络。第二步配置网络Sender的网卡IP设为192.168.10.10/24网关192.168.10.1。 Router的eth0设为192.168.10.1/24eth1设为192.168.20.1/24。 Receiver的网卡IP设为192.168.20.10/24网关192.168.20.1。Router上开启转发sysctl -w net.ipv4.ip_forward1第三步构造丢包环境在Router上对eth1接口面向Receiver加延迟和丢包tc qdisc add dev eth1 root netem delay 30ms loss 0.5%这个参数的意思是每个包延迟30ms随机丢0.5%的包。0.5%的丢包率足以触发拥塞控制但不会让连接完全断掉。第四步启动抓包在Sender上启动Wireshark抓eth0接口。过滤条件设为tcp.port 5201iperf3默认端口。第五步打流在Receiver上启动iperf3服务端iperf3 -s在Sender上启动客户端iperf3 -c 192.168.20.10 -t 60 -i 1这个命令会打60秒的TCP流每秒输出一次吞吐量。第六步分析抓包在Wireshark里找到iperf3的TCP流右键选择“Follow → TCP Stream”然后关闭流窗口。回到主界面点击“Statistics → TCP Stream Graphs → Time Sequence (Stevens)”。你会看到一条阶梯状的曲线那就是cwnd的变化。如果看不到cwnd检查一下Wireshark的TCP协议设置Edit → Preferences → Protocols → TCP确保“Relative sequence numbers”勾选“Window scaling”勾选。4.2 参数计算如何根据实验目标调整tc参数tc的netem模块参数很多调错了可能实验现象完全不对。这里说一下几个关键参数的计算逻辑。延迟delay延迟的大小决定了RTT。RTT 2 × 单向延迟。如果你想让RTT大约是100ms单向延迟设50ms。RTT会影响TCP的慢启动速度因为cwnd每收到一个ACK才增长RTT越大增长越慢。丢包率loss丢包率决定了拥塞控制触发的频率。0.1%的丢包率可能几分钟才触发一次1%的丢包率可能几秒钟就触发一次。建议从0.5%开始试观察现象后再调整。带宽限制rate如果你想模拟带宽受限的链路可以加rate参数tc qdisc add dev eth1 root netem delay 30ms loss 0.5% rate 10mbit这条命令把eth1的带宽限制在10Mbps。注意netem的rate限制是“整形”shaping不是“ policer”监管。整形会缓存超出的包监管会直接丢弃。做拥塞控制实验时用整形更接近真实链路。队列长度limitnetem默认的队列长度是1000个包。如果队列满了新包会被丢弃。这个参数会影响缓冲膨胀bufferbloat现象。如果你想观察bufferbloat把limit设大比如10000然后打流你会看到延迟急剧上升。提示tc命令加的规则重启后会消失。如果想持久化可以写到systemd服务里或者用tc qdisc save和tc qdisc restore。4.3 抓包分析从海量数据包中提取关键信息抓包容易分析难。一个60秒的iperf3流可能产生几万个包。你怎么从里面找到有用的信息我的做法是分三步第一步看统计概览Wireshark的“Statistics → Summary”可以看总包数、总字节数、平均速率。这让你对实验规模有个概念。第二步看TCP流图“Statistics → TCP Stream Graphs”下面有几个图很有用Time Sequence (Stevens)看序列号随时间的变化能看出重传和乱序。Throughput看吞吐量随时间的变化能看出拥塞控制的宏观效果。Round Trip Time看RTT的变化能看出排队延迟。第三步看具体包用显示过滤器缩小范围。比如只看重传包tcp.analysis.retransmission只看快速重传tcp.analysis.fast_retransmission只看零窗口tcp.analysis.zero_window这些过滤器能帮你快速定位异常事件。我一般会导出一个“关键事件表”类似这样时间戳事件包号说明1.234快速重传1523收到3个重复ACK后重传1.567拥塞窗口减半-cwnd从32降到162.345超时重传2891RTO超时cwnd降到13.456慢启动结束-cwnd达到ssthresh这张表是你写实验报告的核心素材。4.4 实验记录如何整理一份可复现的实验笔记实验做完就忘是很多人的通病。我的做法是每做完一个实验写一份“实验卡片”包含以下字段实验编号3-01实验日期2025-01-15实验目标观察TCP拥塞控制在随机丢包下的行为拓扑描述Sender → Router → ReceiverRouter加30ms延迟和0.5%丢包关键命令tc qdisc add dev eth1 root netem delay 30ms loss 0.5%观察到的现象cwnd从10开始指数增长到约64遇到丢包后降到32然后线性增长与理论的差异初始cwnd不是1而是10快速恢复触发后cwnd没有降到ssthresh以下疑问为什么SACK启用后恢复过程更快下一步对比Reno和CUBIC的拥塞控制行为这份卡片写下来下次做类似实验时直接翻出来看不用重新踩坑。而且当你积累了几十张卡片后你会发现很多知识点是相通的比如TCP的流量控制和拥塞控制都涉及窗口但一个是接收端控制一个是发送端控制。5. 常见问题与排查技巧实录5.1 实验环境类问题速查做网络实验环境问题占了一大半时间。我把常见问题整理成表方便你快速排查现象可能原因排查方法解决方法虚拟机之间ping不通网卡没启用ip link showip link set eth0 upping通但TCP连不上防火墙拦截iptables -L -n放行对应端口抓包看不到流量抓错网卡ip route get 目标IP确认出口网卡tc规则不生效qdisc已存在tc qdisc show先tc qdisc del再添加DNS解析失败缓存干扰dig trace清缓存或换DNSHTTP/2不生效没配SSLcurl -I --http2必须用HTTPS5.2 协议分析类问题为什么抓包结果跟理论不符这是最让人头疼的一类问题。你明明按教科书操作但抓包结果就是不对。我总结了几种常见情况情况一TCP重传了但Wireshark没标记Wireshark判断重传的依据是序列号。如果序列号回绕了比如32位序列号用完了Wireshark可能判断错误。解决办法是启用“Relative sequence numbers”让Wireshark显示相对序列号。情况二看到大量重复ACK但没有快速重传快速重传的触发条件是收到3个重复ACK。如果你只看到2个可能是网络乱序导致的不是丢包。Wireshark的tcp.analysis.duplicate_ack过滤器可以帮你区分。情况三NAT表项存在但回包没转换检查conntrack表里的[ASSURED]标记。如果没有这个标记说明连接还没被确认NAT可能不会正确转换回包。另外检查nf_conntrack_tcp_be_liberal参数如果设为1NAT对TCP状态检查会宽松一些。情况四HTTP/2帧解析乱码Wireshark解析HTTP/2需要密钥。如果是HTTPS你需要配置SSLKEYLOGFILE环境变量让浏览器导出密钥。Chrome和Firefox都支持这个功能export SSLKEYLOGFILE/path/to/sslkeylog.log然后在Wireshark的SSL协议设置里指定这个文件。5.3 性能调优类问题实验跑得慢怎么办网络实验经常需要打流、抓包、分析对机器性能有一定要求。如果实验跑得慢可以从这几个方面优化减少抓包量用显示过滤器而不是捕获过滤器。捕获过滤器在抓包时生效显示过滤器在分析时生效。如果你确定只看TCP 5201端口用捕获过滤器tcp port 5201能大幅减少抓包文件大小。关闭不必要的虚拟机每个虚拟机至少占1G内存如果宿主机只有8G跑三个虚拟机就很吃力了。建议Sender和Receiver用轻量级Linux发行版比如Alpine。用SSD而不是HDD抓包文件写入磁盘很频繁HDD会成为瓶颈。调整Wireshark缓冲区Edit → Preferences → Capture把缓冲区大小从默认的2MB调到64MB减少丢包。提示如果实验涉及大量小包比如DNS查询建议关闭Wireshark的“Update list of packets in real time”等抓完再分析。实时更新会消耗大量CPU。5.4 独家避坑技巧那些实验指导书不会告诉你的事最后分享几个我在做网络实验时总结的“野路子”技巧技巧一用tcpdump做快速验证用Wireshark做深度分析tcpdump在命令行下跑资源占用小适合快速验证“有没有流量”。Wireshark图形界面适合分析协议细节。我的习惯是先用tcpdump确认流量方向再用Wireshark抓包分析。技巧二用ss命令看TCP内部状态Wireshark只能看到线上的包看不到协议栈内部的状态。ss -ti能看到cwnd、rtt、retransmit等内部变量跟抓包结果对照着看理解更深刻。技巧三用mtr代替traceroutemtr结合了ping和traceroute的功能能持续显示每一跳的延迟和丢包。做路由实验时mtr比traceroute好用得多。技巧四实验前先画拓扑图别急着敲命令先在纸上或draw.io上把拓扑画出来标好IP地址、网段、接口。画图的过程就是理清思路的过程。我见过太多人因为IP配错、网段冲突折腾半天才发现是拓扑设计有问题。技巧五保存实验快照虚拟机快照是网络实验的救命稻草。每做完一个步骤打个快照。实验做砸了回滚到上一个快照几秒钟恢复。没有快照你可能得重装系统。技巧六用版本控制管理实验笔记实验笔记用Git管理每次实验提交一次。这样你能看到自己的学习轨迹也能在需要时回退到某个版本。Markdown格式的笔记配合Git比Word文档好用得多。这个系列做到第三卷内容已经相当丰富了。但网络协议栈本身还在演进比如QUIC、HTTP/3、SRv6这些新技术都值得继续加进来。我自己的做法是每学一个新协议就设计一个最小实验抓包验证然后写进笔记。这个过程很慢但每一步都踩得实。