0%

Stanford-CS144-Sponge 笔记 - Lab 0: Networking Warmup

本实验是热身实验,主要需要我们了解系统提供的Socket使用、实现一个字节流。

1. Writing a network program using an OS stream socket

使用系统提供的TCP支持从一个指定的URL获取网页。

提示:

  1. HTTP请求每行的末尾都需要使用“\r\n”
  2. 在请求中要包含“Connection: close”,这告诉服务器在结束该请求后应立即关闭连接。此外,读取数据流时应判断“EOF”标志,这意味着服务端数据发送完毕。
  3. 你应该把“EOF”之前的全部从服务器接收到的数据打印出来。
  4. 我们认为你不需要写超过10行的代码。

解决方案:

1
2
3
4
5
6
7
8
9
10
// apps\webget.cc
TCPSocket sock;
sock.connect(Address(host, "http"));
string data_send = "GET " + path + " HTTP/1.1\r\nHost: " + host + "\r\n" + "Connection: close\r\n\r\n";
sock.write(data_send);
while (!sock.eof()) {
auto data_recv = sock.read();
std::cout << data_recv;
}
sock.close();

2. An in-memory reliable byte stream

现在你已经知道reliable byte stream如何帮助你进行通信了。在本实验中,你需要实现一个内存中的字节流。字节被“input”端写入,并且能够被“output”端读取。当读取方读到了流的末尾,其将会遇到EOF标志。

您的字节流在任何时候都将被流控制,以限制其内存消耗。该对象初始化时使用“capacity”来设置其能够占用内存的最大空间。该对象将限制写入方来保证不超过capacity,当读取方读走一些字节后,写入方才能够继续写入。你的字节流只会有一个线程,你不需要考虑并发问题。

解决方案:
我们需要在头文件中增加几个变量,修改后的private如下:

1
2
3
4
5
6
7
8
// lib\byte_stream.hh
bool _error{}; //!< Flag indicating that the stream suffered an error.
std::string buffer_ = "";
size_t capacity_ = 0;
size_t used_ = 0;
size_t total_read_ = 0;
size_t total_write_ = 0;
bool input_end_ = false;

然后就是实现这些函数了,比较简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// lib\byte_stream.cc
ByteStream::ByteStream(const size_t capacity) : capacity_(capacity) {}

size_t ByteStream::write(const string &data) {
DUMMY_CODE(data);
// 获取数据大小
size_t writed_size = (remaining_capacity() >= data.size() ? data.size() : remaining_capacity());
// 保存数据并更新信息
buffer_ += data.substr(0, writed_size);
used_ += writed_size;
total_write_ += writed_size;
return writed_size;
}

//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const { return buffer_.substr(0, len); }

//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
buffer_.erase(0, len);
total_read_ += len;
used_ -= len;
}

//! 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) {
// 获取数据大小
size_t read_size = (len <= used_ ? len : used_);
// 返回数据
std::string temp = peek_output(read_size);
pop_output(read_size);
return temp;
}

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

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

size_t ByteStream::buffer_size() const { return used_; }

bool ByteStream::buffer_empty() const { return (used_ == 0); }

bool ByteStream::eof() const { return input_end_ && used_ == 0; }

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

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

size_t ByteStream::remaining_capacity() const { return capacity_ - used_; }