“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第n篇文章,点击查看活动详情”
1. 要求
在我们所实现的流重组器中,有以下几种特性:
-
接收子字符串。这些子字符串中包含了一串字节,以及该字符串在总的数据流中的第一个字节的索引。
-
流的每个字节都有自己唯一的索引,从零开始向上计数。
-
StreamReassembler 中存在一个 ByteStream 用于输出,当重组器知道了流的下一个字节,它就会将其写入至 ByteStream中。
需要注意的是,传入的子串中:
-
子串之间可能相互重复,存在重叠部分
但假设重叠部分数据完全重复。
不存在某些 index 下的数据在某个子串中是一种数据,在另一个子串里又是另一种数据。
重叠部分的处理最为麻烦。
-
可能会传一些已经被装配了的数据
-
如果 ByteStream 已满,则必须暂停装配,将未装配数据暂时保存起来
其中, ByteStream 是我们已经在 Lab0 中实现完成的。
我们将在接下来的实验中分别实现:
- Lab1
StreamReassembler:实现一个流重组器,一个将字节流的字串或者小段按照正确顺序来拼接回连续字节流的模块 - Lab2
TCPReceiver:实现入站字节流的TCP部分。 - Lab3
TCPSender:实现出站字节流的TCP部分。 - Lab4
TCPConnection: 结合之前的工作来创建一个有效的 TCP 实现。最后我们可以使用这个 TCP 实现来和真实世界的服务器进行通信。
该实验引导我们以模块化的方式构建一个 TCP 实现。
流重组器在 TCP 起到了相当重要的作用。迫于网络环境的限制,TCP 发送者会将数据切割成一个个小段的数据分批发送。但这就可能带来一些新的问题:数据在网络中传输时可能丢失、重排、多次重传等等。而TCP接收者就必须通过流重组器,将接收到的这些重排重传等等的数据包重新组装成新的连续字节流。
StreamReassembler
class StreamReassembler {
private:
std::map<size_t, std::string> _unassemble_strs;
size_t _next_assembled_idx;//下一个需要重组的idx,可认为idx前的字符都是已经完成重组的
size_t _unassembled_bytes_num;//还未重组的字符数
size_t _eof_idx; //结束的idx
ByteStream _output; //将重组后的data存入字符流中
size_t _capacity; //_output的容量
public:
StreamReassembler(const size_t capacity);
void push_substring(const std::string &data, const uint64_t index, const bool eof);//将数据写入字节流中的特定索引。
const ByteStream &stream_out() const { return _output; }
ByteStream &stream_out() { return _output; }//字节流
size_t unassembled_bytes() const;//没重组好的字符数
bool empty() const;//没重组好的字符数是否为0
};