CS144 Lab 2

by on under CS144
2 minute read

上一个实验:Lab 1

这篇文章会讲解 CS144 Lab 2 的思路和代码。

Lab 2

这个 Lab 的目标是写一个 TCP receiver,它支持三个功能:

  • 可以接受一个 TCPSegment,并把其中的数据传给上一个 Lab 实现的 StreamReassembler;
  • 支持查询 ackno,也就是被排列好的最后一个字符的序数 +1;
  • 支持查询 window_size,也就是滑动窗口里剩下的空间。

git merge origin/lab2-startercode 合并 Lab 2 的代码后,我们就可以开始啦。

Translating between 64-bit indexes and 32-bit seqnos

为了达成这三个目的,我们需要先弄清楚这三个序数的定义:

  • Sequence Numbers, 记为 SN
  • Absolute Sequence Numbers,记为 ASN
  • Stream Indices,记为 SI

这三个数的定义在原文档我就不复制了,简单说一下他们的关系:

  • SN = (ASN + ISN) & (0xFFFFFFFF) (取最低32位)
  • SI = ASN - 1

我们还需要一个把 WrappingInt32 转换为 uint54_t 的转换函数:

uint64_t wrappingInt32_to_uint64(WrappingInt32 n) { 
    return static_cast<uint64_t>(n.raw_value()); 
}

在它的帮助下,我们可以写出 wrap 函数:

WrappingInt32 wrap(uint64_t n, WrappingInt32 isn) {
    uint64_t res = n + wrappingInt32_to_uint64(isn);
    res &= 0xFFFFFFFF;
    return WrappingInt32{static_cast<uint32_t>(res)};
}

unwrap 函数稍微复杂一些。由于 CS144 lab 都是有测试例的,所以即使一些方法可以帮助我们完成主要目标(指实现 TCPReceiver),但想完成 Lab 的话不能绕过这些测试例。比如这里,其实我们可以返回比 checkpoint 大的最小满足条件的数,但是由于 Lab 已经规定好了 unwrap 是寻找最接近的数,因此还是得按要求来。参考代码如下:

uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint) {
    uint64_t res = wrappingInt32_to_uint64(n) - wrappingInt32_to_uint64(isn);
    res &= 0xFFFFFFFF;
    uint64_t first_part = checkpoint & 0xFFFFFFFF00000000;
    res |= first_part;
    uint64_t alternative = res;
    if (res < checkpoint) {
        alternative = res + (1ul << 32);
        return alternative - checkpoint >= checkpoint - res ? res : alternative;
    } else if (res > checkpoint) {
        if (res < (1ul << 32))
            return res;
        alternative = res - (1ul << 32);
        return checkpoint - alternative >= res - checkpoint ? res : alternative;
    }
    return res;
}

这部分可以在 build 目录下编译并测试,和之前类似:

$ CXX=g++-8 cmake ..
$ make format
$ make -j4
$ ctest -R wrap

通过的话会得到类似字样:

100% tests passed, 0 tests failed out of 4

Total Test time (real) =   0.14 sec

在这些函数的帮助下,我们可以开始实现 TCPreceiver 了。

Implementing the TCP receiver

正如同文档所说,这个 Lab 没有什么需要构造的数据结构,但是我们需要对很多数值的定义小心。 由于 ISN 指代的是 SYN 的 SN,所以除了第一个带有 SYN 的 TCPsegment 以外,在我们计算其他的 TCPSegment 的 payload 对应的 SI 时,都需要减一。另外需要注意的是如果没有收到 SYN 信号的话,我们应该抛弃所有的 TCPSegment,因为这个时候我们的 TCPReceiver 还没准备好接受分段,这里我们维护变量 initFlag 来记载是否接收到了 SYN

参考代码:

void TCPReceiver::segment_received(const TCPSegment &seg) {
    if (seg.header().syn) {
        // initialize ISN
        ISN = seg.header().seqno;
        initFlag = true;
    } else if (!initFlag) {
        return;
    }
    size_t stream_idx = unwrap(seg.header().seqno, ISN, _reassembler.stream_out().bytes_written());
    if (!seg.header().syn)
        stream_idx -= 1;
    _reassembler.push_substring(seg.payload().copy(), stream_idx, seg.header().fin);
}

optional<WrappingInt32> TCPReceiver::ackno() const {
    size_t abs_idx = _reassembler.stream_out().bytes_written();
    if (!initFlag)
        return {};
    abs_idx++;
    if (_reassembler.stream_out().input_ended())
        abs_idx++;
    return wrap(abs_idx, ISN);
}

size_t TCPReceiver::window_size() const { 
    return _reassembler.stream_out().remaining_capacity(); 
}

完成后我们在build目录下编译并测试,和之前类似:

$ CXX=g++-8 cmake ..
$ make format
$ make -j4
$ make check_lab2

通过的话会得到类似字样:

100% tests passed, 0 tests failed out of 26

Total Test time (real) =   0.82 sec

下一个实验:Lab 3

CS144, computer networking
comments powered by Disqus