lab1
个人实验留档:cs144-2021
Overview
通过上面这个图,可以看到这个实验要做的部分:在tcp将数据报文传输过来时,由于报文会存在乱序、重复的问题,所以需要在接收端将报文排序成有序的数据报,然后在传输给在Lab0中的ByteStream
Putting substrings in sequence
对于传输过来的数据报,其拥有唯一的 index 标识其第一个字节在原始的、未被分割的数据中的位置。
传输过来的数据会出现重复、部分重叠问题,需要在实现过程中考虑。
上面这个图表示的是capacity的范围,即,capacity应该是byteStream中写入还没被读的容量(即byteStream中的队列容量)+ 未被重排的数据。
具体实现
这一部分存储unassembled的数据一开始打算使用map<size_t,string>的数据结构来进行存储,但是在实现过程中发现有太多情况比如子串重叠、重复等需要覆盖,面向测试用例编程但是最后也没有通过所有测试。最后放弃重新改为类似于lab1中使用deque来进行存储。
- 私有变量设置
class StreamReassembler {
private:
// Your code here -- add private members as necessary.
// index->(data, origin_data_size)
std::deque<char> _unassembled_queue;
std::deque<bool> _flags_queue; // 标记queue中的对应位置是否有数据
bool _is_eof;
size_t _eof_idx;
size_t _unassembled_size;
ByteStream _output; //!< The reassembled in-order byte stream
size_t _capacity; //!< The maximum number of bytes
//。。。。后面省略
- 成员函数实现
StreamReassembler::StreamReassembler(const size_t capacity)
: _unassembled_queue(capacity, '\0')
, _flags_queue(capacity, false)
, _is_eof(false)
, _eof_idx(0)
, _unassembled_size(0)
, _output(capacity)
, _capacity(capacity) {}
//! \details This function accepts a substring (aka a segment) of bytes,
//! possibly out-of-order, from the logical stream, and assembles any newly
//! contiguous substrings and writes them into the output stream in order.
void StreamReassembler::push_substring(const string &data, const size_t index, const bool eof) {
// substrings provided to the push substring() function may overlap
size_t first_unassembled = _output.bytes_written();
size_t first_unaccept = _output.bytes_read() + _capacity;
// 处理那些在范围之外内的数据
// 注意,有可能数据在范围之外,但是其 eof 标志位为1,因此,对于在范围之外的数据不能简单丢弃
if (!(index >= first_unaccept || index + data.size() <= first_unassembled)) {
size_t begin_idx = max(index, first_unassembled);
size_t end_index = min(first_unaccept, index + data.size());
// 将数据存放到 _unassembled_queue 中
for (size_t i = begin_idx; i < end_index; i++) {
if (!_flags_queue[i - first_unassembled]) {
_unassembled_queue[i - first_unassembled] = data[i - index];
_unassembled_size++;
_flags_queue[i - first_unassembled] = true;
}
}
// 将有序的数据写入 ByteStream
string to_write = "";
while (_flags_queue.front() && to_write.size() < _output.remaining_capacity()) {
to_write += _unassembled_queue.front();
_unassembled_queue.pop_front();
_flags_queue.pop_front();
// 入队占位,保持队列容量不变,类似滑动窗口
_unassembled_queue.emplace_back('\0');
_flags_queue.emplace_back(false);
}
if (to_write.size() > 0) {
_unassembled_size -= to_write.size();
_output.write(to_write);
}
}
if (eof) {
_eof_idx = index + data.size();
_is_eof = true;
}
if (_is_eof && _eof_idx == _output.bytes_written()) {
_output.end_input();
}
}
size_t StreamReassembler::unassembled_bytes() const { return _unassembled_size; }
bool StreamReassembler::empty() const { return unassembled_bytes() == 0; }class StreamReassembler {
private:
// Your code here -- add private members as necessary.
std::deque<char> _unassembled_queue;
std::deque<bool> _flags_queue; // 标记是否有数据
bool _is_eof;
size_t _eof_idx;
size_t _unassembled_size;
ByteStream _output; //!< The reassembled in-order byte stream
size_t _capacity; //!< The maximum number of bytes
- 注意在实现时,对于不在范围内的数据不能立即return,因为此时参数中的eof标志位可能是有效的,因此需要更新eof_index。
测试
总结
这个实验在难度上不算难,重要的是如何实现、用什么样的数据结构以及对于边界条件的覆盖。