CS144 Lab:Lab0
本文最后更新于 51 天前,内容如有失效请评论区留言。

Lab0 首先有两个小实验热身,然后就是配置开发环境,熟悉实验环境,并实现 ByteStream

据了解,CS144 的 Lab 现在有两个大版本,一个是 Sponge 版本,一个是 Minnow,Sponge 是旧版本,也是大多数人完成实验的版本,8 个 Project 带你实现整个 TCP/IP 协议栈。而 Minnow 是 2023 年春季新上线的版本,新版本据说难度降低了,砍掉了原来最难的 TCP Connection 部分,内容也有所改动。为了更好的实验体验和学习资料,这里我还是选择经典 Sponge 版本来完成实验。

但一件遗憾的事是,新版本 Minnow 上线后旧版本 Sponge 的仓库被官方下线了,我们无法再直接获取到官方的 Sponge 最新仓库代码,只能从别人实验的 GitHub 仓库中回退来获取到原始实验代码,为此后面也踩了点坑和折腾了一下(因为 Git 技术太菜了)。

我的 CS144 代码仓库

Fetch a Web page

使用 telnet 发送 HTTP 报文获取网页内容

image-20230706124929756

一个坑点是步骤中没有说清楚成功要点:输入的速度一定要快

换句话说,只能复制粘贴下面的内容,而不是手动输入

GET /hello HTTP/1.1
Host: cs144.keithw.org
Connection: close

第二个热身 Send yourself an email 我没有做,因为文档上的步骤需要 Stanford 的邮箱

Listening and connecting

netcat 作为 server,telnet 作为 client,相互通信

image-20230706224245757

配置开发环境

实验环境需要 cmake, git 和最新的 g++,整个 Lab 采用 Modern C++ 作为开发语言,C++ 17 标准。(当然官网现在的 mirrow 版本的 Lab 已经是 C++ 20 标准了)

image-20230706235427409

竟然遇到了下面这个错误

image-20230707001119609

buffer.hh 中添加 #include <stdexcept> 就可以解决了(更新:后来换了一个貌似更新的实验仓库,直接就能编译通过了)

image-20230707001057010

编译安装 doxygen

image-20230707161940032

Webget

要求实现 get_URL 函数,功能为向指定 IP 地址发送 HTTP GET 请求,然后输出所有响应。

要求调用 Linux kernel 自带的 TCP/IP 协议栈实现即可,Sponge 已经将 C-Style 的 Socket 封装成了几个 C++ 类:FileDescriptor, Socket, TCPSocket, and Address classes。具体见官方文档

按照 HTTP 报文格式模拟发送 HTTP 报文即可实现

代码如下:

void get_URL(const string &host, const string &path) {
    // Your code here.

    // You will need to connect to the "http" service on
    // the computer whose name is in the "host" string,
    // then request the URL path given in the "path" string.

    // Then you'll need to print out everything the server sends back,
    // (not just one call to read() -- everything) until you reach
    // the "eof" (end of file).

    TCPSocket sock;
    sock.connect(Address(host, "http"));
    sock.write("GET " + path + " HTTP/1.1\r\n");
    sock.write("Host: " + host + "\r\n");
    sock.write("Connection: close\r\n");
    sock.write("\r\n");
    sock.shutdown(SHUT_WR);

    while (!sock.eof()) {
        cout << sock.read();
    }
    sock.close();

    cerr << "Function called: get_URL(" << host << ", " << path << ").\n";
    // cerr << "Warning: get_URL() has not been implemented yet.\n";
}

测试结果,如下:

image-20230707012244859

image-20230707012218262

ByteStream

要求实现一个运行在内存的有序可靠字节流(An in-memory reliable byte stream),类似于管道。要求如下:

  • 字节流可以从写入端写入,并以相同的顺序,从读取端读取
  • 字节流是有限的,写者可以终止写入。而读者可以在读取到字节流末尾时,产生 EOF标志,不再读取
  • 所实现的字节流必须支持流量控制,以控制内存的使用。当所使用的缓冲区爆满时,超过缓冲区容量的内容直接丢弃。字节流会返回成功写入的字节数
  • 写入的字节流可能会很长,必须考虑到字节流大于缓冲区大小的情况。即便缓冲区只有1字节大小,所实现的程序也必须支持正常的写入读取操作

在单线程环境下执行,因此不用考虑各类条件竞争问题。

网上很多博客是采用 std::deque<char> 实现的,用它实现很好想也容易写,但是 std::deque<char> 的底层实现并不是连续的数组,会带来额外的开销,而现在的需求是容量(capacity)是在初始化后就确定的,因此我采用 std::vector<char> 来实现。

为了能够放入 capacity bytes,我们需要开辟 capacity + 1 的空间。否则就无法区分全空和全满状态。

声明部分代码如下:

//! \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::vector<char> _buffer;  //!< The buffer of the stream
    size_t _capacity;         //!< The capacity of the stream
    size_t _written_cnt;  //!< The number of bytes written
    size_t _read_cnt;     //!< The number of bytes read
    int _head, _tail;     //!< The head and tail of the buffer
    bool _input_ended_flag = false;  //!< Flag indicating that the input has ended
    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;
    //!@}
};

成员函数实现代码如下:

ByteStream::ByteStream(const size_t capacity) : _buffer(capacity + 1), _capacity(capacity), _written_cnt(0),
                                                _read_cnt(0), _head(0), _tail(_capacity) {}

size_t ByteStream::write(const string &data) {
    auto ret = min(data.size(), remaining_capacity());
    for (size_t i = 0; i < ret; ++i) {
        _tail = (_tail + 1) % (_capacity + 1);
        _buffer[_tail] = data[i];
    }
    _written_cnt += ret;
    return ret;
}

//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const {
    string ret;
    auto n = min(len, buffer_size());
    for (size_t i = 0; i < n; ++i) {
        ret.push_back(_buffer[(_head + i) % (_capacity + 1)]);
    }
    return ret;
}

//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
    auto n = min(len, buffer_size());
    _head = (_head + n) % (_capacity + 1);
    _read_cnt += n;
}

//! 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) {
    auto ret = peek_output(len);
    pop_output(len);
    return ret;
}

void ByteStream::end_input() { _input_ended_flag = true; }

bool ByteStream::input_ended() const { return _input_ended_flag; }

size_t ByteStream::buffer_size() const { return (_tail - _head + 1 + _capacity + 1) % (_capacity + 1) ; }

bool ByteStream::buffer_empty() const { return buffer_size() == 0; }

bool ByteStream::eof() const { return _input_ended_flag && buffer_empty(); }

size_t ByteStream::bytes_written() const { return _written_cnt; }

size_t ByteStream::bytes_read() const { return _read_cnt; }

size_t ByteStream::remaining_capacity() const { return _capacity - buffer_size(); }

测试

image-20230708114305457

References

我使用的 Sponge 仓库

CS144 计算机网络 Lab0

康宇PL’s Blog-【计算机网络】Stanford CS144 Lab Assignments 学习笔记

评论

  1. last-whisper
    Macintosh Chrome 115.0.0.0
    9 月前
    2023-8-13 19:44:56

    hey 哥们我发现 github 上官方换了 minnow 后我的环境就死活搭不起来。你现在用的 lab 的 pdf 是哪个版本的呀~ 最新的版本的环境配不动。

    • 博主
      last-whisper
      Windows Chrome 115.0.0.0
      9 月前
      2023-8-13 19:52:43

      PDF 我用的 Fall 2021 的版本,实验框架还是旧的 Sponge,新的 Minnow 网上教程几乎没有,当然写经典旧版学习更方便🙂

      • last-whisper
        admin52
        Macintosh Chrome 115.0.0.0
        9 月前
        2023-8-13 19:55:16

        你好~ 请问 Fall 2021 的版本请问还有嘛?我今天下午搭了一个下午 2023 Spring 的环境都 fail 了,人麻了 TAT。可以一起交流下嘛,mail: pkuwkl@gmail.com (☆ω☆),如果可以的话我们在上面交流吧~

        • 博主
          last-whisper
          Windows Chrome 115.0.0.0
          9 月前
          2023-8-13 19:58:17

          可以呀,加 qq 525687841 聊吧

  2. yoo2i
    Windows Edge 116.0.1938.62
    8 月前
    2023-8-31 19:45:12

    老哥,搭环境搭麻了,方便加你qq请教一下吗

    • 博主
      yoo2i
      Windows Chrome 115.0.0.0
      8 月前
      2023-8-31 22:35:56

      可以的

  3. eightclounde
    Windows Edge 116.0.1938.76
    8 月前
    2023-9-12 19:28:33

    老哥 拉的你的仓库 回退到你的仓库 写好lab0的webget make check_webget 然后 testing webget 直接就跳出来Build target check_webget 没有看到样例测试 拉了好几遍都是这样 用的ubuntu20.04 这是为什么啊 /(ㄒoㄒ)/

    • 博主
      eightclounde
      Windows Chrome 115.0.0.0
      8 月前
      2023-9-12 19:46:53

      先 make 编译再 make check_webget 吧,webget 就只有 1 个测试用例吧

      • eightclounde
        admin52
        Windows Edge 116.0.1938.76
        8 月前
        2023-9-12 23:03:56

        对啊 就是这样的 但是 tests pass 跟时间那些都没跳出来 然后就直接build target了 只跳出来两行 一行testing webget 然后下一行就是Build target check_webget了 = =

        • 博主
          eightclounde
          Windows Chrome 116.0.0.0
          8 月前
          2023-9-13 19:27:40

          没看懂,我也没遇到过🌝

  4. yoo2i
    Windows Edge 117.0.2045.60
    7 月前
    2023-10-13 22:18:28

    老哥,你跑webget测试之前进行的那个tr命令,删除r是啥意思呢?我本来也死活check不了,看了你的截图尝试了一下才好的

    • 博主
      yoo2i
      Windows Chrome 118.0.0.0
      已编辑
      7 月前
      2023-10-13 22:27:38

      就是删除文件里的 ‘\r’,把 CRLF 转换为 LF(因为 Linux 下一般是 LF,不转换的话脚本好像没法运行)

  5. yoo2i
    Windows Edge 118.0.2088.46
    7 月前
    2023-10-16 8:52:51

    老哥,telnet热身里面那三行请求体的第三行connection写成conncetion了

    • 博主
      yoo2i
      Windows Chrome 118.0.0.0
      7 月前
      2023-10-16 11:00:27

      噢噢,还真是(其实请求体我也是从其它博客文章复制过来的🙃)

    • 博主
      yoo2i
      Windows Chrome 121.0.0.0
      2 月前
      2024-3-08 13:12:05

      fixed ✅

  6. Leo
    Windows Chrome 120.0.0.0
    已编辑
    4 月前
    2023-12-19 15:48:16

    请问为什么不能在 read 函数里更新 _read_cnt?

    string ByteStream::read(const size_t len) {
        string res = peek_output(len);
        pop_output(len);
        // _read_cnt += res.size(); this is wrong 
        return res;
    }

    这样写就会出错,只有在 pop 函数里跟新_read_cnt 才能过全部testcase
    这可能是因为多线程的影响吗?

    错误testcase

    4/9 Test #29: t_byte_stream_capacity ...........***Failed    0.00 sec
    Test Failure on expectation:
            Expectation: bytes_read: 2
    
    Failure message:
            The ByteStream should have had bytes_read equal to 2 but instead it was 0
    
    List of steps that executed successfully:
            Initialized with (capacity=2)
                 Action: write "cat" to the stream
                 Action: pop 2
                 Action: write "tac" to the stream
            Expectation: input_ended: 0
            Expectation: buffer_empty: 0
            Expectation: eof: 0
    
    Exception: The test "overwrite-clear-overwrite" failed
    • 博主
      Leo
      Windows Chrome 119.0.0.0
      4 月前
      2023-12-20 14:13:29

      建议仔细阅读实验文档的要求,文档里说了整个实验都是单线程,不需要考虑多线程。_read_cnt 文档里也有说明,是 total number of bytes popped。

      • Leo
        admin52
        Windows Chrome 120.0.0.0
        4 月前
        2023-12-20 14:46:57

        感谢博主解答!是我文档没有读清楚,现在明白了~

  7. liyishui
    Windows Chrome 120.0.0.0
    4 月前
    2024-1-06 1:51:44

    想请教一下输完GET /hello HTTP/1.1
    Host: cs144.keithw.org
    Conncetion: close之后直接跳出Connection closed by foreign host如何解决

    • aaa
      liyishui
      Windows Chrome 120.0.0.0
      4 月前
      2024-1-12 19:37:27

      尽量快的把接下来的几行命令输进去

  8. cheung
    Windows Edge 123.0.0.0
    3 周前
    2024-4-09 16:57:48

    你好,我在make的时候,没有出现buffer.cc中的错误,但是出现了address.cc中的错误。

    /home/cs144/sponge/libsponge/util/address.cc: In member function ‘std::pair<std::__cxx11::basic_string, short unsigned int> Address::ip_port() const’:
    /home/cs144/sponge/libsponge/util/address.cc:91:29: error: variable ‘std::array ip’ has initializer but incomplete type
       91 |     array ip{};
          |                             ^~
    /home/cs144/sponge/libsponge/util/address.cc:92:29: error: variable ‘std::array port’ has initializer but incomplete type
       92 |     array port{};
          |                             ^~~~
    /home/cs144/sponge/libsponge/util/address.cc:100:41: error: could not convert ‘{, }’ from ‘’ to ‘std::pair<std::__cxx11::basic_string, short unsigned int>’
      100 |     return {ip.data(), stoi(port.data())};
          |                                         ^
          |                                         |
          |                                         
    make[2]: *** [libsponge/CMakeFiles/sponge.dir/build.make:90: libsponge/CMakeFiles/sponge.dir/util/address.cc.o] Error 1
    make[1]: *** [CMakeFiles/Makefile2:1971: libsponge/CMakeFiles/sponge.dir/all] Error 2
    make[1]: *** Waiting for unfinished jobs....
    [ 13%] Linking CXX static library libspongechecks.a
    [ 13%] Built target spongechecks
    make: *** [Makefile:101: all] Error 2

    网上说这个错误是因为没包含头文件导致的,但这个有#include “address.hh”
    可以麻烦看看吗

    • 博主
      cheung
      Windows Chrome 123.0.0.0
      3 周前
      2024-4-09 17:13:13

      The errors you’re encountering in your C++ code are related to the usage of the std::array class template without specifying the required template parameters, and a conversion issue when trying to return a std::pair.

      1. Incomplete Type for std::array: The std::array needs two template arguments: the type of the elements and the size of the array. You’re not providing any, which is why the compiler complains about an initializer but incomplete type. You should define your ip and port variables with proper types and sizes, like std::array<char, SIZE> where SIZE is the number of elements you expect in the arrays.
      2. Conversion Issue: The error on line 100 seems to suggest that the compiler is unable to convert from the initializer list {, } to the required type std::pair<std::string, unsigned short>. This might be due to the variables ip and port not being used correctly. Ensure ip.data() returns a string or can be converted to a string and stoi(port.data()) successfully converts the port data to an integer.

      Here’s a corrected snippet considering these points, but you’ll need to adjust the array types and sizes according to your actual data:

      #include 
      #include 
      #include  // For std::pair
      
      std::pair Address::ip_port() const {
          std::array ip{}; // Adjust 16 to the actual size you need
          std::array port{}; // Adjust 6 to the actual size you need
      
          // Your logic to fill ip and port arrays goes here
      
          return {std::string(ip.data()), static_cast(std::stoi(std::string(port.data())))};
      }

      Ensure you’ve included the array header (#include <array>) and adjust the array sizes (16 and 6 in the example) as per your requirements. The conversion to std::string and then to unsigned short should handle the types correctly for the std::pair being returned.

    • 博主
      cheung
      Windows Chrome 123.0.0.0
      3 周前
      2024-4-09 17:16:34

      我觉得这种简单的编译问题问 GPT 比问我来得快和简单😂

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇