TCP实施中模块和数据流的排列 :
字节流是Lab0。TCP的工作是通过不可靠的数据报网络传输两个字节流(每个方向一个),以便写入连接一侧套接字的字节显示为可在对等端读取的字节,反之亦然。Lab1是StreamReAssemer
,在Lab2、3和4中,您将实施TCPReceiver
、TCPSender
,然后实施 TCPConnection
将它们连接在一起。
在Lab1中,您将实现一个流重组器-该模块将字节流的一小部分(称为子串或段)按正确的顺序缝合回连续的字节流。
在Lab2中,您将实现TCP中处理入站字节流的部分:TCPReceiver
。这涉及到考虑TCP将如何表示流中每个字节的位置-称为“序列号”。TCPReceiver
负责告诉发送者(A)它已经能够成功组装多少入站字节流(这称为“确认”)和(B)发送者现在被允许发送多少字节(“flow控制”)。(B)TCPReceiver
负责告诉发送者(A)它已经能够成功组装多少入站字节流(这称为“确认”)和(B)允许发送者现在发送多少字节(“flow control”)。
在Lab3中,您将实现TCP中处理出站字节流的部分:TCPSender
。当发送方怀疑其传输的数据段在途中丢失并且从未到达接收方时,它应该如何反应?它应该在什么时候重试并重新传输丢失的数据段?
在Lab4中,您将结合前面的工作和Lab来创建工作的TCP实现:包含TCPSender
和 TCPReceiver
的 TCPConnection
。您将使用它与世界各地的真实服务器进行对话。
您的Push Substring
方法将忽略会导致 StreamReAssembly
超出其“容量”的字符串的任何部分:内存使用限制,即允许它存储的最大字节数。这可以防止重新组装器使用无限量的内存,无论TCP发送器决定执行什么操作。我们已经在下面的图片中对此进行了说明。“容量”是两者的上限:
重组的ByteStream
中的字节数(如下绿色所示),以及
“unassembled
”的子字符串可以使用的最大字节数(以红色显示)
re-assembler
保存在辅助存储器中的已接收字节re-assembler
保存在字节流中的已接收字节数在我们所实现的 StreamReassembler
中,有以下几种特性:
StreamReassembler
中存在一个 ByteStream 用于输出,当 StreamReassembler
知道了流的下一个字节,它就会将其写入至 ByteStream
中。需要注意的是,传入的子串中:
子串之间可能相互重复,存在重叠部分
但假设重叠部分数据完全重复。
不存在某些 index 下的数据在某个子串中是一种数据,在另一个子串里又是另一种数据。
重叠部分的处理最为麻烦。
可能会传一些已经被装配了的数据
如果 ByteStream 已满,则必须暂停装配,将未装配数据暂时保存起来
除了上面的要求以外,容量 Capacity 需要严格限制:
为了便于说明,将图中的绿色区域称为 ByteStream,将图中**存放红色区域的内存范围(即 first unassembled - first unacceptable)**称为 Unassembled_strs。
CS144 要求将 ByteStream + Unassembled_strs 的内存占用总和限制在 Reassember 中构造函数传入的 capacity 大小。因此我们在构造 Reassembler 时,需要既将传入的 capacity 参数设置为 ByteStream
的缓冲区大小上限,也将其设置为first unassembled - first unacceptable的范围大小,以避免极端情况下的内存使用。
注意:first unassembled - first unacceptable的范围大小,并不等同于存放尚未装配子串的结构体内存大小上限,别混淆了。
Capacity 这个概念很重要,因为它不仅用于限制高内存占用,而且它还会起到流量控制的作用(见 lab2)。
变量说明:
_next_assembled_idx
:下一个待装配的字节索引
_unassemble_strs
: 一个字节索引到数据子串的 map
映射
_eof_idx
: 指示哪个字节索引代表 EOF
_unassemle_strs
一个字节索引到数据子串的 map
映射
_next_assembled_idx
下一个待装配的字节索引
_unassembled_bytes_num
已存储但尚未重组的子字符串中的字节数
_eof_idx
指示哪个字节索引代表 EOF
_output
重组后的有序字节流
_capacity
最大字节数
data
子字符串
index
表示data
中第一个字节的索引(按顺序放置)
eof
标记data
的最后一个字节是不是整个流中的最后一个字节
以下是具体需要考虑的情况
index <= _next_assembled_idx && _next_assembled_idx < index + data.size()
$ next-assembled-idx \in [index,\ index + data.size()] $
下一个待装配的字节索引在 data
子字符串之中
data 头 data 尾
index _next_assembled_idx index+data.size()
V V V
--+---------------+-------------------+-----------------------------
|///+///|
--+---------------+-------------------+-----------------------------
这种情况可以先截断掉
前面已经装配过的那部分数据,即 [ i n d e x n e x t − a s s e m b l e d − i d x ] [index ~ next-assembled-idx] [index next−assembled−idx] 这一部分
后面与已经存入 _unassembled_strs 的数据重合的那部分数据,即 data
中 位于$ [next-assembled-idx,\ index + data.size()] $ 这一区间中与 _unassembled_strs
的数据重合的部分
这样截断是为了让每次装配进的数据与存入 _unassembled_strs
的数据不产生重合,简化处理逻辑。
之后就可以直接装配,无需任何额外处理。
如果装配不下,即 _output
已满,那么就必须先放入待装配队列 _unassembled_strs
中,等待装配。
index > _next_assembled_idx
这种情况是需要认真考虑的,因为这种情况可能会与一些已经保存起来的未装配子串重合,导致大量的内存占用以及无用的轮询处理。对于传入数据的始末位置,分别有好几种情况。
为了便于说明,我们将
_unassembled_strs
中比index
小且距离最近 的那部分数据,称作front_data
, 其起始位置称为front_idx
。
back_data
和back_idx
同上,指的是在_unassembled_strs
中比当前传入index
大且距离最近的那部分数据与起始位置。
front
指的是数据前面的那个方向,back
是数据后面的那个方向。
首先是传入数据头部位置的情况:
若 front_idx + front_data.size() <= index
, 则说明当前传入 data
没有与已经保存的上一个子串重叠。这种无需处理
_next_assembled_idx front_idx index
V V V
----------+------------+------------------+---+-----------------...
| |++++++++++++++++++| |\\\\\\\\\\\\\\\\\...
----------+------------+------------------+---+-----------------...
A
front_data.size
若 front_idx + front_data.size() > index
, 则说明传入数据前半部分重合,需要进行截断,同时在截断后更新当前 index
。
截断前:
_next_assembled_idx front_idx front_idx+front_data.size
V V V
----------+------------+-----------+------+-----------------...
| |+++++++++++|+/+/+/|\\\\\\\\\\\\\\\\\...
----------+------------+-----------+------+-----------------...
A
index
截断后:
_next_assembled_idx front_idx front_idx+front_data.size
V V V
----------+------------+------------------+-----------------...
| |++++++++++++++++++|\\\\\\\\\\\\\\\\\...
----------+------------+------------------+-----------------...
A
new_idx
而对于传入数据尾部位置的情况,情况又有所不同:
若 index + data.size() <= back_idx
,则说明当前数据的后半部分没有重合,此时无需进行任何处理。
index index+data.size back_idx
V V V
---+---------------+------------+------------------...
|///| |++++++++++++++++++...
---+---------------+------------+------------------...
若 index + data.size() > back_idx
,则说明后半部分重合。但后半部分重合又有两种情况
index + data.size() < back_idx + back_data.size()
,这种就是常规情况的部分重合,截断掉重合部分即可
截断前:
index index+data.size
V V
---+---------------+-------+------------+-----...
|///|+/+/+/+|++++++++++++| ...
---+---------------+-------+------------+-----...
A A
back_idx back_idx+back_data.size
截断后
index index+data.size
V V
---+------------------------------------+-----...
|///|++++++++++++++++++++| ...
---+------------------------------------+-----...
A A
back_idx back_idx+back_data.size
index + data.size() < back_idx + back_data.size()
,这种是完全重合:当前传入的 data 完全覆盖下一个保存的 data
,此时将下一个 data
丢弃。
注意,若存在完全覆盖的情况,则需要重复检测 index + data.size()
的位置与丢弃下一个data
后,新的下一个data的末尾位置。因为可能当前传入的 data
会同时覆盖好几个保存的 data
。
处理前:
index index+data.size
V V
---+-----+--------------------+---------+-----+------...
|/|/+/+/+/+/+/+/+/+/+/+|/| |++++++...
---+-----+--------------------+---------+-----+------...
A A
back_idx back_idx+back_data.size
处理后:
index index+data.size
V V
---+------------------------------------+-----+------...
|| |++++++...
---+------------------------------------+-----+------...
A
新的 back_idx
上面所描述的处理方式可以很好的保证, _unassembled_strs
中的各个子串之间互不重叠,提高了内存利用效率。
EOF
的实现需要严格按照实验指导书来。当传入的 eof
参数为真时,表示当前传入的数据子串的最后一个字节将是整个流中的最后一个字节,这并不意味着这是最后一次调用 reassembler
来传入子串,因此需要额外将这个 eof_idx
保存,并在 reassemble
后判断一下是否到达 EOF
位置。
git fetch
git merge origin/lab1-startercode
cd build
make -j4
出现如下错误:
/usr/bin/ld: CMakeFiles/fsm_stream_reassembler_holes.dir/fsm_stream_reassembler_holes.cc.o: in function `BytesAvailable::execute(StreamReassembler&) const':
/home/misaka/Desktop/sponge/tests/fsm_stream_reassembler_harness.hh:57: undefined reference to `ByteStream::read[abi:cxx11](unsigned long)'
collect2: error: ld returned 1 exit status
make[2]: *** [tests/CMakeFiles/fsm_stream_reassembler_holes.dir/build.make:86:tests/fsm_stream_reassembler_holes] 错误 1
make[1]: *** [CMakeFiles/Makefile2:5093:tests/CMakeFiles/fsm_stream_reassembler_holes.dir/all] 错误 2
make: *** [Makefile:95:all] 错误 2
由于 Lab0
中没有实现 ByteStream::read
函数,所以在 byte_stream.cc
中添加如下实现:
//! Read (i.e., copy and then pop) the next "len" bytes of the stream
//! \param[in] len bytes will be popped and returned
//! \returns a string
std::string ByteStream::read(const size_t len) {
string data = this->peek_output(len);
this->pop_output(len);
return data;
}
重新编译:
cd build
make clean && make -j4
编译成功
[ 8%] Building CXX object libsponge/CMakeFiles/sponge.dir/stream_reassembler.cc.o
[ 8%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/address.cc.o
[ 8%] Building CXX object tests/CMakeFiles/spongechecks.dir/byte_stream_test_harness.cc.o
[ 8%] Building CXX object libsponge/CMakeFiles/sponge.dir/byte_stream.cc.o
[ 10%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/buffer.cc.o
[ 12%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/eventloop.cc.o
[ 14%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/file_descriptor.cc.o
[ 17%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/parser.cc.o
[ 19%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/socket.cc.o
[ 21%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/tun.cc.o
[ 23%] Linking CXX static library libspongechecks.a
[ 23%] Built target spongechecks
[ 25%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/util.cc.o
[ 27%] Linking CXX static library libsponge.a
[ 27%] Built target sponge
[ 36%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_seq.dir/fsm_stream_reassembler_seq.cc.o
[ 36%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_single.dir/fsm_stream_reassembler_single.cc.o
[ 36%] Building CXX object apps/CMakeFiles/webget.dir/webget.cc.o
[ 36%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_dup.dir/fsm_stream_reassembler_dup.cc.o
[ 38%] Linking CXX executable webget
[ 38%] Built target webget
[ 40%] Building CXX object tests/CMakeFiles/byte_stream_capacity.dir/byte_stream_capacity.cc.o
[ 42%] Linking CXX executable fsm_stream_reassembler_seq
[ 44%] Linking CXX executable fsm_stream_reassembler_single
[ 44%] Built target fsm_stream_reassembler_seq
[ 46%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_holes.dir/fsm_stream_reassembler_holes.cc.o
[ 46%] Built target fsm_stream_reassembler_single
[ 48%] Building CXX object tests/CMakeFiles/byte_stream_construction.dir/byte_stream_construction.cc.o
[ 51%] Linking CXX executable fsm_stream_reassembler_dup
[ 51%] Built target fsm_stream_reassembler_dup
[ 53%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_many.dir/fsm_stream_reassembler_many.cc.o
[ 55%] Linking CXX executable byte_stream_capacity
[ 55%] Built target byte_stream_capacity
[ 57%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_overlapping.dir/fsm_stream_reassembler_overlapping.cc.o
[ 59%] Linking CXX executable byte_stream_construction
[ 59%] Built target byte_stream_construction
[ 61%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_win.dir/fsm_stream_reassembler_win.cc.o
[ 63%] Linking CXX executable fsm_stream_reassembler_many
[ 63%] Built target fsm_stream_reassembler_many
[ 65%] Building CXX object tests/CMakeFiles/byte_stream_many_writes.dir/byte_stream_many_writes.cc.o
[ 68%] Linking CXX executable fsm_stream_reassembler_win
[ 68%] Built target fsm_stream_reassembler_win
[ 70%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_cap.dir/fsm_stream_reassembler_cap.cc.o
[ 72%] Linking CXX executable fsm_stream_reassembler_holes
[ 72%] Built target fsm_stream_reassembler_holes
[ 74%] Building CXX object tests/CMakeFiles/byte_stream_one_write.dir/byte_stream_one_write.cc.o
[ 76%] Linking CXX executable byte_stream_many_writes
[ 78%] Linking CXX executable fsm_stream_reassembler_overlapping
[ 78%] Built target byte_stream_many_writes
[ 80%] Building CXX object tests/CMakeFiles/byte_stream_two_writes.dir/byte_stream_two_writes.cc.o
[ 80%] Built target fsm_stream_reassembler_overlapping
[ 82%] Building CXX object doctests/CMakeFiles/parser_dt.dir/parser_dt.cc.o
[ 85%] Linking CXX executable parser_dt
[ 87%] Linking CXX executable byte_stream_one_write
[ 87%] Built target parser_dt
[ 89%] Building CXX object doctests/CMakeFiles/socket_dt.dir/socket_dt.cc.o
[ 89%] Built target byte_stream_one_write
[ 91%] Building CXX object doctests/CMakeFiles/address_dt.dir/address_dt.cc.o
[ 93%] Linking CXX executable byte_stream_two_writes
[ 93%] Built target byte_stream_two_writes
[ 95%] Linking CXX executable address_dt
[ 95%] Built target address_dt
[ 97%] Linking CXX executable socket_dt
[100%] Linking CXX executable fsm_stream_reassembler_cap
[100%] Built target socket_dt
[100%] Built target fsm_stream_reassembler_cap
stream_reassembler.hh
:
#ifndef SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH
#define SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH
#include "byte_stream.hh"
#include
#include
#include
//! \brief A class that assembles a series of excerpts from a byte stream (possibly out of order,
//! possibly overlapping) into an in-order byte stream.
class StreamReassembler {
private:
// Your code here -- add private members as necessary.
std::map<size_t, std::string> _unass; //!< The map the reflect the index of bytes to the substring
size_t _next_ass_idx; //!< The index of the next bytes should be assembled
size_t _unass_bytes; //!< The number of bytes that is stored but still unassembled
size_t _eof_idx; //!< The index of EOF
ByteStream _output; //!< The reassembled in-order byte stream
size_t _capacity; //!< The maximum number of bytes
size_t get_idx(const size_t index);
void truncation(const size_t new_idx, ssize_t& data_size);
void check_data(
const std::string &data,
const size_t new_idx,
const ssize_t data_size,
const size_t data_start_pos
);
void check_contiguous();
public:
//! \brief Construct a `StreamReassembler` that will store up to `capacity` bytes.
//! \note This capacity limits both the bytes that have been reassembled,
//! and those that have not yet been reassembled.
StreamReassembler(const size_t capacity);
//! \brief Receive a substring and write any newly contiguous bytes into the stream.
//!
//! The StreamReassembler will stay within the memory limits of the `capacity`.
//! Bytes that would exceed the capacity are silently discarded.
//!
//! \param data the substring
//! \param index indicates the index (place in sequence) of the first byte in `data`
//! \param eof the last byte of `data` will be the last byte in the entire stream
void push_substring(const std::string &data, const uint64_t index, const bool eof);
//! \name Access the reassembled byte stream
//!@{
const ByteStream &stream_out() const { return _output; }
ByteStream &stream_out() { return _output; }
//!@}
//! The number of bytes in the substrings stored but not yet reassembled
//!
//! \note If the byte at a particular index has been pushed more than once, it
//! should only be counted once for the purpose of this function.
size_t unassembled_bytes() const;
//! \brief Is the internal state empty (other than the output stream)?
//! \returns `true` if no substrings are waiting to be assembled
bool empty() const;
};
#endif // SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH
stream_reassembler.cc
:
#include "stream_reassembler.hh"
#include
// Dummy implementation of a stream reassembler.
// For Lab 1, please replace with a real implementation that passes the
// automated checks run by `make check_lab1`.
// You will need to add private members to the class declaration in `stream_reassembler.hh`
template <typename... Targs>
void DUMMY_CODE(Targs &&.../* unused */) {}
using namespace std;
StreamReassembler::StreamReassembler(const size_t capacity)
: _unass()
, _next_ass_idx(0)
, _unass_bytes(0)
, _eof_idx(-1)
, _output(capacity)
, _capacity(capacity) {}
//! \details This function calls at first in the push_substring
//! It aims to get the first index that doesn't overlap with
//! the substring in the unassembled string in the map
size_t StreamReassembler::get_idx(const size_t index) {
//!< Gets the first iterator pointer in _ unass that is greater than index
auto pos_iter = _unass.upper_bound(index);
//!< Try to get an iterator pointer that is less than or equal to index
if (pos_iter != _unass.begin()) {
pos_iter -- ;
}
size_t new_idx = index; //!< Gets the new starting position of the current substring
//!< If there is a substring in front of it
if (pos_iter != _unass.end() && pos_iter->first <= index) {
const size_t front_idx = pos_iter->first;
//!< If there is an overlap in front of the current substring
if (index < front_idx + pos_iter->second.size()) {
new_idx = front_idx + pos_iter->second.size();
}
} else if (index < _next_ass_idx) {
//!< If there is no substring in front of it, compare it with the currently read pos
new_idx = _next_ass_idx;
}
return new_idx;
}
//! \details When getting the next substring of the current substring,
//! we need to consider that new_idx may coincide with back_idx
//! This function is aims to truncate the overlapping area
void StreamReassembler::truncate(const size_t new_idx, ssize_t& data_size) {
auto pos_iter = _unass.lower_bound(new_idx);
//!< If it conflicts with the following substring, truncate the length
while (pos_iter != _unass.end() && new_idx <= pos_iter->first) {
const size_t data_end_pos = new_idx + data_size;
if (pos_iter->first < data_end_pos) { //!< If there is an overlapping area
//!< If it's a partial overlap
if (data_end_pos < pos_iter->first + pos_iter->second.size()) {
data_size = pos_iter->first - new_idx;
break;
} else { //!< If it all overlaps
_unass_bytes -= pos_iter->second.size();
pos_iter = _unass.erase(pos_iter);
continue;
}
} else { //!< If there is no overlap, break directly.
break;
}
}
}
//! \details This function is aim at determine whether any data is independent
//! and check whether the current substring is completely
//! contained by the previous substring.
void StreamReassembler::check_data (
const std::string &data,
const size_t new_idx,
const ssize_t data_size,
const size_t data_start_pos
) {
if (data_size > 0) {
const string new_data = data.substr(data_start_pos, data_size);
if (new_idx == _next_ass_idx) { //!< If the new string can be written directly
const size_t write_byte = _output.write(new_data);
_next_ass_idx += write_byte;
if (write_byte < new_data.size()) { //!< If it haven't been wirte all, save it
//!< _output is full that can't be written
//!< insert into the _unass
size_t len = new_data.size() - write_byte;
const string data_to_store = new_data.substr(write_byte, len);
_unass_bytes += data_to_store.size();
_unass.insert(make_pair(_next_ass_idx, std::move(data_to_store)));
}
} else {
const string data_to_store = new_data.substr(0, new_data.size());
_unass_bytes += data_to_store.size();
_unass.insert(make_pair(new_idx, std::move(data_to_store)));
}
}
}
//! \details This functions calls just after pushing a substring into the
//! _output stream. It aims to check if there exists any contiguous substrings
//! recorded earlier can be push into the stream.
void StreamReassembler::check_contiguous() {
for (auto iter = _unass.begin(); iter != _unass.end(); /* nop */) {
assert(_next_ass_idx <= iter->first);
//!< If it happens to be a message that can be received
if (iter->first == _next_ass_idx) {
const size_t write_num = _output.write(iter->second);
_next_ass_idx += write_num;
//!< If it is not fully written, it means that it is full
//!< keep the rest and exit.
if (write_num < iter->second.size()) {
_unass_bytes += iter->second.size() - write_num;
_unass.insert(
make_pair(
_next_ass_idx,
std::move(iter->second.substr(write_num))
)
);
_unass_bytes -= iter->second.size();
_unass.erase(iter);
break;
} else {
//!< If it is all written, delete the original iterator and update it
_unass_bytes -= iter->second.size();
iter = _unass.erase(iter);
}
} else { //!< Otherwise, just leave.
break;
}
}
}
//! \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) {
size_t new_idx = get_idx(index);
//!< The data index to which the new start position of the substring corresponds
const size_t data_start_pos = new_idx - index;
//!< The length of the data to be saved by the current substring
ssize_t data_size = data.size() - data_start_pos;
truncate(new_idx, data_size);
size_t _1st_unac_idx = _next_ass_idx + _capacity - _output.buffer_size();
//!< Detect whether there is data out of capacity.
//!< Note that the capacity here does not refer to the number of bytes that can be saved
//!< but to the size of the window that can be saved.
if (_1st_unac_idx <= new_idx) {
return;
}
check_data(data, new_idx, data_size, data_start_pos);
check_contiguous();
if (eof) {
_eof_idx = index + data.size();
}
if (_eof_idx <= _next_ass_idx) {
_output.end_input();
}
}
size_t StreamReassembler::unassembled_bytes() const { return _unass_bytes; }
bool StreamReassembler::empty() const { return _unass_bytes == 0; }
编译测试:
$ make -j4 && make check_lab1
[ 10%] Built target spongechecks
[ 27%] Built target sponge
[ 40%] Built target fsm_stream_reassembler_dup
[ 40%] Built target fsm_stream_reassembler_seq
[ 40%] Built target fsm_stream_reassembler_single
[ 44%] Built target webget
[ 61%] Built target byte_stream_capacity
[ 61%] Built target fsm_stream_reassembler_holes
[ 61%] Built target fsm_stream_reassembler_many
[ 61%] Built target byte_stream_construction
[ 78%] Built target fsm_stream_reassembler_cap
[ 78%] Built target byte_stream_many_writes
[ 78%] Built target fsm_stream_reassembler_overlapping
[ 78%] Built target fsm_stream_reassembler_win
[ 87%] Built target byte_stream_two_writes
[ 87%] Built target byte_stream_one_write
[ 91%] Built target socket_dt
[ 95%] Built target parser_dt
[100%] Built target address_dt
[100%] Testing the stream reassembler...
Test project /home/misaka/Desktop/sponge/build
Start 18: t_strm_reassem_single
1/16 Test #18: t_strm_reassem_single ............ Passed 0.00 sec
Start 19: t_strm_reassem_seq
2/16 Test #19: t_strm_reassem_seq ............... Passed 0.00 sec
Start 20: t_strm_reassem_dup
3/16 Test #20: t_strm_reassem_dup ............... Passed 0.00 sec
Start 21: t_strm_reassem_holes
4/16 Test #21: t_strm_reassem_holes ............. Passed 0.00 sec
Start 22: t_strm_reassem_many
5/16 Test #22: t_strm_reassem_many .............. Passed 0.05 sec
Start 23: t_strm_reassem_overlapping
6/16 Test #23: t_strm_reassem_overlapping ....... Passed 0.00 sec
Start 24: t_strm_reassem_win
7/16 Test #24: t_strm_reassem_win ............... Passed 0.05 sec
Start 25: t_strm_reassem_cap
8/16 Test #25: t_strm_reassem_cap ............... Passed 0.06 sec
Start 26: t_byte_stream_construction
9/16 Test #26: t_byte_stream_construction ....... Passed 0.00 sec
Start 27: t_byte_stream_one_write
10/16 Test #27: t_byte_stream_one_write .......... Passed 0.00 sec
Start 28: t_byte_stream_two_writes
11/16 Test #28: t_byte_stream_two_writes ......... Passed 0.00 sec
Start 29: t_byte_stream_capacity
12/16 Test #29: t_byte_stream_capacity ........... Passed 0.30 sec
Start 30: t_byte_stream_many_writes
13/16 Test #30: t_byte_stream_many_writes ........ Passed 0.00 sec
Start 53: t_address_dt
14/16 Test #53: t_address_dt ..................... Passed 0.00 sec
Start 54: t_parser_dt
15/16 Test #54: t_parser_dt ...................... Passed 0.00 sec
Start 55: t_socket_dt
16/16 Test #55: t_socket_dt ...................... Passed 0.00 sec
100% tests passed, 0 tests failed out of 16
Total Test time (real) = 0.49 sec
[100%] Built target check_lab1