【斯坦福计网CS144项目】环境配置 & Lab0: ByteStream | 您所在的位置:网站首页 › stream计算机 › 【斯坦福计网CS144项目】环境配置 & Lab0: ByteStream |
前言
感觉学了不少 C++ 编程的知识和技术但比较缺少实践,于是打算找一些项目跟着做一做。 首先安利一个自学网站 CS自学指南,北大的同学做的,汇总了很多国内外高校 CS 相关的高质量公开课,其中大部分是有课程项目的。翻了翻感觉 Stanford 的这门 CS144 计网课的 Lab 比较有趣,难度也不错,就是它了! 课程网址:CS 144: Introduction to Computer Networking 我跟的是 2021 Fall 学期的版本。 我的 Github 项目:CS144-Lab,7个实验的完成版代码位于 libsponge/ 文件夹内。 准备工作
完成后得到的是一个控制台界面的 Ubuntu 18.04 系统。该虚拟机已经设置好可以通过ssh连接(localhost:2222 端口),为了方便开发,可以用 PuTTY 连接打开多个窗口,配置如下(建议保存成 session,每次直接加载):
因为计网的知识大三都学过,做完以上准备工作就可以愉快地开始实验了。Lab 0 的第二部分 Networking by hand 和第三部分 Writing a network program using an OS stream socket 都比较简单,跟着文档说明一步步做就好,这里直接看第四部分 An in-memory reliable byte stream。 本节要求我们实现一个可靠的字节流(Byte Stream)类,reader/writer 分别能够从一端读出数据,一端写入数据,同时要控制流中数据任意时刻不能超过某个容量(capacity)值。这里无需考虑多线程使用读写锁等问题。需要实现的 public 函数已经定义好,实现时可以补充任意私有成员。 待完成的代码在 sponge/libsponge 目录内,完成后在 sponge/build 目录中执行 make (-j4/-j8) 编译,然后执行 make check_lab0 以运行自动测试,如果看到 100% tests Passed 则说明通过(t_webget 需要连接国外网站,耗时较长或超时都没关系)。如果需要运行单个测试,执行 ctest -R 测试名,测试名就是运行所有测试时输出的 Test #1: t_xx 中的 t_xx,然后根据提示信息可以查看单次测试的输出日志(注意:如果要用打印语句 Debug,用 cerr 而不是 cout 输出)。以后的实验都是如此,不再重复。 回到本次任务,要求还是比较简单的,选用一个数据结构存储数据流并维护一些状态信息即可,我这里用的是 deque。容器的操作等实现细节上注意尽量用 Modern C++ 的风格。 ByteStream.hh: #ifndef SPONGE_LIBSPONGE_BYTE_STREAM_HH #define SPONGE_LIBSPONGE_BYTE_STREAM_HH #include #include //! \brief An in-order byte stream. //! Bytes are written on the "input" side and read from the "output" //! side. The byte stream is finite: the writer can end the input, //! and then no more bytes can be written. class ByteStream { private: // Your code here -- add private members as necessary. // Hint: This doesn't need to be a sophisticated data structure at // all, but if any of your tests are taking longer than a second, // that's a sign that you probably want to keep exploring // different approaches. std::deque _buf{}; std::size_t _capacity; std::size_t _bytes_written = 0; std::size_t _bytes_read = 0; bool _input_ended = false; bool _error = false; //!< Flag indicating that the stream suffered an error. public: //! Construct a stream with room for `capacity` bytes. ByteStream(const size_t capacity); //! \name "Input" interface for the writer //!@{ //! Write a string of bytes into the stream. Write as many //! as will fit, and return how many were written. //! \returns the number of bytes accepted into the stream size_t write(const std::string &data); //! \returns the number of additional bytes that the stream has space for size_t remaining_capacity() const; //! Signal that the byte stream has reached its ending void end_input(); //! Indicate that the stream suffered an error. void set_error() { _error = true; } //!@} //! \name "Output" interface for the reader //!@{ //! Peek at next "len" bytes of the stream //! \returns a string std::string peek_output(const size_t len) const; //! Remove bytes from the buffer void pop_output(const size_t len); //! Read (i.e., copy and then pop) the next "len" bytes of the stream //! \returns a string std::string read(const size_t len); //! \returns `true` if the stream input has ended bool input_ended() const; //! \returns `true` if the stream has suffered an error bool error() const { return _error; } //! \returns the maximum amount that can currently be read from the stream size_t buffer_size() const; //! \returns `true` if the buffer is empty bool buffer_empty() const; //! \returns `true` if the output has reached the ending bool eof() const; //!@} //! \name General accounting //!@{ //! Total number of bytes written size_t bytes_written() const; //! Total number of bytes popped size_t bytes_read() const; //!@} }; #endif // SPONGE_LIBSPONGE_BYTE_STREAM_HHByteStream.cc: #include "byte_stream.hh" using namespace std; ByteStream::ByteStream(const size_t capacity) : _capacity(capacity) {} size_t ByteStream::write(const string &data) { if (_input_ended) { return 0; } size_t write_len = min(data.size(), remaining_capacity()); _buf.insert(_buf.end(), data.begin(), data.begin() + write_len); _bytes_written += write_len; return write_len; } //! \param[in] len bytes will be copied from the output side of the buffer string ByteStream::peek_output(const size_t len) const { size_t peeked_len = min(len, _buf.size()); return string(_buf.begin(), _buf.begin() + peeked_len); } //! \param[in] len bytes will be removed from the output side of the buffer void ByteStream::pop_output(const size_t len) { size_t popped_len = min(len, _buf.size()); _buf.erase(_buf.begin(), _buf.begin() + popped_len); _bytes_read += popped_len; } //! 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 string ByteStream::read(const size_t len) { if (len == 0) { return ""; } string res = peek_output(len); pop_output(res.size()); return res; } void ByteStream::end_input() { _input_ended = true; } bool ByteStream::input_ended() const { return _input_ended; } size_t ByteStream::buffer_size() const { return _buf.size(); } bool ByteStream::buffer_empty() const { return _buf.empty(); } bool ByteStream::eof() const { return input_ended() && buffer_empty(); } size_t ByteStream::bytes_written() const { return _bytes_written; } size_t ByteStream::bytes_read() const { return _bytes_read; } size_t ByteStream::remaining_capacity() const { return _capacity - _buf.size(); }最后贴张通关图 😎 |
CopyRight 2018-2019 实验室设备网 版权所有 |