Stanford CS144 Lab0: Networking Warmup

应用层Overview

1. Fetch a Web page

使用telnet通过http协议访问远端服务器
Image

2. Writing webget

2.1 描述

实现webget来通过Internet获取网页,使用TCP协议和套接字抽象。实现简单的Web客户端,使用上述HTTP的形式。读数据时遇到EOF则结束,使用轮询的方式读取数据。

Please note that in HTTP, each line must be ended with “\r\n” (it’s not sufficient
to use just “\n” or endl).
• Don’t forget to include the “Connection: close” line in your client’s request. This
tells the server that it shouldn’t wait around for your client to send any more
requests after this one. Instead, the server will send one reply and then will
immediately end its outgoing bytestream (the one from the server’s socket to your
socket). You’ll discover that your incoming byte stream has ended because your
socket will reach “EOF” (end of file) when you have read the entire byte stream
coming from the server. That’s how your client will know that the server has
finished its reply.
• Make sure to read and print all the output from the server until the socket reaches
“EOF” (end of file)—a single call to read is not enough.
• We expect you’ll need to write about ten lines of code.

2.2 实现

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
void get_URL(const string &host, const string &path) {
// Your code here.

// You will need to connect to the "http" service on
// the computer whose name is in the "host" string,
// then request the URL path given in the "path" string.

// Then you'll need to print out everything the server sends back,
// (not just one call to read() -- everything) until you reach
// the "eof" (end of file).

// cerr << "Function called: get_URL(" << host << ", " << path << ").\n";
// cerr << "Warning: get_URL() has not been implemented yet.\n";
TCPSocket client;
client.connect(Address(host, "http"));
string message = "GET " + path + " HTTP/1.1\r\nHost: " +
host + " \r\nConnection: close\r\n\r\n";
client.write(message);
client.shutdown(SHUT_WR);

while (!client.eof()) { // polling
cout << client.read();
}
client.close();
return;
}

3. An in-memory reliable byte stream

3.1 描述

  1. ByteStream字节流是有限的, 容量为capacity,允许在内存中存在的最大数量的字节,确保写字节数不超过capacity
  2. 需要实现一个缓冲区buffer来模拟当前字节流相应的功能。考虑到读端(Output End)队头和写端(Input End)队尾,因此使用stl中的双端队列deque来实现。
  3. eof()返回true, 读端无数据可读有两种情况, 一次读到eof、或者buffer为空。
  4. 从写端取数据分为两种操作, peekpop

3.2 实现

byte_stream.cc

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
ByteStream::ByteStream(const size_t capacity) : buffer_(std::move(deque<char>(0))), capacity_(capacity), end_write_(false),
read_num_(0), write_num_(0) { DUMMY_CODE(capacity); }

size_t ByteStream::write(const string &data) {
DUMMY_CODE(data);
size_t can_write = min(data.size(), remaining_capacity());
for (size_t i = 0; i < can_write; ++i) {
buffer_.push_back(data[i]); // push from the input side.
}
write_num_ += can_write;
return can_write;
}

//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const {
DUMMY_CODE(len);
size_t can_peek = min(len, buffer_.size());
string peek_data = "";
for (size_t i = 0; i < can_peek; ++i) {
peek_data += buffer_[i];
}
return peek_data;
}

//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
DUMMY_CODE(len);
if (len > buffer_.size()) {
set_error();
return;
}
for (size_t i = 0; i < len; ++i) {
buffer_.pop_front(); // removed from the output side.
}
read_num_ += 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) {
DUMMY_CODE(len);
string message = "";
size_t can_read = min(len, buffer_.size()); // next "len" bytes.
for (size_t i = 0; i < can_read; ++i) {
message += buffer_.front();
buffer_.pop_front();
}
read_num_ += can_read;
return message;
}

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

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

size_t ByteStream::buffer_size() const { return buffer_.size(); }

bool ByteStream::buffer_empty() const { return buffer_.empty(); }

bool ByteStream::eof() const { return buffer_empty() && end_write_; }

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

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

size_t ByteStream::remaining_capacity() const { return capacity_ - buffer_.size(); }

byte_stream.hh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ByteStream {
private:
// Your code here -- add private members as necessary.

// Hint: This doesn't need to be a sophisticated data structure at
// all, but if any of your tests are taking longer than a second,
// that's a sign that you probably want to keep exploring
// different approaches.
std::deque<char> buffer_;
size_t capacity_; // capacity in memory.
bool end_write_;
size_t read_num_;
size_t write_num_;

bool _error{}; //!< Flag indicating that the stream suffered an error.