本实验是热身实验,主要需要我们了解系统提供的Socket使用、实现一个字节流。
1. Writing a network program using an OS stream socket
使用系统提供的TCP支持从一个指定的URL获取网页。
提示:
- HTTP请求每行的末尾都需要使用“\r\n”
- 在请求中要包含“Connection: close”,这告诉服务器在结束该请求后应立即关闭连接。此外,读取数据流时应判断“EOF”标志,这意味着服务端数据发送完毕。
- 你应该把“EOF”之前的全部从服务器接收到的数据打印出来。
- 我们认为你不需要写超过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_; }