CS 144 Lab One -- 流重组器

这篇具有很好参考价值的文章主要介绍了CS 144 Lab One -- 流重组器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


对应课程视频: 【计算机网络】 斯坦福大学CS144课程

Lab 1 对应的PDF: Lab Checkpoint 1: stitching substrings into a byte stream


实验结构

这幅图完整的说明了CS144 这门实验的结构:
CS 144 Lab One -- 流重组器,# CS 144 & MIT 6.829,网络
其中, ByteStream 是我们已经在 Lab0 中实现完成的。

我们将在接下来的实验中分别实现:

  • Lab1 StreamReassembler:实现一个流重组器,一个将字节流的字串或者小段按照正确顺序来拼接回连续字节流的模块
  • Lab2 TCPReceiver:实现入站字节流的TCP部分。
  • Lab3 TCPSender:实现出站字节流的TCP部分。
  • Lab4 TCPConnection: 结合之前的工作来创建一个有效的 TCP 实现。最后我们可以使用这个 TCP 实现来和真实世界的服务器进行通信。

该实验引导我们以模块化的方式构建一个 TCP 实现。

流重组器在 TCP 起到了相当重要的作用。迫于网络环境的限制,TCP 发送者会将数据切割成一个个小段的数据分批发送。但这就可能带来一些新的问题:数据在网络中传输时可能丢失、重排、多次重传等等。而TCP接收者就必须通过流重组器,将接收到的这些重排重传等等的数据包重新组装成新的连续字节流。


如何调试

先 cmake && make 一个 Debug 版本的程序。

所有的评测程序位于build/tests/中,先一个个手动执行过去。

若输出了错误信息,则使用 gdb 调试一下。


StreamReassembler 实现

在我们所实现的流重组器中,有以下几种特性:

  • 接收子字符串。这些子字符串中包含了一串字节,以及该字符串在总的数据流中的第一个字节的索引

  • 流的每个字节都有自己唯一的索引,从零开始向上计数。

  • StreamReassembler 中存在一个 ByteStream 用于输出,当重组器知道了流的下一个字节,它就会将其写入至 ByteStream中。

需要注意的是,传入的子串中:

  • 子串之间可能相互重复,存在重叠部分

    • 但假设重叠部分数据完全重复。
    • 不存在某些 index 下的数据在某个子串中是一种数据,在另一个子串里又是另一种数据。
    • 重叠部分的处理最为麻烦。
  • 可能会传一些已经被装配了的数据

  • 如果 ByteStream 已满,则必须暂停装配,将未装配数据暂时保存起来

除了上面的要求以外,容量 Capacity 需要严格限制:

CS 144 Lab One -- 流重组器,# CS 144 & MIT 6.829,网络
为了便于说明,将图中的绿色区域称为 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)。

代码实现如下:

  • stream_reassembler.hh
//! \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.
    struct Datum {
        char ch = 0;
        bool valid = false;
    };
    // 用于存放未按序达到的字节流
    std::vector<Datum> buffer_;  // buffer to store unassembled data.
    size_t buffer_header_;       // pointer to the begining of the unssembled bytes in
                                 // buffer.
    size_t unassembled_bytes_;
    size_t eof_byte_;
    bool is_eof_set_;    // true if read the eof
    // 存放按序到达的字节流,但是这部分字节流还没有被read
    ByteStream _output;  //!< The reassembled in-order byte stream
    size_t _capacity;    //!< The maximum number of bytes

  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;
};
  • stream_reassembler.cc
#include "stream_reassembler.hh"

#include <cassert>

// 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`

using namespace std;

StreamReassembler::StreamReassembler(const size_t capacity)
    : buffer_(capacity)
    , buffer_header_(0)
    , unassembled_bytes_(0)
    , eof_byte_(0)
    , is_eof_set_(false)
    , _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) {
    if (_output.input_ended())
        return;
    const size_t max_byte = _output.bytes_read() + _capacity;
    const size_t index_start = std::max(_output.bytes_written(), index);
    size_t index_end = std::min(max_byte, index + data.length());
    // check for eof
    if (eof) {
        is_eof_set_ = true;
        eof_byte_ = index + data.length();
    }
    if (is_eof_set_)
        index_end = std::min(index_end, eof_byte_);

    // buffer the data
    for (size_t write_index = index_start; write_index < index_end; write_index++) {
        size_t cache_index = (buffer_header_ + write_index - _output.bytes_written()) % _capacity;
        assert(!(buffer_[cache_index].valid && buffer_[cache_index].ch != data[write_index - index]));
        if (!buffer_[cache_index].valid)
            unassembled_bytes_++;
        buffer_[cache_index].valid = true;
        buffer_[cache_index].ch = data[write_index - index];
    }

    // write the data.
    while (buffer_[buffer_header_].valid) {
        buffer_[buffer_header_].valid = false;
        _output.write_char(buffer_[buffer_header_].ch);
        unassembled_bytes_--;
        buffer_header_ = (buffer_header_ + 1) % _capacity;
    }
    if (is_eof_set_ && _output.bytes_written() >= eof_byte_)
        _output.end_input();
}

size_t StreamReassembler::unassembled_bytes() const { return unassembled_bytes_; }

bool StreamReassembler::empty() const { return unassembled_bytes_ == 0; }

代码可能不太容易理解,但是大家对照下图,把几种可能出现的情况看明白,再回去看代码,相信就不难了:

CS 144 Lab One -- 流重组器,# CS 144 &amp; MIT 6.829,网络
核心一点: buffer用于暂存未按序到达的这部分不连续的字节流,而output用于存放按序到达的这部分字节流,但是这段字节流还没有被read。

测试代码正确性:
CS 144 Lab One -- 流重组器,# CS 144 &amp; MIT 6.829,网络文章来源地址https://www.toymoban.com/news/detail-595432.html

到了这里,关于CS 144 Lab One -- 流重组器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • CS144 计算机网络 Lab3:TCP Sender

    在 Lab2 中我们实现了 TCP Receiver,负责在收到报文段之后将数据写入重组器中,并回复给发送方确认应答号。在 Lab3 中,我们将实现 TCP 连接的另一个端点——发送方,负责读取 ByteStream (由发送方上层应用程序创建并写入数据),并将字节流转换为报文段发送给接收方。 TC

    2024年02月01日
    浏览(52)
  • CS144 计算机网络 Lab0:Networking Warmup

    本科期间修读了《计算机网络》课程,但是课上布置的作业比较简单,只是分析了一下 Wireshark 抓包的结构,没有动手实现过协议。所以最近在哔哩大学在线学习了斯坦福大学的 CS144 计算机网课程,这门课搭配了几个 Lab,要求动手实现一个 TCP 协议,而不是简单地调用系统为

    2023年04月18日
    浏览(34)
  • CS144-Lab6

    在本周的实验中,你将在现有的 NetworkInterface 基础上实现一个IP路由器,从而结束本课程。路由器有几个网络接口,可以在其中任何一个接口上接收互联网数据报。路由器的工作是根据 路由表 转发它得到的数据报:一个规则列表,它告诉路由器,对于任何给定的数据报: 发

    2024年02月09日
    浏览(44)
  • CS144--Lab1笔记

    CS144——Lab1笔记 作为使用了版本管理的项目,开始新的开发,当然先要新建一个开发分支啦,可以用命令或者直接在IDE中GIT的图形控制界面操作,太简单就不细说。(我习惯命名:dev-lab1) 首先要从原始仓库合并Lab1的相关文件到本地仓库,接着在build目录下编译。执行下面的

    2024年02月20日
    浏览(54)
  • CS 144 Lab Four -- the TCP connection

    对应课程视频: 【计算机网络】 斯坦福大学CS144课程 Lab Four 对应的PDF: Lab Checkpoint 4: down the stack (the network interface) TCPConnection 需要将 TCPSender 和 TCPReceiver 结合,实现成一个 TCP 终端,同时收发数据。 TCPConnection 有几个规则需要遵守: 对于 接收数据段 而言: 如果接收到的数据包

    2024年02月13日
    浏览(34)
  • CS 144 Lab Six -- building an IP router

    对应课程视频: 【计算机网络】 斯坦福大学CS144课程 Lab Six 对应的PDF: Lab Checkpoint 5: building an IP router 在本实验中,你将在现有的 NetworkInterface 基础上实现一个IP路由器,从而结束本课程。路由器有几个网络接口,可以在其中任何一个接口上接收互联网数据报。路由器的工作是根

    2024年02月14日
    浏览(34)
  • CS144(2023 Spring)Lab 0:networking warmup(环境搭建 & webget & bytestream)

    最近心情非常郁闷,搓一个CS144玩玩吧,正好2023 spring出新版了。。。CS144的头4个Lab(加上0是5个),一步步实现了一个TCP。在开始之前,我想贴一下Lab中的这句话: The lab documents aren’t “specifications”—meaning they’re not intended to be consumed in a one-way fashion. They’re written closer

    2024年02月11日
    浏览(45)
  • MIT 6.S081 Lab Three

    本文为 MIT 6.S081 2020 操作系统 实验三解析。 MIT 6.S081课程前置基础参考: 基于RISC-V搭建操作系统系列 在本实验中,您将探索页表并对其进行修改,以简化将数据从用户空间复制到内核空间的函数。 开始编码之前,请阅读xv6手册的第3章和相关文件: * kernel/memlayout.h* ,它捕获了

    2024年02月09日
    浏览(48)
  • mit 6.824 lab1分析

    略 map阶段每个worker应该把中间文件分成nReduce份,nReduce是reduce任务的数量 worker完成reduce任务后生成文件名 mr-out-X mr-out-X 文件每行应该是 \\\"%v %v\\\" 格式,参考 main/mrsequential.go worker处理完map任务,应该把生成的中间文件放到当前目录中,便于worker执行reduce任务时读取中间文件 当所

    2023年04月10日
    浏览(50)
  • mit6.828 - lab5笔记(上)

    unix的文件系统相关知识 unix将可用的磁盘空间划分为两种主要类型的区域: inode区域 和 数据区域 。 unix为每个文件分配一个inode,其中保存文件的 关键元数据 ,如文件的stat属性和指向文件数据块的指针。 数据区域中的空间会被分成大小相同的数据块(就像内存管理中的分

    2024年02月02日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包