CS144 是一门关于计算机网络的斯坦福大学的公开课。课程的实验是动手写一个用户态的 TCP 协议,本文是第二篇。

  • 课程链接: https://cs144.github.io/

  • 本文讲义: https://cs144.github.io/assignments/lab1.pdf

  • 本文相关代码: https://github.com/hexyoungs/tcplab/tree/lab1

背景知识

数据在网络中的传播是字节流,字节流会被切分为不同大小的数据包,这些包在网络中传输。在网路中,每个包的传输速度、传输路径等等都是不可控的,因此数据包到达的顺序也是不可控的,所以 TCP 协议需要完成这些数据包的重组工作。

讲义

Lab1 算是开篇,介绍了我们实现 TCP 协议的策略,即模块化地实现它,并且划分了接下来几个 Lab 的主要内容和需要实现的模块。

Lab1 的内容是实现一个 Stream 的重组器(reassembler),接收一堆字节片段,然后将他们重组成为顺序正确,完整的内容。

要点

首先简单转换一下问题:输入字符串片段和它们所在的位置,输出一个完整有序的字符串。

这是关键的函数签名

void StreamReassembler::push_substring(const string &data, const size_t index, const bool eof)

index 是 data 的第一个字节在原数据的位置,假设所有数据按照正确的顺序到达,并且不存在重传的数据包,那么就什么都不用管,直接往目的地写数据就行了。

为了达到这种假设

  • 首先,可以使用最小堆,每来一条数据,就按照 index 插入进堆,如果堆顶部数据的 index 正好是需要的,那就取出来,写到目的地。如果不是,说明想要的数据片段还没有到,还需要再等等。
  • 其次要考虑到边界情况,比如包可能产生重传,也可能出现 overlap 的现象

整体按照这个大思路,使用测试用例作为辅助,实现即可。

碎片笔记

实现过程中,下面几点是我个人不太熟悉的部分

  1. 使用 make_heappush_heappop_heap 操作的目标是一个复杂对象时,可以传入自定义的比较器。
  2. 使用 pop_heap 还需要配合容器的 pop 比如配合 vector 的 pop_back
  3. 想要截取子字符串一定要通过 string 的 substr 方法,不要试图直接利用 c_str 的内容加 offset 的方式,这样可能会丢失需要写入的数据(debug 了好一阵子才发现)。

结语

Lab1 需要实现的接口依旧很少,需要考虑的更多是边界情况,有测试用例的情况下还是相对容易的。难度开始递进了,期待下一个 Lab..

另外,完整实现在 repo 的 lab1-ans 分支供同学们参考。