CS144-Lab0笔记

本文详细记录了CS144课程的Lab0笔记,涉及代码规范、Linux平台安装clang-format,使用TCPSocket进行HTTP访问,以及在内存中实现可靠的字节流通信,包括如何在终端中操作和遇到的问题解决方法。

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

CS144——Lab0笔记

lab0

使用固定格式代码规范,在build目录下make format,出现错误:
image
其原因是本linux平台没有安装clang-format,解决方式(terminal):

sudo apt install clang-format

如果安装成功,则同样在terminal中可以查看其版本信息:

然后修改本项目文件目录etc/clang_format.cmake文件中第5行的参数设置,其目标值设为你所安装版本,我的版本根据查询是14。
没有修改前:

修改后:

然后重新make format:

make -j4
//4线程编译

任务 1 Fetch a web page

需要在出现’^]后直接粘贴要求的命令(速度要快,提前写好那个命令,然后复制,在terminal上直接ctrl+shift+v)
然而碰上501状态码,因为我拿的是21年的,估计被服务端给禁止了,因此无法解决

任务 2 Send yourself an email

由于需要斯坦福大学的email,跳过(也有使用国内邮箱的,有兴趣的去搜索就可以了)

任务 3 Listening and connectin

借助tmux(linux上的一个分屏工具,很好用),在terminal上开两个窗口,一个窗口输入:

netcat -v -l -p 3300
//netcat 监听3300端口,作为server

切换到另外一个窗口,输入:

telnet localhost 3300
//该窗口作为client,通过3300端口,和server通信

任务 4 webget

阅读lab0.pdf可知其实验目的是让你重新实验一遍http访问的过程,使用其框架下的TCPSocket、Address类
所以找到webget.cc源文件,在未完成的get_URL()函数中,按照pdf指示完成即可
首先要启动一个TCPSocket链接,则需要声明定义一个对象实例:

//使用其封装的TCPSocket类
TCPSocket tcpsock;

接着使用该类的成员函数connect()调用系统的sock建立一个http服务的链接,由函数参数可知,其为封装的Address类,其第一个参数是hostname,第二个参数是service类型即http

tcpsock.connect(Address(host,"http"));

接着需要调用write()成员函数写入一个字节流到server,该字节流表明http服务的具体类型、版本,目的地址,以及关闭的信息

tcpsock.write("GET " + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n");
tcpsock.write("Connection: close\r\n\r\n");

ps:

在HTTP中,换行符不是\n,而是\r\n

当发送端不必发送数据时,可以使用shutdown()函数,相比于close()函数,有如下好处:

  • close()是完全断开连接,既不能发送也不能接受
  • shutdown()提供三种参数:
    • SHUT_RD 断开读入流
    • SHUT_WR 断开写入流
    • SHUT_RDWR 同时断开I/O流
  • 默认情况,调用close()无论输入缓冲区是否有数据都立即关闭套接字,后续发送数据需要重新建立套接字,而shutdown()只关闭连接不关闭套接字,并且会等输入缓冲区的数据传输完成才关闭连接

接着需要接受读入流的数据,使用一个循环,判断条件是否为EOF,最后调用close()关闭连接,完整代码:

try {
        TCPSocket tcpsock;
        // set the connection, void sock:: connect(const Address &address)
        // as to the Address class, you can find correct constructor, it's first parameter
        //  is hostname
        tcpsock.connect(Address(host, "http"));
        // write bytestream to server,attention the endl is"\r\n",not"\n"
        tcpsock.write("GET " + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n");
        // as client's request, must include 'Connection: close"
        tcpsock.write("Connection: close\r\n\r\n");
        // now, shutdown the sock,as ref shutdown flag is SHUT_WR
        tcpsock.shutdown(SHUT_WR);
        // when get eof , your sock should read at all
        // at the end of final, its EOF
        while (!tcpsock.eof()) {
            std::cout << tcpsock.read();
        }
        // finally, closed the connection
        tcpsock.close();
    } catch (...) {
        cerr << "Function called: get_URL(" << host << ", " << path << ").\n";
        cerr << "Warning: get_URL() has not been implemented yet.\n";
        throw;
    }

我是使用了抛出异常来处理警告信息的弹出,也可以直接注释掉这俩警告,然后在terminal中输入命令:

//重新make项目
make
//调用检查
make check_webget

结果如下:
在这里插入图片描述
对比网络上之前的任务安排,貌似21年的实验测试只有一个,所以100%通过啦(小小窃喜一下)

任务 5 An in-memory reliable byte stream

该章节是让你实现在不可靠(best-effort)网络中的可靠字节流通信。
实现的目录文件:/libsponge/byte_stream.h 和 /libsponge/byte_stream.cc
PS:

  • 在内存中的抽象类(类似CS110)
  • 字节可以在输入端写入,然后以同样顺序在输出端读取,同时字节流是有限的
  • 写入段可以终止输入,终止后不再写入任何字节
  • 读入端读取到EOF标志后,不再读取任何字节
  • 支持流量控制,以控制内存占用,该对象将初始化一个占用内存所能存储的最大容量,同时限制写入端随时能写入的字节数,确保不会超过容量,读取端读取后写入端就可以写入更多
  • 单线程,不用考虑锁、竞争
  • 你的Lab0的实现,在检测后不要超过1s,超过了就要考虑换方式实现

初读文档要求,就想到了deque<type>的数据结构,其中type为char,同时需要设置字节流的容量、写入的长度,读取的长度,为了实现写入端可以终止输入,则需要一个控制量来控制,所以ByteStream类的成员声明如下:

std::deque<char> byte_stream{}; //! < the buffer of byte stream implement by deque<char>
    size_t buffer_capacity ; //!< the buffer's capacity
    size_t byte_to_write ; //!< the count of byte to write into buffer
    size_t byte_by_read ;//!< the count of byte to read from buffer
    bool write_to_end = false; //!< it's flag indicating that the writer had arrival the buffer
                                    //! end

对于其他成员函数的实现:
构造函数

ByteStream::ByteStream(const size_t capacity) : buffer_capacity(capacity),byte_to_write(0),byte_by_read(0)
                                                    {
//    DUMMY_CODE(capacity);
}

write():对于字符串的写入,需要考虑待写入的字符串长度是否超过字节流的容量(长度),如果超过就只写入剩余字节流空间长度的字符串,实现调用自身的remaining_capacity()函数,remaining_capacity()函数调用buffer_size()函数

  • remaining_capacity():返回剩余容量,即总总量减去字节流缓冲区的长度
  • buffer_size():返回缓冲区的长度,即写入的长度减去已经读取的长度
size_t ByteStream::write(const string &data) {
    //!< set a temporary val that get how much char by written
    size_t write_length = data.size();
    //!< need check write_length bigger than surplus capacity
    //! the surplus capacity : buffer_capacity - byte_to_write (remaining_capacity())
    //! if bigger, only can write surplus capacity bytes to buffer
    if(write_length > remaining_capacity()){
        write_length = remaining_capacity();
    }
    for(size_t j = 0 ; j < write_length ;j++){
        byte_stream.emplace_back(data[j]);
    }
    //!< update write_length
    byte_to_write += write_length;
//    DUMMY_CODE(data);
    return write_length;
}
//remaining_capacity()函数实现
size_t ByteStream::remaining_capacity() const { return buffer_capacity - buffer_size(); }
//buffer_size()函数实现
size_t ByteStream::buffer_size() const { return byte_to_write - byte_by_read; }

peek_output(len):从头部读取len长度的字符

string ByteStream::peek_output(const size_t len) const {
    std::string output_head_string;
    for(size_t i = 0 ; i < len && i < byte_stream.size();i++){
        output_head_string += byte_stream[i];
    }
//    DUMMY_CODE(len);/
    return output_head_string;
}

pop_output(len):将此len字节移除字节流

void ByteStream::pop_output(const size_t len) {
    size_t pop_length = (len > byte_stream.size() ? byte_stream.size():len);
    byte_by_read += pop_length;
    while(pop_length--){
        byte_stream.pop_front();
    }
//    DUMMY_CODE(len);
}

read(len):读取并移除前len长度字节流,就是组合使用上面的两个函数,唯一需要注意的是参数len需要判断是否溢出

std::string ByteStream::read(const size_t len) {
//    DUMMY_CODE(len);
    size_t read_length = (len > byte_stream.size()?byte_stream.size():len);
    std::string read_string = peek_output(read_length);
    pop_output(read_length);
    return read_string;
}

其他成员函数:因为很简单就不介绍思路了,直接展示代码

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

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

bool ByteStream::buffer_empty() const { return byte_stream.empty(); }

bool ByteStream::eof() const { return write_to_end && byte_stream.empty(); }

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

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

接着在build目录下make,没有提示错误后,make check_lab0,然后就是自动测试性能等(如果有错误,就自己根据提示修改吧)
第一次结果:

在这里插入图片描述

时间远远超出,大哭········


参考资料


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值