Stanford CS144 Lab 0
Lab Checkpoint 0: networking warmup
寒假在几个不同的地方刷到这门课的介绍,想在 25 春跟着过一遍,最后显然是没有跟。三伏天的中伏,偶然想起来,决定在暑假把 lab 做了,也算找回一点失去的热情。
1 Set up GNU/Linux on your computer #
Debian 12 on WSL2,没啥说的。
lab 框架用了一些 modern C++ 特性,需要 gcc > 13,于是自己编译了,详见上一篇文章。
2 Networking by hand #
2.1 Fetch a Web page #
输了我的清华学号居然也能给我一个 secret,可见这个东西是按某种规则计算出来的。
➜ ~ telnet cs144.keithw.org http
Trying 104.196.238.229...
Connected to cs144.keithw.org.
Escape character is '^]'.
GET /lab0/2023XXXXXX HTTP/1.1
Host: cs144.keithw.org
Connection: close
HTTP/1.1 200 OK
Date: Wed, 30 Jul 2025 14:35:08 GMT
Server: Apache
X-You-Said-Your-SunetID-Was: 2023XXXXXX
X-Your-Code-Is: 593860
Content-length: 114
Vary: Accept-Encoding
Connection: close
Content-Type: text/plain
Hello! You told us that your SUNet ID was "2023XXXXXX". Please see the HTTP headers (above) for your secret code.
Connection closed by foreign host.
2.2 Send yourself a email #
手敲 SMTP 协议,但我又不是斯坦福学生,告辞。
2.3 Listening and connecting #
1sudo apt install netcat-traditional3 Writing a network program using an OS stream socket #
3.5 Writing webget
#
1void get_URL( const string& host, const string& path )
2{
3 Address address { host, "http" };
4 TCPSocket socket;
5 socket.connect( address );
6 socket.set_reuseaddr();
7 socket.write( "GET " + path + " HTTP/1.1\r\n" );
8 socket.write( "Host: " + host + "\r\n" );
9 socket.write( "Connection: close\r\n" );
10 socket.write( "\r\n" );
11
12 string response;
13 // read until EOF
14 while ( !socket.eof() ) {
15 string buffer;
16 socket.read( buffer );
17 response += buffer;
18 }
19 cout << response;
20 socket.close();
21}这里设置了 SO_REUSEADDR flag 用来允许地址重用,也就是说允许绑定到一个存在已建立连接的地址,除非另一个 socket 正在监听它。可能会存在这种情况:服务器启动后,有客户端连接并已建立,如果服务器主动关闭,那么和客户端的连接会处于 TIME_WAIT 状态。如果没有设置地址重用,再次启动服务器时,就会因 Address already in use 而启动失败。
4 An in-memory reliable byte stream #
首先用 std::string 实现了一个 naive 版的:
1std::string buffer_ {};
2
3void Writer::push( string data )
4{
5 uint64_t space_available = available_capacity();
6 uint64_t len = min( space_available, static_cast<uint64_t>(data.size()) );
7 if ( len > 0 ) {
8 buffer_.append( data, 0, len );
9 bytes_pushed_ += len;
10 }
11}
12
13string_view Reader::peek() const
14{
15 return {buffer_.data(), buffer_.size()};
16}
17
18void Reader::pop( uint64_t len )
19{
20 len = min( len, static_cast<uint64_t>(buffer_.size()) );
21 bytes_popped_ += len;
22 buffer_.erase( 0, len );
23}Test project /home/rhdu/cs144-minnow-25winter/build
Start 1: compile with bug-checkers
1/11 Test #1: compile with bug-checkers ........ Passed 4.20 sec
Start 2: t_webget
2/11 Test #2: t_webget ......................... Passed 1.83 sec
Start 3: byte_stream_basics
3/11 Test #3: byte_stream_basics ............... Passed 0.01 sec
Start 4: byte_stream_capacity
4/11 Test #4: byte_stream_capacity ............. Passed 0.01 sec
Start 5: byte_stream_one_write
5/11 Test #5: byte_stream_one_write ............ Passed 0.01 sec
Start 6: byte_stream_two_writes
6/11 Test #6: byte_stream_two_writes ........... Passed 0.01 sec
Start 7: byte_stream_many_writes
7/11 Test #7: byte_stream_many_writes .......... Passed 0.04 sec
Start 8: byte_stream_stress_test
8/11 Test #8: byte_stream_stress_test .......... Passed 0.02 sec
Start 37: no_skip
9/11 Test #37: no_skip .......................... Passed 0.00 sec
Start 38: compile with optimization
10/11 Test #38: compile with optimization ........ Passed 2.19 sec
Start 39: byte_stream_speed_test
ByteStream throughput (pop length 4096): 31.69 Gbit/s
ByteStream throughput (pop length 128): 6.86 Gbit/s
ByteStream throughput (pop length 32): 1.53 Gbit/s
11/11 Test #39: byte_stream_speed_test ........... Passed 0.19 sec
100% tests passed, 0 tests failed out of 11
Total Test time (real) = 8.51 sec
Built target check0
我想看看怎么提高性能,于是用 std::vector 写了一个 ring buffer:
1std::vector<char> buffer_; // Ring buffer
2uint64_t head_ {}; // Read position
3uint64_t tail_ {}; // Write position
4uint64_t size_ {}; // Current data size
5
6void Writer::push( string data )
7{
8 uint64_t space_available = available_capacity();
9 uint64_t len = min( space_available, static_cast<uint64_t>(data.size()) );
10 if (tail_ + len <= buffer_.size())
11 {
12 copy(data.data(), data.data() + len, buffer_.data() + tail_);
13 }
14 else
15 {
16 copy(data.data(), data.data() + (buffer_.size() - tail_), buffer_.data() + tail_);
17 copy(data.data() + (buffer_.size() - tail_), data.data() + len, buffer_.data());
18 }
19 tail_ = (tail_ + len) % buffer_.size();
20 bytes_pushed_ += len;
21 size_ += len;
22}
23
24string_view Reader::peek() const
25{
26 if (size_ == 0)
27 return {};
28 if (head_ + size_ <= buffer_.size())
29 return {buffer_.data() + head_, size_};
30 else
31 {
32 static string tmp;
33 tmp.resize(size_);
34 copy(buffer_.data() + head_, buffer_.data() + buffer_.size(), tmp.data());
35 copy(buffer_.data(), buffer_.data() + ((head_ + size_) % buffer_.size()), tmp.data() + (buffer_.size() - head_));
36 return {tmp.data(), tmp.size()};
37 }
38}
39
40void Reader::pop( uint64_t len )
41{
42 len = min( len, size_ );
43 head_ = (head_ + len) % buffer_.size();
44 bytes_popped_ += len;
45 size_ -= len;
46}然而其速度十分拉跨,猜测可能是因为访存不连续以及大段 copy 导致的。
Test project /home/rhdu/cs144-minnow-25winter/build
Start 1: compile with bug-checkers
1/11 Test #1: compile with bug-checkers ........ Passed 1.08 sec
Start 2: t_webget
2/11 Test #2: t_webget ......................... Passed 1.87 sec
Start 3: byte_stream_basics
3/11 Test #3: byte_stream_basics ............... Passed 0.01 sec
Start 4: byte_stream_capacity
4/11 Test #4: byte_stream_capacity ............. Passed 0.01 sec
Start 5: byte_stream_one_write
5/11 Test #5: byte_stream_one_write ............ Passed 0.01 sec
Start 6: byte_stream_two_writes
6/11 Test #6: byte_stream_two_writes ........... Passed 0.01 sec
Start 7: byte_stream_many_writes
7/11 Test #7: byte_stream_many_writes .......... Passed 0.04 sec
Start 8: byte_stream_stress_test
8/11 Test #8: byte_stream_stress_test .......... Passed 0.02 sec
Start 37: no_skip
9/11 Test #37: no_skip .......................... Passed 0.00 sec
Start 38: compile with optimization
10/11 Test #38: compile with optimization ........ Passed 0.53 sec
Start 39: byte_stream_speed_test
ByteStream throughput (pop length 4096): 31.31 Gbit/s
ByteStream throughput (pop length 128): 2.92 Gbit/s
ByteStream throughput (pop length 32): 0.78 Gbit/s
11/11 Test #39: byte_stream_speed_test ........... Passed 0.26 sec
100% tests passed, 0 tests failed out of 11
Total Test time (real) = 3.84 sec
Built target check0
最后用 std::deque 实现了一个,速度最慢,就不放代码了。