CS144 TCP Labs
文章目录
这篇文章单独记录 CS144 TCP labs 的实现过程。
0. Checkpoint 0:ByteStream
Checkpoint 0 实现的是一个有限容量的 FIFO 字节流。
1 | Writer -> buffer -> Reader |
核心状态:
1 | capacity_ : buffer 最大容量 |
0.1 push
写入时不能超过剩余容量。
1 | void Writer::push( string data ) |
0.2 pop
读取时不能超过当前 buffer 中已有的数据。
1 | void Reader::pop( uint64_t len ) |
0.3 close 与 is_finished
close() 只表示 writer 不再写入;只有 buffer 也被读空,整个 stream 才算 finished。
1 | bool Reader::is_finished() const |
这和 TCP 的 FIN 很像:收到 FIN 后,之前已经收到的数据仍然要继续交给应用层读取。
1. Checkpoint 1:Reassembler
Checkpoint 1 实现的是 Reassembler,用于把乱序、重复、重叠的 substring 重新拼成连续 ByteStream。
接口:
1 | void Reassembler::insert( uint64_t first_index, string data, bool is_last_substring ) |
核心状态:
1 | next_index : 下一个可以写入 ByteStream 的 index |
1.1 已输出数据裁剪
如果收到的数据前缀已经输出过,需要裁掉旧前缀。
1 | const uint64_t current_index = output_.writer().bytes_pushed(); |
1.2 正好接上 next_index
如果新片段正好接上当前 ByteStream 的末尾,就直接写入,并尝试 flush pending。
1 | if ( first_index == next_index() ) { |
2.3 重叠区间合并
重叠数据不能重复保存,需要合并成更大的区间。
1 | new: [3,8) = lowor |
核心代码:
1 | const uint64_t merged_start = min( new_start, old_start ); |
这里的 offset 用来把 stream index 转换成 merged 字符串内部下标。
1.4 遍历时删除 map 元素
如果遍历过程中要删除 map 元素,应使用 iterator,而不是 range-for。
1 | it = pending_.erase( it ); |
erase(it) 会删除当前元素,并返回下一个有效 iterator。
2. Checkpoint 2:TCPReceiver
Checkpoint 2 把 TCP segment 接到 Reassembler 上。
1 | TCPSenderMessage |
2.1 Wrap32 & Unwrap
- Wrap()
Wrap将64位的绝对序列号转换成32位的TCP序列号。
1 | return zero_point + static_cast<uint32_t>(n); |
保留n的低32位,同时加上此时的ISN。
例如:
1 | ISN = 1000 |
如果此时absolute seqno = 2^32 + 5 , TCP seqno仍然是1005.
- Unwrap()
- 计算offset:offset在做什么?
当前TCP序列号相对ISN向前移动了多少。是当前TCP seqno相对于ISN 32位的环形距离,也是绝对序列号的低32位
1 | const uint32_t offset = raw_value_ - zero_point.raw_value_; |
因此只知道offset,无法知道它属于哪一轮。
- checkpoint的作用
如果checkpoint <= offset,offset是距离 checkpoint 最近的非负候选值,可以直接返回。这里不能据此断言实际是否发生过回绕;checkpoint 只是帮助选择候选值的参考位置。
否则需要根据 checkpoint 估算目标绝对序号位于第几个回绕周期。所有候选值都满足 offset + k × 2^32,相邻候选值之间相差 2^32。k 先找到 checkpoint 左边的候选值;余数再判断左边和右边哪一个离 checkpoint 更近。
1 | L = offset + k × 2^32 R = offset + (k + 1) × 2^32 |
图中 remainder 是 checkpoint 到左侧候选值 L 的距离。当它达到半个周期 2^31 时,右侧候选值 R 不再比 L 更远,因此代码选择下一轮。
1 | //offset只是绝对序列号的低32位 |
2.2 TCPReceiver::receive
2.3 TCPReceiver::send
3. Checkpoint 3:TCPSender
4. Network Interface(Checkpoint 5)
保留 Ethernet frame、ARP、IP 与网卡接口,这是嵌入式网络开发的核心内容。
5. IP 路由基础(Checkpoint 6 选学)
只学习路由表、默认网关、TTL 与最长前缀匹配,不要求完整实现路由器项目。
6. End-to-End TCP/IP Integration(Checkpoint 7 精选)
只完成单机端到端联调和数据传输,跳过多人组网与创意项目。