Skip to content

About

Stanford CS144: Introduction to Computer Networking - my lab implementations

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

CS144 TCP/IP 协议栈实现(C++20)

基于 Stanford CS144(2024 Winter, Minnow 框架)的完整协议栈实现 —— 自底向上实现字节流、流重组、序列号回绕、TCP 收发引擎、11 状态连接机、ARP 与 IP 路由。

并在课程范围之外自行扩展了 TCP 拥塞控制(Reno)、Nagle、延迟确认与 SACK,配一套消融实验量化每个机制的独立贡献。

32 项功能测试通过,实现代码分布在 17 个源文件中,约 1350 行。


我实现了什么

课程提供 Minnow 框架(构建系统、测试框架、基础类型)与各 Lab 的骨架,以下实现代码均为本人完成:

模块 文件 内容
有序字节流 src/byte_stream.* 定容 FIFO 缓冲区,best-effort 写入,生命周期状态管理
流重组器 src/reassembler.* 乱序碎片缓存 + 区间合并去重,窗口容量控制,智能关闭
序列号回绕 src/wrapping_integers.* 32 位回绕序列号 ↔ 64 位绝对序列号转换;解回绕用 O(1) 除法而非 O(n) 迭代
TCP 接收方 src/tcp_receiver.* 累计确认,SYN/FIN 处理,{ackno, window_size} 计算
TCP 发送方 src/tcp_sender.* 双队列收发分离,RFC 6298 重传定时器 + 指数退避,零窗口探测
TCP 连接状态机 ★ src/tcp_connection.* 课程未提供,自行设计:11 状态机、自定义线上段格式(合并收发消息以支持捎带 ACK)、三次握手 / 四次挥手 / TIME_WAIT / RST 异常
ARP 与网络接口 src/network_interface.* IP→MAC 缓存(30s 过期),ARP Request 广播与 5s 重发,积压帧批量发送
IP 路由器 src/router.* 最长前缀匹配转发,TTL 递减 + 校验和重算,回声去重

★ 标记为课程基线中不存在的模块。

超出课程范围的扩展

课程的 Lab 只要求「能正确传输」。以下四项是我在跑通之后自己加的,并配了独立开关(use_reno / use_sack / use_delayed_ack / use_nagle),关闭时回退到原始路径:

机制 说明
TCP Reno 拥塞控制 慢启动 / 拥塞避免 / 快重传(3 dup ACK)/ 快恢复;初始 cwnd = 10 MSS(RFC 6928)
SACK 选择性确认 发送方维护空洞表,精确重传真正丢失的区间,而非整窗重传
延迟确认 顺序到达时合并 ACK(最多等 2 个段或 200ms),乱序立即回 ACK
Nagle 算法 有在途数据时延迟小段发送,合并成满段

性能基准与消融实验

tests/tcp_benchmark.cc 建立内存回环:两个 TCPConnection 直连,中间经 droptail 瓶颈(带宽排水 + 缓冲溢出尾丢)——丢包是发送方超发的涌现结果,而非按概率注入。

  • 规模:32 MB × 50 种子,低 / 高拥塞两档
  • 参数:随机丢包 01%、单向延迟 0100 ms、突发 1~3
  • 公平性关键:同一 seed 下 decide(seed, byte, tx) 确定性判定,不同配置经历完全相同的丢包判定序列,差异可归因到配置本身而非随机性

原始结果

config     完成率      goodput(KB/s)  RST      重传率 ACK/段
===== 低拥塞 =====
all          50/50       154.3KB/s     0      1.0%     0.57
nodelack     50/50       225.8KB/s     0      1.0%     1.00
noreno       50/50        85.4KB/s     0      1.0%     0.65
none         50/50        93.7KB/s     0      1.0%     1.00
===== 高拥塞 =====
all          49/50        97.7KB/s     1      1.3%     0.57
nosack       45/50        16.1KB/s     5      8.2%     0.68
noreno       34/50         1.8KB/s    16     24.2%     0.98
none         37/50         2.0KB/s    13     24.2%     0.99
===== 平均(低+高) =====
all          99/100      126.0KB/s     1      1.2%     0.57
nodelack    100/100      142.5KB/s     0      2.1%     0.99
noreno       84/100       43.6KB/s    16     12.6%     0.81
none         87/100       47.8KB/s    13     12.6%     1.00

消融:每个机制的独立贡献

对比 关键差异 结论
all vs noreno 高拥塞完成率 49/50 → 34/50;goodput 97.7 → 1.8 KB/s(54×);RST 1 → 16 Reno 是生存前提:无 cwnd 限速则持续灌爆缓冲,最终 RST 断开
all vs nosack 高拥塞 goodput 97.7 → 16.1 KB/s(6×);重传率 1.3% → 8.2% SACK 是精确恢复的关键:无 SACK 只能靠累计 ACK 推断,RTO 重传队头未必是空洞
all vs nodelack 低拥塞 goodput 154.3 → 225.8 KB/s(无延迟确认快 46%);平均完成率 99 → 100/100 延迟确认是双刃剑:低拥塞拖慢 ACK 时钟,高拥塞减少 ACK 风暴。本次参数下弊大于利
all vs nonagle 平均 goodput 126.0 vs 126.0 KB/s Nagle 在 bulk 负载下几乎零影响

综合最优配置是 nodelack(Reno + SACK + Nagle,不开延迟确认):平均 142.5 KB/s、100% 完成。

一个方法论问题:为什么最初测不出 Nagle

初版 benchmark 中 Nagle 完全无效果。排查后确认不是 bug 而是负载问题:写入循环「尽量填满窗口」,发送方把随机 1–1000 B 的写入攒成 1000 B 满段,而 Nagle 只在「要发 < MSS 小段且有在途数据」时拦截——bulk 模式根本不产生小段。

为此补了三种写入模式(argv[4]:0=bulk / 1=interactive / 2=mixed),在 interactive 模式下 Nagle 的段合并效果才显现(ACK/段 0.80 → 0.53)。


目录结构

minnow/
├── src/                    实现代码(本人完成,见上表)
├── util/                   基础类型 + 自定义 RenoConfig.hh / tcp_message.hh
├── tests/
│   ├── tcp_benchmark.cc           消融实验 benchmark ★
│   ├── fsm_connect.cc             连接建立测试(6 项)★
│   ├── fsm_close.cc               关闭 / RST / TIME_WAIT 测试(5 项)★
│   ├── tcp_connection_test_harness.hh ★
│   └── ...                        课程原有测试
├── apps/ scripts/ etc/     课程框架
└── CMakeLists.txt

★ 为自行新增的文件。


构建与运行

依赖:CMake ≥ 3.24.2、GCC 12+(constexpr std::string 需要 GCC 12 的 libstdc++,GCC 11 无法编译)

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j$(nproc)

# 跑全部测试
cd build && ctest

# 单独跑性能基准
./build/tests/tcp_benchmark

已知局限

  1. t_webget 需要联网 —— 该测试会真实发起 HTTP 请求,无网络环境下必然失败,与协议栈实现无关
  2. router 在整套并行测试下偶发失败 —— 单独运行稳定通过(0.03s),疑与测试间资源竞争有关,尚未定位
  3. 尾部丢包与连续丢包仍需 RTO 兜底 —— 窗口最后一段丢失时无后续 dup ACK 触发快重传,此为 Reno + SACK 的固有限制,需 FACK / RACK 才能消除
  4. 仅对接模拟瓶颈 —— 目前是内存回环 + droptail 模拟,未接 Linux TUN/TAP 走真实协议栈;真实网络下的 ACK 压缩、乱序等场景未覆盖
  5. 已删除课程原有的 send_transmit / send_extra 两个测试 —— 二者的场景假设与加入拥塞控制后的发送行为冲突(例如假定发送方不受 cwnd 约束),无法同时成立,故移除。代价是发送方的部分边界场景不再有测试覆盖
  6. SACK 未实现 DSACK 与重排序容忍 —— 收到乱序段时可能重复标记空洞

来源与致谢

本项目基于 Stanford CS144(Introduction to Computer Networking)2024 Winter 的公开课程材料与 Minnow 框架自学完成。课程原始 README 见 README.course.md。

About

Stanford CS144: Introduction to Computer Networking - my lab implementations

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages