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 #
sudo apt install netcat-traditional
3 Writing a network program using an OS stream socket #
3.5 Writing webget
#
void get_URL( const string& host, const string& path )
{
Address address { host, "http" };
TCPSocket socket;
socket.connect( address );
socket.set_reuseaddr();
socket.write( "GET " + path + " HTTP/1.1\r\n" );
socket.write( "Host: " + host + "\r\n" );
socket.write( "Connection: close\r\n" );
socket.write( "\r\n" );
string response;
// read until EOF
while ( !socket.eof() ) {
string buffer;
socket.read( buffer );
response += buffer;
}
cout << response;
socket.close();
}
这里设置了 SO_REUSEADDR flag 用来允许地址重用,也就是说允许绑定到一个存在已建立连接的地址,除非另一个 socket 正在监听它。可能会存在这种情况:服务器启动后,有客户端连接并已建立,如果服务器主动关闭,那么和客户端的连接会处于 TIME_WAIT 状态。如果没有设置地址重用,再次启动服务器时,就会因 Address already in use 而启动失败。
4 An in-memory reliable byte stream #
首先用 std::string 实现了一个 naive 版的:
std::string buffer_ {};
void Writer::push( string data )
{
uint64_t space_available = available_capacity();
uint64_t len = min( space_available, static_cast<uint64_t>(data.size()) );
if ( len > 0 ) {
buffer_.append( data, 0, len );
bytes_pushed_ += len;
}
}
string_view Reader::peek() const
{
return {buffer_.data(), buffer_.size()};
}
void Reader::pop( uint64_t len )
{
len = min( len, static_cast<uint64_t>(buffer_.size()) );
bytes_popped_ += len;
buffer_.erase( 0, len );
}
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:
std::vector<char> buffer_; // Ring buffer
uint64_t head_ {}; // Read position
uint64_t tail_ {}; // Write position
uint64_t size_ {}; // Current data size
void Writer::push( string data )
{
uint64_t space_available = available_capacity();
uint64_t len = min( space_available, static_cast<uint64_t>(data.size()) );
if (tail_ + len <= buffer_.size())
{
copy(data.data(), data.data() + len, buffer_.data() + tail_);
}
else
{
copy(data.data(), data.data() + (buffer_.size() - tail_), buffer_.data() + tail_);
copy(data.data() + (buffer_.size() - tail_), data.data() + len, buffer_.data());
}
tail_ = (tail_ + len) % buffer_.size();
bytes_pushed_ += len;
size_ += len;
}
string_view Reader::peek() const
{
if (size_ == 0)
return {};
if (head_ + size_ <= buffer_.size())
return {buffer_.data() + head_, size_};
else
{
static string tmp;
tmp.resize(size_);
copy(buffer_.data() + head_, buffer_.data() + buffer_.size(), tmp.data());
copy(buffer_.data(), buffer_.data() + ((head_ + size_) % buffer_.size()), tmp.data() + (buffer_.size() - head_));
return {tmp.data(), tmp.size()};
}
}
void Reader::pop( uint64_t len )
{
len = min( len, size_ );
head_ = (head_ + len) % buffer_.size();
bytes_popped_ += len;
size_ -= len;
}
然而其速度十分拉跨,猜测可能是因为访存不连续以及大段 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 实现了一个,速度最慢,就不放代码了。