第一个小测试 webget 是想让我们体验并模拟一下在浏览器中键入 URL 后获得远程服务器传来的内容,这并没有太大的难度,因为 Lab 本身已经为我们搭建好了 TCP/IP 五层协议栈的框架,我们要做的仅仅是解析传入的 host 和 path 即可,具体完善在 apps/webget.cc
的 get_URL()
中
第二个基本组件也比较简单,是想让我们实现一个最基本且最常用的字节流,可以将其理解成是一个队列,可以从头部读出数据,也可以从末端写入数据。同样,大体的框架 Lab 已经实现好了,只需要我们在 libsponge/byte_stream.cc
中完善其各功能即可
针对第一个测试点 webget,应该在 apps/webget.cc
中完善具体的 get_URL()
即可,
void get_URL(const string &host, const string &path) {
TCPSocket sock;
sock.connect(Address(host, "http"));
sock.write("GET " + path + " HTTP/1.1\r\nHost: " + host +
"\r\nConnection: close\r\n\r\n");
sock.shutdown(SHUT_WR);
while (!sock.eof())
cout << sock.read();
sock.close();
}
首先,进入函数内不管三七二十一,先建 socket,然后尝试与目标 web 主机建立 TCP 连接。成功后,透过 write 将获取 URL 内容的请求发送给服务器。发送完成之后,关闭 socket 写的 Channel,意味着不能再向该 socket 当中写数据了,或许只能读
之后,就是等待 web 主机回复。通过eof判断可读的 Channel 内是否有数据送来,送来一份,读一份,直到读完为止
针对第二个测试点稍微复杂一点,要修改 libsponge/byte_stream.h
和 libsponge/byte_stream.cc
的内容,让其支持一个队列的功能,
对于 writer,主要就是能够写入数据并且自知状态,包括队列的剩余容量及 Channel 是否已被关闭。在这里,我选用 deque 双端对列作为 ByteStream 的底层数据结构,来研究一下 write()
,
size_t ByteStream::write(const string &data)
{
size_t len = data.length();
/* 有多少容量写多少数据 */
if(remaining_capacity() < data.length())
len = remaining_capacity();
for(size_t i=0; i<len; i++)
buf_.push_back(data[i]);
nwrite_ += len;
return len;
}
首先,要透过 remaining_capacity()
看一下当前对列还能不能容下将要写入的数据,其定义如下,
size_t ByteStream::remaining_capacity() const
{
return cap_ - buf_.size();
}
我们在此采用的是有多少容量,就写多少数据的策略,而不是 0 或 1 的策略(非写即丢)。定义了 nwrite_
计数了一次 write 写了多少数据。end_input()
较为简单,只分辨是否还有字节将要写入,其定义如下,
void ByteStream::end_input()
{
ended_ = true;
}
第一个 peek_output()
主要用来查看队首元素,定义如下,
string ByteStream::peek_output(const size_t len) const
{
if(len > buf_.size())
return "";
return string(buf_.begin(), buf_.begin()+len);
}
pop_output()
和查看队首的功能差不多,只是将查看功能改为删除,
void ByteStream::pop_output(const size_t len)
{
if(len > buf_.size())
return;
for(size_t i=0; i<len; i++)
buf_.pop_front();
nread_ += len;
}
其中的 nread_
和 nwrite_
一样,用来统计 reader 已经读取了多少数据。read()
是框架定义好的,如下,
std::string read(const size_t len) {
const auto ret = peek_output(len);
pop_output(len);
return ret;
}
之后的一系列状态返回函数,我不一一展开了,无非就是封装一下成员变量,对外提供状态查看功能罢了
根据文档在 sponge 目录下新建 build 文件夹,然后在 build 里透过 cmake ..
初始化编译环境,接着透过 make -j4
进行编译
完成编译之后,可以透过 make check_webget
和 make check_lab0
来检验自己程序的正确性