CS144 计算机网络 lab1

本文详细解析了CS144计算机网络课程中的Lab1任务,重点介绍了字节流重组器(Stream Reassembler)的设计与实现过程,包括对字节流的分析、暂存区管理及结束状态判断等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CS144 计算机网络 lab1

上一学期刚学习了计算机网络这门课程,感觉还是挺有意思的,了解到CS144的lab所以便打算利用假期练练手,顺便也尝试着开始写博客。代码风格还很新手,多多包涵,欢迎讨论,不过估摸着也不会有什么阅读量,所以权且当作我个人的一个记录了。

初步分析

切入正题,lab1的核心思路都在文档里给的这张图上了:

在这里插入图片描述

蓝色的部分是已经从字节流中读出的,绿色的部分是已经接收到但还没有被读出,红色的部分是已经被接收但还没被组装。绿色和红色的这一块构成了容量_capacity。

设置成员变量

class StreamReassembler {
  private:
    // Your code here -- add private members as necessary.
    size_t _first_unread; //第一个还未被读的字节索引
    bool end; //结束标志
    size_t _end_index; //字节流结束的索引
    std::map<size_t, char> mp; //暂存区
    ByteStream _output;  //!< The reassembled in-order byte stream
    size_t _capacity;    //!< The maximum number of bytes
}

初始代码给出了字节流_output,对于未读的字节流我们设置first_unread来进行标识,对于未组装的字节流需要进行暂存,这里就使用了map来进行index对应字符的键值对存储。除此之外,结束标志用end进行标识,还需设置一个字节流结束的索引。当然,这些成员变量一开始也不会考虑得面面俱到,在代码实现的过程中感觉有新的变量需要再在这里添加。

功能分析

1.字节流和读入的子串分析

我们首先要搞清楚_capacity的构成,它是由字节流_output的容量,暂存区的容量以及最新需要加入进来的子串来构成的。而_output里的东西并不是只进不出的,这也是我在做lab1的时候很久没有意识到的一个点,导致花了很长时间在上面,直到研究了测试代码才发现

请添加图片描述

这里的BytesAvailable就是模拟的从字节流中读出数据。

弄清楚了这个,我们就可以把目光放到我们需要实现的函数上去了。这个函数的功能是push一个子串,告诉我们起始的index以及数据内容data。大致会出现以下两种情况:

  1. index比first_unread小;

    举个例子,一开始我们读入了"abc",此时first_unread为3,然后进来一个index为2的数据"cdef"。这时候我们就需要将重复的地方切除掉,让进来的数据变为"def"。当然还有特殊情况,进来一个index为0的"ab"的话就直接将其优化成空字符串就行了。

        size_t data_len = data.length(); //数据的长度
        string valid_data = data;
        bool _eof = eof;
        size_t idx = index; //原来的index是常量无法改变,重新赋给一个idx
        if(idx < _first_unread){
            if(_first_unread - idx >= data_len){
                idx = _first_unread;
                data_len = 0;
                valid_data = "";
            }
            else
            {
                valid_data = valid_data.substr(_first_unread - idx);
                idx = _first_unread;
                data_len = valid_data.length();
            }
        }
    
  2. index大于等于first_unread。

第一种情况的处理完后就会变为第二种情况,这时候两条路便归一了。接着考虑,要是data过长,也会造成溢出:
index加上len超出了capacity;
比如容量为8,先push了abc;
然后来了个index为6的ghX,
此时output容量3加上暂存区容量0加len并未超容,但push的ghX是会超容的。

我当时犯的错误就在此处。我天真地以为,只需index+len和_capacity进行一下判断即可,但是注意一下定义,capacity是output,暂存区,以及新进来的子串三者容量之和。

举个例子(其实就是测试样例):

初始化:capacity为2;

操作1:push一个index为0的"ab";

操作2:从output中读出"ab";

操作3:再push一个index为2的"cd"。

如果单纯用index+data_len和capacity进行比较,操作3就不会执行成功。但实际上output的容量和暂存区的容量为0,所以是可以进行操作3的。

    if(idx + data_len + _output.buffer_size() - _first_unread > _capacity){
        data_len = _capacity - _output.buffer_size() - idx + _first_unread;
        valid_data = valid_data.substr(0, data_len);
        _eof = false;
    }

修改_eof的原因是,只要进入了if语句,数据串就一定会被切割,哪怕给的_eof为true在切割之后都会消失,所以改为false。

2.暂存区分析

对于暂存区,我们可以采用如下的方法来处理:

无脑往暂存区加东西,加完用first_unread扫暂存区,符合条件就往output中去写,写完就将其删除。
还是上面的例子,容量8.
第一次操作,index 0,“abc” -> 暂存区写进abc,first unread从0开始
读暂存区,0,1,2都能读到东西,写进output中,3读不到了,first unread
就暂时为3。此时暂存区的’a’,‘b’,'c’都已经被删了。
第二次操作,index 6,“ghX” -> 先进行一个处理判断,data变为了"gh";
写进暂存区,暂存区现在有"gh"。unread为3。从3开始读暂存区,
读不到东西,操作结束。
第三次操作,index 4,“efg” -> 无脑写进暂存区,暂存区现在有"efgh",
uread为3。
第四次操作,index 3,“d” -> 无脑写进暂存区,现在有"defgh"。
unread从3一个一个读,读到’h’结束,此时unread为8。'defgh’都被写入了output中。

    //下面开始无脑往暂存区写东西
    for(size_t i = idx;i<idx + valid_data.length();i++){
        mp[i] = valid_data[i - idx];
    }
    string str = "";
    while (mp.find(_first_unread)!=mp.end())
    {
        str += mp[_first_unread];
        mp.erase(_first_unread);
        _first_unread++;
    }
    if(str.size()>0){
    _output.write(str);
    }

3.结束的判断

在向暂存区写东西前,我们需要对是否输入结束进行一个判断:eof为true便是结束,给成员变量end赋为true,结束索引为进行修改后的idx+data_len。

 if(_eof){
        end = true;
        _end_index = idx + data_len;
    }

暂存区写完后,end为true的情况下first_unread和_end_index若是相等便是结束了。

if(end && _first_unread == _end_index){
        _output.end_input();//停止写入
    }

测试及代码

测试如下:

请添加图片描述

全部通过。

具体的测试代码在tests文件夹下可找到,可以自己手动修改调试。

stream_reassembler.hh完整代码如下:

#ifndef SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH
#define SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH

#include "byte_stream.hh"

#include <cstdint>
#include <string>
#include <map>
//! \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.
    size_t _first_unread; //第一个还未被读的字节索引
    bool end; //结束标志
    size_t _end_index; //字节流结束的索引
    std::map<size_t, char> mp;
    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;
};

#endif  // SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH

stream_reassembler.cc完整代码如下:

#include "stream_reassembler.hh"

// 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`
#include<iostream>
template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {}
using namespace std;

StreamReassembler::StreamReassembler(const size_t capacity) : _first_unread(0), end(false), _end_index(0), mp{}, _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) {
    size_t data_len = data.length(); //数据的长度
    string valid_data = data;
    bool _eof = eof;
    size_t idx = index; //原来的index是常量无法改变,重新赋给一个idx
    if(idx < _first_unread){
        if(_first_unread - idx >= data_len){
            idx = _first_unread;
            data_len = 0;
            valid_data = "";
        }
        else
        {
            valid_data = valid_data.substr(_first_unread - idx);
            // cout<<valid_data<<endl;
            idx = _first_unread;
            data_len = valid_data.length();
        }
    }

    if(idx + data_len + _output.buffer_size() - _first_unread> _capacity){
        data_len = _capacity - _output.buffer_size() - idx + _first_unread;
        valid_data = valid_data.substr(0, data_len);
        _eof = false;
    }
    if(_eof){
        end = true;
        _end_index = idx + data_len;
    }

    //下面开始无脑往暂存区写东西
    for(size_t i = idx;i<idx + valid_data.length();i++){
        mp[i] = valid_data[i - idx];
    }
    string str = "";
    while (mp.find(_first_unread)!=mp.end())
    {
        str += mp[_first_unread];
        mp.erase(_first_unread);
        _first_unread++;
    }
    if(str.size()>0){
    _output.write(str);
    }
    if(end && _first_unread == _end_index){
        _output.end_input();
    }
}

size_t StreamReassembler::unassembled_bytes() const { 
    return mp.size();
 }

bool StreamReassembler::empty() const { 
    return mp.size() == 0;
 }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fika.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值