1 介绍
在链路层,由传输介质的物理特性决定了数据帧的长度。
以以太网为例,链路层一个数据帧最大为 1518Byte,首部长18Byte,IP层首部长20Byte,传输层 UDP首部长 8Byte,TCP首部长 20Byte。因此TCP包的数据内容最大为 1460 Byte。
而传输数据方的数据是很可能大于1460的,所以会拆分成多个数据包,因此Receiver需要去实现数据包的Reassembler,而这就是lab 1需要完成的内容。
Capacity
PDF中强调了capacity的含义:
capacity表示了整个Reassembler可以存放的数据流的大小,这个数据流包括 ByteStream 以及 缓存的数据流。
所以之前lab0中采用string当作ByteStream当作底层数据结构就不再合适了,因此改为 deque。比较简单,就不贴代码了。
2 添加成员
- 添加了一个内部类, 这个类是用来存放还未写入ByteStream的数据。其中对 操作符 < 进行了重载,用于set的排序。
- _nowIndex:用来表示需要发送者发来的str的index值。
- _inorderedStr:用set去装缓存的str。
- _output:读写的流。
- _capacity:容量。
- _sizeOfUnassembledBytes:set中str的数据量之和。
- remaining_capacity():剩余容量。
- merge_inorderedStr():将新的Str放入set,并处理合并以及删除的逻辑。
// stream_reassembler.hh
private:
// Your code here -- add private members as necessary.
class StoreSubstr{
public:
std::string _data{};
size_t _index{};
bool _eof{};
StoreSubstr(std::string data,const size_t index,const bool eof){
_data = std::move(data);
_index = index;
_eof = eof;
}
bool operator < (const StoreSubstr &b) const{
return _index == b._index ? _index + _data.size() < b._index + b._data.size() : _index < b._index;
}
};
size_t _nowIndex{}; //!< The index needed now
std::set<StoreSubstr> _inorderedStr{}; //!< Store the inordered substr
std::set<size_t> _indexIn{}; //!< Represent the index in the storage
ByteStream _output; //!< The reassembled in-order byte stream
size_t _capacity{}; //!< The maximum number of bytes
size_t _sizeOfUnassembledBytes{};
size_t remaining_capacity() const;
//!< Insert the new Str in the storage
void merge_inorderedStr(const std::string &data, const uint64_t index, const bool eof);
3 接口的实现
实现的主要逻辑都在 merge_inorderedStr()里面。这些接口的内容都很简单,除了push_substring中会有部分简单处理。
push_substring() 先是预处理data,处理完之后就调用merge_inorderedStr(),然后再进行write写入。
StreamReassembler::StreamReassembler(const size_t capacity) : _output(capacity), _capacity(capacity) {
_nowIndex = 0;
_sizeOfUnassembledBytes = 0;
}
//! \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) {
// If the data has been writen in,just discard it,
// and we will accept index + data.size() == _nowIndex,
// because it may carry the info about eof.
if(index + data.size() < _nowIndex)
return;
std::string newStr,ttp;
size_t newIndex;
ttp = data;
// If the index < _nowIndex,truncate the str
if(index < _nowIndex){
newStr = data.substr(_nowIndex-index);
newIndex = _nowIndex;
}
else{
newStr = data;
newIndex = index;
}
merge_inorderedStr(newStr,newIndex,eof);
// If the first str can be write in.
// Because the eof may cause the break part.
while(!_inorderedStr.empty() && _inorderedStr.begin()->_index == _nowIndex){
auto it = _inorderedStr.begin();
_output.write(it -> _data);
if(it -> _eof){
_output.end_input();
}
_nowIndex = it -> _index + it -> _data.size();
_sizeOfUnassembledBytes -= it -> _data.size();
_inorderedStr.erase(it);
}
}
size_t StreamReassembler::unassembled_bytes() const { return _sizeOfUnassembledBytes; }
bool StreamReassembler::empty() const { return _sizeOfUnassembledBytes == 0; }
size_t StreamReassembler::remaining_capacity() const{
return _output.remaining_capacity() - _sizeOfUnassembledBytes;
}
4 merge_inorderedStr()
merge需要考虑的问题:
- 插入后超出capacity。
- 需要删除最后面部分,来满足capacity。
- 插入的合并问题。
- 数据的重复问题。
实现逻辑:
- 直接插入新的StoreSubstr,然后找到它的位置。
- 考虑其是否会合并到前一个StoreSubstr,如果会的话,就从前一个开始。
- 新建一个新StoreSubstr来存储合并后的内容。
- 复制完毕后,删除原StoreSubstr,插入新的StoreSubstr。这边要特别注意到 长度为0的eof的情况。
- 检查是否超出capacity,如果超出就从后面往前面删。
void merge_inorderedStr(const std::string &data, const uint64_t index, const bool eof){
StoreSubstr tmp = StoreSubstr(data,index,eof);
_inorderedStr.insert(tmp);
_sizeOfUnassembledBytes += data.size();
auto it = _inorderedStr.find(tmp);
size_t la = 0;
std::string str;
uint64_t newIndex;
bool newEof = false;
// merge front one
if(it != _inorderedStr.begin()){
it--;
if(it->_index + it->_data.size() <= index)
it++;
}
newIndex = it->_index;
// for erase
auto beg = it;
while(true){
// copy to new str
la = std::max(it->_index + it->_data.size(),la);
for(size_t i = newIndex + str.size() - it->_index;i < it->_data.size();i++)
str.push_back(it->_data[i]);
_sizeOfUnassembledBytes -= it->_data.size();
// if(it->_eof){
// // [begin,end)
// it++;
// newEof = true;
// break;
// }
newEof = it->_eof;
it++;
// If not continuous,break;
if(it->_index > la || (it -> _index == la && it ->_data.size() == 0))
break;
}
// erase the merged ones
_inorderedStr.erase(beg,it);
_inorderedStr.insert(StoreSubstr(str,newIndex,newEof));
_sizeOfUnassembledBytes += str.size();
// delete the oversize part
while(_output.remaining_capacity() < _sizeOfUnassembledBytes){
size_t oversize = _sizeOfUnassembledBytes - _output.remaining_capacity();
it = _inorderedStr.cbegin();
// If it is enough,delete part of it
if(it -> _data.size() > oversize){
size_t oriSize = it->_data.size();
std::string tmp_val = it -> _data.substr(0,oriSize - oversize);
_inorderedStr.insert(StoreSubstr(tmp_val,it->_index,false));
_sizeOfUnassembledBytes -= oversize;
_inorderedStr.erase(it);
}
// else delete it;
else{
_sizeOfUnassembledBytes -= it->_data.size();
_inorderedStr.erase(it);
}
}
}
tr.erase(it);
}
// else delete it;
else{
_sizeOfUnassembledBytes -= it->_data.size();
_inorderedStr.erase(it);
}
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/d6a4174f9ea844cfafdd610b5c46a071.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzE3NDQ3Nw==,size_16,color_FFFFFF,t_70#pic_center)