实验总览

官方文档对实验的描述如下:

In Lab 2, you will implement the TCPReceiver, the part of a TCP implementation that handles the incoming byte stream. The TCPReceiver translates between incoming TCP segments (the payloads of datagrams carried over the Internet) and the incoming byte stream.

The TCPReceiver receives segments from the Internet (via the segment received()method) and turns them into calls to your StreamReassembler, which eventually writes to the incoming ByteStream. Applications read from this ByteStream, just as you did in Lab 0 by reading from the TCPSocket.

在 Lab2 中,我们要完成 TCP 框架中的TCPReceiver组件(其中包括我们已经实现的SreamAssemblerByteStream模块)。这个组件的功能有三:

  1. 接受数据报分片TCPSegement(the actual datagram payloads),并且将其数据报中的数据提取出来,输入到我们上个实验实现的StreamAssembler中。
  2. 实现流量控制(flow control),因此我们要不断向TCPSender报告滑动窗口(sliding window)大小。
  3. 通过segment的序列号保障数据的可靠传输。

第一部分:64-bits 与 32-bits 序列号间转换

为什么需要做转换

  1. TCP 报文段的序列号(sequence number)字段的最大长度是 32 字节,而我们的逻辑数据流的每个字节序号(absolute sequence number)的最大长度是 64 字节,所以sequence number在达到最大23212^{32}-1后会重新从00开始循环。
  2. 为了安全考虑,在 TCP 连接的三次握手阶段,receiver会随机初始化序号isn(initial sequence number, 32-bits), 而我们的逻辑流的第一个序号(64-bits)永远是 0。
    下表来自文档,表示只包含"cat"三个字节的字节流:seqno

转换的接口

1
2
3
4
// convert absolute seqno(64-bits) to seqno(32-bits)
WrappingInt32 wrap(uint64_t n, WrappingInt32 isn)
// convert seqno(32-bits) to absolute seqno(64-bits)
uint64_t unwrap(WrappingInt32_n, WrappingInt32_isn, uint64_t checkpoint)

实现时需要注意的是,SYNFIN也是占据一个序列号的,虽然它们不是一个报文段也不是表示 payload 数据的字节,仅表示序列号的起始和结尾!具体的实现参考了CS144 Lab:Lab2 – LRL52 的博客

第二部分:完善 TCP receiver 的逻辑

evolution
如上图所示,TCP receiver有四种状态:

  • LISTEN:初始化receiver后还未与sender进行 three-way hand shake,相当与client-server模型中的server处于监听状态,监听来自client的连接请求。此时SYN初始化序列号还未确定,所以无法进行接发包。
  • SYN_RECV:处于数据交换阶段,还未接受到包含FIN字段的数据包,因此连接持续。
  • FIN_RECV:接受到FIN的数据包,而且通过重组已经输入到ByteStream中,数据传输已经完成,不再接受数据报。
  • ERROR: 错误状态。

测试结果

test

总结

实验的第一部分涉及绝对序号 (64-bits)和流序号 (32-bits)之间的转换,需要注意的细节还是很多的。第二部分我花了很多时间精力去看tcp_headertcp_segment的接口,搞的很迷糊,最后还是没忍住去参考了网上的实现,发现基本上就没有用到。。我觉得还是自己在这方面的理论没有巩固得特别好,所以这个实验从头到尾都做地磕磕绊绊。
框架
回到 TCP 结构图中看这个实验,我们实现了TCPReceiever,它将接受从Sender传来的 TCP 报文段,从中提取负载的报文并输入到之前实现的Reassembler中。为了实现流量控制,我们还计算了当前滑动窗口的大小,但是并没有报告给Sender,这会在下一个实验实现。