lab0
使用固定格式代码规范,在build目录下make format,出现错误:
其原因是本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,然后就是自动测试性能等(如果有错误,就自己根据提示修改吧)
第一次结果:
时间远远超出,大哭········