2. 把收到的字符串按顺序存放
这个lab和下一个lab将会实现一个TCP receiver:将得到的数据报转换成可靠的字节流,提供给socket读取。
TCP sender会把字节流转换成短的TCP 段(不超过1460字节),但是网络传输过程中,这些TCP段可能会乱序、丢失或者重复。receiver必须重新排序,得到连续的字节流。
TCP receiver收到的数据报,会同时得到这个数据报首字节的序列号。
总的来说,要实现下面的函数接口:
/*
* Insert a new substring to be reassembled into a ByteStream.
* `first_index`: the index of the first byte of the substring
* `data`: the substring itself
* `is_last_substring`: this substring represents the end of the stream
* `output`: a mutable reference to the Writer
*
* The Reassembler's job is to reassemble the indexed substrings (possibly out-of-order
* and possibly overlapping) back into the original ByteStream. As soon as the Reassembler
* learns the next byte in the stream, it should write it to the output.
*
* If the Reassembler learns about bytes that fit within the stream's available capacity
* but can't yet be written (because earlier bytes remain unknown), it should store them
* internally until the gaps are filled in.
*
* The Reassembler should discard any bytes that lie beyond the stream's available capacity
* (i.e., bytes that couldn't be written even if earlier gaps get filled in).
*
* The Reassembler should close the stream after writing the last byte.
*/
void insert( uint64_t first_index, std::string data, bool is_last_substring, Writer& output );
// How many bytes are stored in the Reassembler itself?
uint64_t bytes_pending() const;
2.1 如何实现
insert函数告知一个新的数据流的到来,并且序列号表明这个字节流在整个流中的位置。
- 如果接收到了刚好是下一个数据流,应该立即写入到Writer
- 当前数据流在接收范围内,但是由于之前的字节流还没有收到,因此要缓存
- 当前数据流在接受范围外,应该丢弃
实现这些行为的目的是限制缓存大小,下图表示了接收时可能存在的情况:
- 蓝色区域表示已经被读取的字节范围
- 绿色区域表示在缓存中,可以读取的字节范围
- 红色区域表示在缓存中,乱序的字节范围
2.2 问题
- 整个流中,首字节的索引?当前lab中是0(实际上是随机初始化的序列号)。
- 什么时候应该将字节写入流? 越快越好。一个字节不应该出现在流中的唯一情况是,在它之前还有一个字节尚未被写入。
- 提供给insert的字符串会产生重叠吗?会的,模拟重传。
3. 实现
3.1
下图是我理解的实现的TCP receiver的框架。内核接收到发送的数据报后,直接交给socket,socket根据读缓存的大小决定是否接收这个数据报(还负责把字节整理成有序)。应用程序通过socket读取有序的字节流。
3.2
下图是lab0实现的Reader和Writer类,它们的功能比较简单,读取和写入的数据都是默认有序的。目前来看,Tcp receiver会同时存在一个Reader对象和一个Writer对象。
3.3
所以必须还有一个类来把乱序的数据报转换成有序,也就是lab1实现的Reassembler类。所有从内核发送来的数据都需要经过这个类,转换成有序字节流后,再交由Writer对象写入。
3.4
下图是内核发来的数据报的可能范围,需要将接受范围内的字符保存下来。
3.5
实现方法是使用双向队列按顺序保存每个字符,以及记录当前位置字符是否有效。