CS144计算机网络Lab0: Networking Warmup

本文最后更新于:2024年1月19日 晚上

热身分为三个部分,第一部分要求用telnet工具来发送一些简单的http请求,第二部分是写一个简单的webget应用模拟使用telnet是的操作,第三部分是完善一个ByteStream来实现更底层的socket写入读取等操作

第一部分 - Networking by hand

A. Fetch a Web page

通过在浏览器中访问http://cs144.keithw.org/hello,我们得到一条信息Hello, CS144!,现在让我们用telnet来实现这个过程:

  • 首先需要通过telnetcs144.keithw.org建立reliable byte stream,并告诉对方我们我们接下来的请求将使用http协议:

    1
    2
    3
    4
    ➜  ~ telnet cs144.keithw.org http
    Trying 198.18.0.30...
    Connected to cs144.keithw.org.
    Escape character is '^]'.

​ 现在我们就可以发送请求了

  • 继续发送

    1
    2
    3
    GET /hello HTTP/1.1
    Host: cs144.keithw.org
    Connection: close

    值得注意的是,服务器需要在超时之前接收到请求,我刚开始的时候一行一行地输入,由于打字速度感人,就会由于超时而收到Connection closed by foreign host.。除此之外,再写好请求后需要多按两次回车告诉telnet发送该请求。

  • 最后我们得到了理想的返回响应

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    HTTP/1.1 200 OK
    Date: Sun, 15 Oct 2023 04:38:14 GMT
    Server: Apache
    Last-Modified: Thu, 13 Dec 2018 15:45:29 GMT
    ETag: "e-57ce93446cb64"
    Accept-Ranges: bytes
    Content-Length: 14
    Connection: close
    Content-Type: text/plain

    Hello, CS144!
    Connection closed by foreign host.

Assignment

最后有一个任务,要求从http://cs144.keithw.org/lab0/sunetid获取secret code,这个就非常的简单了

1
2
3
4
5
6
7
➜  ~ telnet cs144.keithw.org http
Trying 198.18.0.30...
Connected to cs144.keithw.org.
Escape character is '^]'.
GET /lab0/0001 HTTP/1.1
Host: cs144.keithw.org
Connection: close
1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Date: Sun, 15 Oct 2023 04:47:40 GMT
Server: Apache
X-You-Said-Your-SunetID-Was: 0001
X-Your-Code-Is: 427826
Content-length: 108
Vary: Accept-Encoding
Connection: close
Content-Type: text/plain

Hello! You told us that your SUNet ID was "0001". Please see the HTTP headers (above) for your secret code.
Connection closed by foreign host.

B.Send yourself an email

C. Listening and connecting

在上面我们使用telnet向服务器发送请求,现在我们将成为一个简单的服务器,接受外部的信息

  • 使用netcat监听9090端口

    1
    2
    ➜  ~ netcat -v -l -p 9090
    Listening on 0.0.0.0 9090
  • 在另一个终端中运行telnet,连接到本地的9090端口

    1
    2
    3
    4
    ➜  ~ telnet localhost 9090
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
  • 现在,我们可以在两个终端之间互相发送信息了

    1
    2
    3
    4
    ➜  ~ netcat -v -l -p 9090
    Listening on 0.0.0.0 9090
    Connection received on localhost 60162
    hello, this is Ivan, bye~
    1
    2
    3
    4
    5
    ➜  ~ telnet localhost 9090
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    hello, this is Ivan, bye~

第二部分 - Writing a network program using an OS stream socket

这一部分需要通过系统内建的stream socket写一个简单的webget来实现之前telnet,具体来说,需要完善apps/webget.cc中的get_URL函数。我们可以参考doctests/address_example_1.ccdoctests/socket_example_2.cc,实际上就是初始化一个TCPSocket并通过目标Address建立连接,之后向服务求发送之前类似的请求

1
2
3
4
5
6
7
8
9
10
11
12
13
// Your code here.

TCPSocket con;
con.connect(Address(host, "http"));
con.write("GET " + path + " HTTP/1.1\r\n");
con.write("Host: " + host + "\r\n");
con.write("Connection: close\r\n");
con.write("\r\n");

while(!con.eof()){
cout << con.read();
}
con.close();

Connection: close\r\n之后,仍然需要写入一个回车告诉socket可以发送了,不然会一直等待直到超时。

测试一下

1
2
3
4
5
6
7
8
9
10
➜  build git:(main) ✗ make check_webget
[100%] Testing webget...
Test project /root/CS144Learn/build
Start 31: t_webget
1/1 Test #31: t_webget ......................... Passed 1.30 sec

100% tests passed, 0 tests failed out of 1

Total Test time (real) = 1.32 sec
[100%] Built target check_webget

第三部分 - An in-memory reliable byte stream

这一部分要求我们根据pdfwriterreader的接口来完善libsponge/byte stream.hhlibsponge/byte stream.cc,并能处理比buffer容量要长的多的字节流。

byte stream.hh

1
2
3
4
5
6
7
8
9
10
11
12
class ByteStream {
private:
// Your code here -- add private members as necessary.
std::string string_buffer;
size_t _buffer_size;
size_t _bytes_written;
size_t _bytes_read;
// bool _buffer_empty = false;
bool _is_input_ended = false;

/*code*/
}

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
ByteStream::ByteStream(const size_t capacity) : string_buffer(""), _buffer_size(capacity), _bytes_written(0), _bytes_read(0) {}

size_t ByteStream::write(const string &data) {
// DUMMY_CODE(data);
size_t write_cnt = min(remaining_capacity(), data.length());
string_buffer += std::move(data.substr(0,write_cnt));
_bytes_written += write_cnt;

return write_cnt;
}

//! \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);
return string_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) {
// DUMMY_CODE(len);
string_buffer.erase(0,len);
_bytes_read+=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);
std::string copied_str = peek_output(len);
pop_output(len);

return copied_str;
}

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

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

size_t ByteStream::buffer_size() const { return string_buffer.length(); }

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

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

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

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

size_t ByteStream::remaining_capacity() const { return _buffer_size - buffer_size(); }

在一开始做这个实验的时候,完全不知道需要干什么,之前学的C++还有面向对象的知识都忘了不少。实际上只需要按照每个模块的功能一步步写+debug就好了。

在测试的时候经常会出现The ByteStream should have had bytes_written equal to 0 but instead it was 1这样的错误,后面发现是我在定义string_buffer的时候把它初始化为了" ",而正常应该是为

本文链接: https://zone.ivanz.cc/p/30fba476.html


CS144计算机网络Lab0: Networking Warmup
https://zone.ivanz.cc/p/30fba476
作者
Ivan Zhang
发布于
2023年10月14日
更新于
2024年1月19日
许可协议