CS144 Lab 0
这篇文章会讲解 CS144 的实验环境配置以及 Lab 0 的思路和代码。
环境配置
我是在 WSL2 下做 Lab 的,首先需要参照这个清单下载好所有依赖,基本上就是sudo apt-get install <dependency>
,不太确定的话请谷歌。
另一部分的配置是在 github 把官方仓库克隆进自己的一个私有仓库,可以参照 stackoverflow 完成。之前看到有其他网友因为设置成了公开被这门课的教授要求改成私人可见,所以感觉还是不要麻烦别人比较好。这步不用着急,在 Lab 0 文档的第三部分有指导。
Lab 0
Networking by hand
第一步是获取一个网页,这里有个小坑就是在命令行输入 telnet cs144.keithw.org http
之后,会进入 command 模式,没办法输入多行,需要按 ctrl + ]
进入 client 模式,这样可以输入多行,可以参照这里。
第二步是用命令行发封 email,由于咱们没有斯坦福的账号,所以需要用个人邮箱,可以谷歌搜索 SMTP 来了解相关设置,具体怎么做就略了。
第三步按照文档开两个 WSL 命令行窗口输指令就好,也略了。
Writing a network program using an OS stream socket
Get started
cmake ..
可能需要替换成 CXX=g++-8 cmake ..
,别的没啥坑。
Writing webget
这部分是需要实现 ../apps/webget.cc 里的 void get_URL(const string &host, const string &path)
这个函数,这个函数的目的是新建一个 TCPSocket
,将其与 host
连接,发送对 path
路径下内容的请求,并将返回内容打印到标准输出。
首先需要阅读 FileDescriptor
, Socket
, TCPSocket
和 Address
的文档。阅读之后我们可以很容易地写出代码,参考如下:
void get_URL(const string &host, const string &path) {
TCPSocket socket{};
socket.connect(Address(host, "http"));
string httpRequest = "GET " + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n";
socket.write(httpRequest);
while (!socket.eof()) {
auto &&response = socket.read();
cout << response;
}
socket.close();
}
An in-memory reliable byte stream
这一部分需要我们实现一个可靠的双向字节流。我用了一个 std::deque<char> buf
来做存储的核心数据结构,但实际上其他 FIFO 满足 O(1) 单个字符输入输出的结构都行。实验设计美中不足的是没有规定阅读超过储存大小的行文以及单次输入超过最大容量的字符串的行为。
除了 buf
之外我还用了几个变量来储存一些信息。
size_t MAX_CAPACITY
用来设定最大容量size_t REMAIN_CAPACITY
用来记录剩余容量size_t _byte_written
用来记录一共写入的字符数,注意这个是包括读取后,现在不在buf
里的字符数的,是历史写入的总和size_t _byte_read
用来记录一共读取的字符数bool _input_ended
用来记录是否已经输入完成了
几个核心的函数实现如下:
ByteStream::ByteStream(const size_t capacity) {
MAX_CAPACITY = capacity;
REMAIN_CAPACITY = capacity;
}
size_t ByteStream::write(const string &data) {
size_t byteCount = 0;
for (auto &&c : data) {
if (REMAIN_CAPACITY == 0) {
set_error();
return byteCount;
}
buf.push_back(c);
byteCount++;
_byte_written++;
REMAIN_CAPACITY--;
}
return byteCount;
}
string ByteStream::peek_output(const size_t len) const {
string res;
for (auto &&iter = buf.cbegin(); iter < buf.cbegin() + len; iter++) {
res.push_back(*iter);
}
return res;
}
void ByteStream::pop_output(const size_t len) {
for (size_t i = 0; i < len; i++) {
buf.pop_front();
_byte_read++;
}
REMAIN_CAPACITY += len;
}
std::string ByteStream::read(const size_t len) {
string res;
for (size_t i = 0; i < len; i++) {
res.push_back(buf.front());
buf.pop_front();
REMAIN_CAPACITY++;
_byte_read++;
}
return res;
}
bool ByteStream::eof() const {
// 虽然感觉文档的 eof 定义不够准确,但这样可以过测试
return buffer_empty() && input_ended();
}
基本上还是比较直白的,不需要太多介绍。
实现完之后可以在 build
目录下通过以下命令来编译并测试自己的代码:
$ CXX=g++-8 cmake ..
$ make format
$ make -j4
$ make check_lab0
通过的话会得到类似字样:
100% tests passed, 0 tests failed out of 9
Total Test time (real) = 1.36 sec
下一个实验:Lab 1
Let me know what you think of this article on twitter @CreamEggMeat or leave a comment below!