CS144-Lab0

开始跟CS144,这门课的lab很有意思,在博客里面记录。

Fetch a Web Page

  1. 在浏览器地址栏键入http://cs144.keithw.org/hello敲击回车,看到屏幕显示如下的字符:
1
Hello, CS144!
  1. 下面用命令行去做和浏览器一样的事情,获取同样的内容。

键入telnet cs144.keithw.org http,敲击回车,建立一个可靠的字节流,显示如下:

1
2
3
Trying 104.196.238.229...
Connected to cs144.keithw.org.
Escape character is '^]'.

键入GET /hello HTTP/1.1敲击回车,确认子路径。

键入Host: cs144.keithw.org敲击回车,确认host path。

键入Connection: Close敲击回车,关闭连接。

最后再敲击一次回车,结束HTTP请求。

屏幕上显示如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET /hello HTTP/1.1
Host: cs144.keithw.org
Connection: close

HTTP/1.1 200 OK
Date: Sat, 10 Apr 2021 07:52:03 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.
  1. Assignment

键入:

1
2
3
4
cs144.keithw.org http
GET /lab0/sunetid HTTP/1.1
Host: cs144.keithw.org
Connection: close

显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
aa@desk:~$ telnet cs144.keithw.org http
Trying 104.196.238.229...
Connected to cs144.keithw.org.
Escape character is '^]'.
GET /lab0/sunetid HTTP/1.1
Host: cs144.keithw.org
Connection: close

HTTP/1.1 200 OK
Date: Sat, 10 Apr 2021 08:43:50 GMT
Server: Apache
X-You-Said-Your-SunetID-Was: sunetid
X-Your-Code-Is: 208907
Content-length: 111
Vary: Accept-Encoding
Connection: close
Content-Type: text/plain

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

X-Your-Code-Is: 208907

监听和连接

  1. 输入netcat -v -l -p 9090,开启一个server,开始监听。
1
2
aa@desk:~$ netcat -v -l -p 9090
Listening on 0.0.0.0 9090
  1. 在另一个terminal输入:
1
2
3
4
aa@desk:~$ telnet localhost 9090
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

开启一个client,去连接localhost:9090

这时前一个terminal有如下输出:

1
Connection received on localhost 7012

即前一个terminal的server(netcat)被来自localhost:7012的client(telnet)程序连接。

  1. 在server随便键入一些东西:
1
hello, world!

在client都可以显示出来。

反过来也是。

  1. 在server键入 Ctrl-C,这样这个连接就关闭了。但是在client键入Ctrl-C就没用。

Write a network program using an OS socket

实验目的

用操作系统预先定义好的对TCP的支持,实现一个webget程序,该程序创建TCP stream socket,然后连接到Web Server,最后获取页面内容。

实验条件

操作系统:Ubuntu 20.04

现有代码见链接,按照说明文档编译。

编码要求

  • 善用cpp文档指南
  • 避免使用malloc/free和new/delete这样的“成对”的操作,这样做是为了避免后面一个操作不会发生,例如在throw一个exception的情况下。应当使用的是在构造函数和析构函数里面完成成对的操作。这种编码风格叫“Resource acquisition is initialization”,RAII,即“资源获取就是初始化”。
  • 原则上不用原始指针,即“*”,只在有必要的时候用智能指针,如unique_ptr或者shared_ptr。
  • 避免使用templete、线程、锁、虚函数。
  • 避免C风格的字符串,即“char* str”,如果有必要用的话使用C++的static_cast。
  • 用const的引用传递函数参数。
  • 除非变量需要变化,否则用const类型的变量。
  • 让每个方法都const,除非需要改变对象。
  • 避免全局变量,让每个变量具有最小的作用域。
  • 在提交该作业之前,运行make format统一代码风格。
  • 用git管理,用频繁的小的commit,每次commit说明改动情况及原因。每次commit应该通过更多的测试样例。小的改动有助于debug,也有助于防作弊。自己的git记得用private。
  • 仔细阅读sponge文档,和libsponge/util目录下的头文件file descriptor.hh, socket.hh, address.hh。

思路

Linux提供了基于IP的服务,sponge提供了面向TCP的socket,所以这里用TPCSocket就可以了,需要传递的字符串和之前的一样。读取的时候用TCPSocket::eof()判断是否文件结尾,打印所有接收到的字符。最后有10行左右的代码。

代码

下面列出get_URL()中涉及到上述任务的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TCPSocket tcpsocket;
tcpsocket.connect(Address(host, "http"));
const string strToSend = "GET " + path + " HTTP/1.1\r\n"
+ "Host: " + host + "\r\n"
+ "Connection: close\r\n\r\n"; // 这里简单地复制前面的实验中用到的命令作为字符串就可以了
// 这里的Connection: close用来关闭连接,否则收不到eof
// 如果不用Connection: TCPSocket::shutdown(SHUT_WR)
// 注意这里最后一定是"\r\n\r\n"而不是"\r\n"
tcpsocket.write(strToSend);
// tcpsocket.shutdown(SHUT_WR); // 关闭写通道,如果用了Connection:close就不需要
for(auto s = tcpsocket.read(); !tcpsocket.eof(); s = tcpsocket.read()) {
// 代码可以写的优雅一点(手动狗头)
cout << s;
}
tcpsocket.close();

结果:

1
2
3
4
5
6
7
8
9
10
11
12
aa@desk:/mnt/c/Users/admin/Desktop/CS144/lab0/sponge/build$ ./apps/webget cs144.keithw.org /hello
HTTP/1.1 200 OK
Date: Wed, 14 Apr 2021 13:41:52 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!

通过了嘻嘻

1
2
3
4
5
6
7
8
9
10
aa@desk:/mnt/c/Users/admin/Desktop/CS144/lab0/sponge/build$ make check_webget
[100%] Testing webget...
Test project /mnt/c/Users/admin/Desktop/CS144/lab0/sponge/build
Start 28: t_webget
1/1 Test #28: t_webget ......................... Passed 1.18 sec

100% tests passed, 0 tests failed out of 1

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

An in-memory reliable byte stream

实验目的

在内存里面模拟一个TCP连接,实现提供这些功能的接口。

实验要求

  • 遇到EOF的时候结束。
  • 处理字节流的容器大小受限,满了之后必须读取才能写入。
  • 需要能处理很长很长的字节流。
  • 本实验是在内存里面模拟。后面的实验可能就不是在内存里面模拟,而是在不可靠的IP层上进行。

代码

这里用deque实现字节流,也可以用string。指向字节流的指针用的是unique_ptr。

吐槽:真的离谱,通过t_webget和t_address_dt需要搭梯子,通过t_socket_dt不能用win10自带的ubuntu。

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
68
69
70
71
ByteStream::ByteStream(const size_t capacity): 
_stream(),
_capacity(capacity),
_availCapacity(capacity),
_bytesWritten(0),
_bytesRead(0),
_input_ended(false) { /* { DUMMY_CODE(capacity); } */
_stream.reset(new deque<char>);
}

size_t ByteStream::write(const string &data) {
size_t availLen = min(_availCapacity, data.size());
for(size_t indexData = 0; indexData < availLen; indexData++) {
_stream->emplace_back(data[indexData]);
}
_availCapacity -= availLen;
_bytesWritten += availLen;
return availLen;
}

//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const {
return std::string(_stream->begin(), _stream->begin() + min(min(len, _capacity - _availCapacity), _stream->size()));
}

//! \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); }
size_t availLen = min(len, _capacity - _availCapacity);
for(size_t indexData = 0; indexData < availLen; indexData++) {
_stream->pop_front();
}
_bytesRead += availLen;
_availCapacity += availLen;
}

//! 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 availLen = min(len, _capacity - _availCapacity);
std::string res = peek_output(availLen);
pop_output(availLen);
return res;
}

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

bool ByteStream::input_ended() const { //{ return {}; }
return _input_ended;
}

size_t ByteStream::buffer_size() const { //{ return {}; }
return _capacity - _availCapacity;
}

bool ByteStream::buffer_empty() const { // { return {}; }
if(_availCapacity == _capacity) return true;
else return false;
}

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

size_t ByteStream::bytes_written() const { // { return {}; }
return _bytesWritten;
}

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

size_t ByteStream::remaining_capacity() const { // { return {}; }
return _availCapacity;
}

通过了

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
cs144@cs144vm:/media/sf_share/sponge/build$ make check_lab0
[100%] Testing Lab 0...
Test project /media/sf_share/sponge/build
Start 23: t_byte_stream_construction
1/9 Test #23: t_byte_stream_construction ....... Passed 0.01 sec
Start 24: t_byte_stream_one_write
2/9 Test #24: t_byte_stream_one_write .......... Passed 0.01 sec
Start 25: t_byte_stream_two_writes
3/9 Test #25: t_byte_stream_two_writes ......... Passed 0.01 sec
Start 26: t_byte_stream_capacity
4/9 Test #26: t_byte_stream_capacity ........... Passed 0.99 sec
Start 27: t_byte_stream_many_writes
5/9 Test #27: t_byte_stream_many_writes ........ Passed 0.01 sec
Start 28: t_webget
6/9 Test #28: t_webget ......................... Passed 6.08 sec
Start 50: t_address_dt
7/9 Test #50: t_address_dt ..................... Passed 5.02 sec
Start 51: t_parser_dt
8/9 Test #51: t_parser_dt ...................... Passed 0.01 sec
Start 52: t_socket_dt
9/9 Test #52: t_socket_dt ...................... Passed 0.01 sec

100% tests passed, 0 tests failed out of 9

Total Test time (real) = 12.25 sec
[100%] Built target check_lab0