Stanford CS144 lab0

https://cs144.github.io/

环境搭建

这里选择使用WSL2+VSCode完成实验

基本使用以下命令安装

但GNU coreuntils不同

Lab0 networking warmup

官方描述的一些要点:

  • 这次的warmup,将安装Linux在你的电脑上,学会执行一些任务,用CPP编写一个从互联网上获取网页的小程序。并且实现网络在内存上一个关键抽象,也就是可靠流的字节接收方与发送方,本次时延大约需要2~6小时完成
  • 看完文档再做实验
  • 做完所有实验,能够实现Internet重要组成部分——路由功能,网际接口,TCP协议(将不可靠数据报转化为可靠字节流)
  • 需要使用Linux,支持CPP17标准的编译器

网络实践

这些都依赖于一种可靠的双向字节流,你在终端键入一个字节序列,这个字节序列会以同样的顺序被发出,另一台电脑(一台服务器)会接受到这段字节序列,并回应一段字节序列

  • 获取一个网页
    • 在浏览器中访问http://cs144.keithw.org/hello ,并观察结果

    • 现在在手动执行访问步骤

      • 在终端输入telnet cs144.keithw.org http,这是告诉telnet程序去打开一个字节流在你的电脑和另一台计算机之间(被命名为cs144.keithw.org),并且使用的是HTTP服务,超文本传输协议,万维网使用的也是它。telnet连接到服务器,又通过服务器去获取服务器上的内容
    • 给自己发邮件

      用可靠字节流给自己发邮件(需要斯坦福账号,跳了)

    • 监听与连接

      netccat是一个通过TCP/UDP在网络中进行读写数据工具(命令)

  • 用OS流套接字编写网络程序

通过stream socket这个类似于文件描述符的特性来实现一个可靠双向字节流。数据包会丢失,失序,内容发送变化,甚至重复。连接双方必须确保字节流中的字节正确送达,也需要保证接收方的缓冲区不会溢出(接受方告诉发送方接收窗口大小),这些都是由一个在1981年设立的叫做TCP的协议所完成的。把“尽力交付的数据报”(Internet提供的抽象)转化为“可靠字节流”(应用程序想要的抽象)是os的职责这个lab需要写一个"webget",来获取网页。

  1. "Sponge"是这个lab的的初始代码库的名字,按部就班获取并构初始建代码。
  2. 现代CPP,基本安全,并快且底层。与一些准则
  3. 看Sponge的文档,也可以通过make doc构建编译在本地。不过make format出了问题。这种注释是doxygen构建文件,不是cpp语法
    4.实现webget
    查看文档可以知道需要建立一个client的socket与server连接client_sock.connect(Address(host, "http"));,调用这个Address的构造函数
点击查看代码
void get_URL(const string &host, const string &path) {
    // Your code here.
    TCPSocket client_sock;
    client_sock.connect(Address(host, "http"));
    string s = "GET " + path + " HTTP/1.1\r\n" + "Host: "+ host +"\r\n" + "Connection: close\r\n\r\n";
    //与手动调用webget一致,将请求内容通过连接传递
    client_sock.write(s);
    while(!client_sock.eof()){//需要检测是否读到所有server传递的内容
        cout<<client_sock.read();
    }
    client_sock.close();//别忘了关闭套接字
    // 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).

    cerr << "Function called: get_URL(" << host << ", " << path << ").\n";
    cerr << "Warning: get_URL() has not been implemented yet.\n";
}
  • 内存中的可靠字节流

在内存中实现"流控制"的读写方,并且接下来的lab会实现一个在不可靠的互联网中的流读写方

  1. 太久没写CPP,遇到一些错误

    这里使用循环队列,不是用变量记录当前缓存容量,需要使循环队列的容量capacity + 1。判断不能是逗号运算符。参数列表初始化顺序需要与参数在类中的声明一致。
点击查看代码
//byte_stream.cc
#include "byte_stream.hh"
#include<iostream>
using namespace std;
// Dummy implementation of a flow-controlled in-memory byte stream.

// For Lab 0, please replace with a real implementation that passes the
// automated checks run by `make check_lab0`.

// You will need to add private members to the class declaration in `byte_stream.hh`

template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {}

using namespace std;

ByteStream::ByteStream(const size_t capacity) : byteQueue(capacity + 1), cap(capacity + 1){ }

size_t ByteStream::write(const string &data) {
    // Write a string of bytes into the stream. Write as many
    // as will fit, and return the number of bytes written.
    uint32_t preWrite = totWrite;
    for(auto c : data){
        if((tail + 1) % cap != head){
            totWrite++;
            byteQueue[tail] = c; 
            tail = (tail + 1) % cap;
        }else break;
    }
    // DUMMY_CODE(data);
    return totWrite - preWrite ; 
}

//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const {
    string s;
    size_t temHead = head;
    for(size_t i = 0; i < len && temHead != tail; ++i){//注意这里是temHead != tail
        s += byteQueue[temHead];
        temHead = (temHead + 1) % cap;
    }
    // DUMMY_CODE(len);
    return s;
}

//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) { 
    size_t bufferLen = (tail - head + cap) % cap;
    if(bufferLen >= len){
        bufferLen = len;
    }
    head = (head + bufferLen) % cap;
    totRead += bufferLen;
 }

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

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

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

size_t ByteStream::buffer_size() const { return (tail - head + cap) % cap; }

bool ByteStream::buffer_empty() const { 
    return head == tail; 
}

bool ByteStream::eof() const { return isEnd && head == tail; }

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

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

size_t ByteStream::remaining_capacity() const { return cap - ((tail - head + cap) % cap) - 1; }


//byte_stream.hh
#ifndef SPONGE_LIBSPONGE_BYTE_STREAM_HH
#define SPONGE_LIBSPONGE_BYTE_STREAM_HH

#include <string>
#include <vector>

//! \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.
    // static const uint32_t maxLen = UINT32_MAX; 
    // char byteQueue[maxLen];//注意不要用固定大小的数组,需要实时初始化,不然会SegFault
    std::vector<char> byteQueue;
    size_t head = 0, tail = 0, cap;
    uint64_t totWrite = 0, totRead = 0;
    bool isEnd = false;
    
    // 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.

    bool _error{};  //!< 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_HH

记得每次修改需要先 make,再make check_lab0

posted @   抿了抿嘴丶  阅读(292)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示