Lab 0: Networking Warmup
网络热身
1. Docs
预计用时 2 ~ 6 h。
1.1. Main Point
安装 GNU/Linux
Write a small program in C++ that fetches a Web page over the Internet
Implement (in memory) one of the key abstractions of networking: a reliable stream of bytes between a writer and a reader.
1.2. 先决 TASK 0: Set up GNU/Linux on your computer
Lab 需要使用 GNU/Linux 作为系统和支持 C++2023标准的编译器
这里选择使用 VM 虚拟机 Ubuntu 作为实验环境
配备 GNU
1.3. Test
联网测试:
rain0832@ubuntu:~$ ping -c 4 google.com
PING google.com (198.18.0.94) 56(84) bytes of data.
64 bytes from 198.18.0.94 (198.18.0.94): icmp_seq=1 ttl=128 time=1.62 ms
64 bytes from 198.18.0.94 (198.18.0.94): icmp_seq=2 ttl=128 time=1.57 ms
64 bytes from 198.18.0.94 (198.18.0.94): icmp_seq=3 ttl=128 time=2.62 ms
64 bytes from 198.18.0.94 (198.18.0.94): icmp_seq=4 ttl=128 time=2.45 ms
--- google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3008ms
rtt min/avg/max/mdev = 1.572/2.064/2.622/0.474 ms
测试成功。
C++ 23 标准:
首先是要求 g++ 13
,随后可以测试 C++ 23 标准的一些新库是否能够正常引入。
这对于我的老机器确实有点搞。
正常终端指令:
一些 Linux 常用的指令能否正常使用。
2. Networking by hand
Main Topic
HTTP 协议:手动发送 HTTP 请求,观察服务器响应
SMTP 协议:手动共发送邮件,遵循 SMTP 协议
网络通信:本地的服务器 与 本地客户端的简单数据传输
2.1. Fetch a Web page
告诉 telnet 程序计算机在与 cs144.keithw.org
之间打开一个 reliable byte stream。并且需要运行 http 服务 (Hyper-Text Transfer Protocol)
rain0832@ubuntu:~$ telnet cs144.keithw.org http
Trying 198.18.0.99...
Connected to cs144.keithw.org.
Escape character is '^]'.
分别执行以下三行请求:
告诉服务器 URL 的路径
告诉服务器 URL 的 host 部分
告诉服务器已经完成请求
最后加入空行,告诉服务器已经完成了 HTTP request
GET /hello HTTP/1.1
Host: cs144.keithw.org
Connection: close
最后顺利得到一个请求 就像 http://cs144.keithw.org/hello 上看到的那样
HTTP/1.1 200 OK
Date: Mon, 24 Feb 2025 12:50:09 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
相当于课后练习
SUNet ID 是斯坦福的师生唯一标识
我没有SUNet ID,这里用虚拟的 test 来假装我的 SUNet ID...作为实验来说够用了
rain0832@ubuntu:~$ telnet cs144.keithw.org http
Trying 198.18.0.99...
Connected to cs144.keithw.org.
Escape character is '^]'.
GET /lab0/test HTTP/1.1
Host: cs144.keithw.org
Connection: close
HTTP/1.1 200 OK
Date: Mon, 24 Feb 2025 13:06:10 GMT
Server: Apache
X-You-Said-Your-SunetID-Was: test
X-Your-Code-Is: 590984
Content-length: 108
Vary: Accept-Encoding
Connection: close
Content-Type: text/plain
Hello! You told us that your SUNet ID was "test". Please see the HTTP headers (above) for your secret code.
Connection closed by foreign host.
返回了一个 X-Your-Code-Is
的 header,记录下来
X-You-Said-Your-SunetID-Was: test
X-Your-Code-Is: 590984
实验成功
2.2. Send yourself an email
无法接上 斯坦福的VPN...GG,
痛在我不是留子。有机会我去那边读研究生的时候在弄这个吧。
Main Topic:
telnet
连接邮件服务器SMTP 命令 (Simple Mail Transfer Protocol,简单邮件传输协议)
HELO mycomputer.stanford.edu
标识你的计算机,向服务器发起连接请求MAIL FROM: your_sunet_id@stanford.edu
指定发件人地址DATA
表示开始邮件内容邮件内容结束后,输入一个单独的点号(
.
)并按回车键,表示邮件内容结束
最后,
QUIT
结束与邮件服务器的对话,检查你的邮箱有没有收到邮件
Assignment:
用上面的技术给身边的人发送邮件。
2.3. Listening and connecting
监听 和 连接。有点类似我之前写的 C 的 TCP 实验差不多。
可以参考复习:TCP 通讯 Demo,这个实验属于是更底层的实现了。
在 Linux 甚至可以直接用netcat
和 telnet 就可以建立简单的通讯服务
新建终端,启动监听 port 9090 的服务器
netcat -v -l -p 9090
rain0832@ubuntu:~$ netcat -v -l -p 9090
Listening on 0.0.0.0 9090
新建终端,启动客户端并连接本地服务器
telnet localhost 9090
rain0832@ubuntu:~$ telnet localhost 9090
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
这时候
netcat
会提示成功连接
Connection received on localhost 49380
进行通讯,在双方进行文本输入,回车后在异端都可以显示。实验成功
退出通讯
在
netcat
服务器端按Ctrl+C
退出。在
telnet
客户端输入^]
(按住Ctrl
键,然后按]
键),然后输入quit
退出。
3. Writing a network program using an OS stream socket
3.1. Let’s get started—setting up the repository on your VM and on GitHub
先克隆官方的仓库。
git clone https://github.com/cs144/minnow
开一个个人仓库...把 CS144 的 grader 加进 collaborators...这一步我跳过了。毕竟用的虚拟机,代码后面统一转过去吧。
3.2. Compiling the starter code
CMake 编译代码 熟悉代码库的结构
CMake 我还没安装,先 sudo apt 一下
居然不支持 3.18.xx...要求真高
创建构造目录
cmake -S . -B build
,初始化构建环境
rain0832@ubuntu:~/CS144/minnow$ cmake -S . -B build
-- The CXX compiler identification is GNU 11.4.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Setting build type to 'Debug'
-- Building in 'Debug' mode.
-- Configuring done (0.6s)
-- Generating done (0.2s)
-- Build files have been written to: /home/rain0832/CS144/minnow/build
编译代码
cmake --build build
初次编译失败,大概是少了一个 库...
最终发现是因为 GNU 的版本太低了,不支持 C++20 的 <format>
最后还是要更新 g++,参考 Blog,最后成功
rain0832@ubuntu:~/CS144/minnow$ cmake --build build
[ 10%] Building CXX object src/CMakeFiles/minnow_debug.dir/byte_stream.cc.o
[ 10%] Building CXX object util/CMakeFiles/util_debug.dir/address.cc.o
[ 15%] Building CXX object src/CMakeFiles/minnow_debug.dir/byte_stream_helpers.cc.o
[ 21%] Linking CXX static library libminnow_debug.a
[ 21%] Built target minnow_debug
[ 26%] Building CXX object tests/CMakeFiles/minnow_testing_debug.dir/common.cc.o
[ 31%] Building CXX object util/CMakeFiles/util_debug.dir/debug.cc.o
In file included from /home/rain0832/CS144/minnow/tests/common.hh:4,
from /home/rain0832/CS144/minnow/tests/common.cc:1:
/home/rain0832/CS144/minnow/util/debug.hh:3:10: fatal error: format: No such file or directory
3 | #include <format>
| ^~~~~~~~
compilation terminated.
make[2]: *** [tests/CMakeFiles/minnow_testing_debug.dir/build.make:79: tests/CMakeFiles/minnow_testing_debug.dir/common.cc.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:2978: tests/CMakeFiles/minnow_testing_debug.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
[ 36%] Building CXX object util/CMakeFiles/util_debug.dir/eventloop.cc.o
In file included from /home/rain0832/CS144/minnow/util/debug.cc:1:
/home/rain0832/CS144/minnow/util/debug.hh:3:10: fatal error: format: No such file or directory
3 | #include <format>
| ^~~~~~~~
compilation terminated.
make[2]: *** [util/CMakeFiles/util_debug.dir/build.make:93: util/CMakeFiles/util_debug.dir/debug.cc.o] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [CMakeFiles/Makefile2:2786: util/CMakeFiles/util_debug.dir/all] Error 2
make: *** [Makefile:101: all] Error 2
更新 g++ 后成功编译
rain0832@ubuntu:~/CS144/minnow$ cmake --build build
[ 15%] Built target minnow_debug
[ 21%] Building CXX object util/CMakeFiles/util_debug.dir/debug.cc.o
[ 26%] Building CXX object tests/CMakeFiles/minnow_testing_debug.dir/common.cc.o
[ 31%] Building CXX object util/CMakeFiles/util_debug.dir/eventloop.cc.o
[ 36%] Linking CXX static library libminnow_testing_debug.a
[ 36%] Built target minnow_testing_debug
[ 42%] Building CXX object apps/CMakeFiles/stream_copy.dir/bidirectional_stream_copy.cc.o
[ 47%] Building CXX object util/CMakeFiles/util_debug.dir/file_descriptor.cc.o
[ 52%] Linking CXX static library libstream_copy.a
[ 52%] Built target stream_copy
[ 57%] Building CXX object util/CMakeFiles/util_debug.dir/helpers.cc.o
[ 63%] Building CXX object util/CMakeFiles/util_debug.dir/random.cc.o
[ 68%] Building CXX object util/CMakeFiles/util_debug.dir/socket.cc.o
[ 73%] Linking CXX static library libutil_debug.a
[ 78%] Built target util_debug
[ 84%] Building CXX object apps/CMakeFiles/webget.dir/webget.cc.o
[ 89%] Building CXX object apps/CMakeFiles/tcp_native.dir/tcp_native.cc.o
[ 94%] Linking CXX executable webget
[100%] Linking CXX executable tcp_native
[100%] Built target webget
[100%] Built target tcp_native
3.3. Modern C++: mostly safe but still fast and low-level
本小节介绍 CS144 Lab 的基础是 现代 C++ 风格的代码。介绍现代 C++ 的特效。
we would like you to:
使用语言文档作为 https://en.cppreference.com/,而不是 cplusplus.com(out of date)
不再使用 malloc() or free()
不再使用 new or delete
不再使用 raw pointers (*),而是在必要的时候使用 "smart" pointers
避免 templates, threads, locks, and virtual functions
避免 C 风格的 字符串 和字符串处理函数,这容易出错。而是使用
std : : string
避免 C 风格的 强制类型转换。而是使用 C++
static_cast
避免全局变量。而是给每个变量尽量小的 scope
首选使用 const 引用传递函数参数
首选使用 const 来标注所有变量,除非它需要被改变 mutate
首选使用 const 来标注所有方法,除非他需要改变对象 mutate the object
提交前,运行
cmake--build build--target tidy
以获取有关如何改进与 C++ 编程实践相关的代码的建议,以及cmake--build build--target format
以一致地格式化代码。
To repeat: 尽量频繁的使用 Commit,并且使用 Commit to identify what to change and why
3.4. Reading the Minnow support code
阅读 Minnow 的公共接口。里面提供了一些现代 C++ 的容器。
util/socket.hh
util/file descriptor.hh
请注意,Socket 是 FileDescriptor 的一种类型,而 TCPSocket 是 Socket 的一种类型。
3.5. Writing webget
现在要写一个程序 fetch 一个 Web,通过系统的 TCP 和 stream-socket 抽象(类似上面我说的以前写的 TCP 实验)
打开 ../apps/webget.cc
get_URL 函数:
实现简单的 Web 客户端
使用 HTTP 的 format
使用 TCPSocket and Address 类
Hints:
HTTP 的换行 ended with "\r\n" (用 "\n" 或者 endl 是不够的)
不要忘记 Connection: close 在客户端请求,实际上在读到 EOF 的时候会也会停止
确保 read and print all output from 服务端,直到 socket 达到 EOF
写大约 10 行代码
运行
cmake--build build
,然后自己 debug测试
./apps/webget cs144.keithw.org /hello
,和你直接打开 http://cs144.keithw.org/hello 有什么不一样?和你在 Section 2.1 有什么区别?如果一切正常,运行
cmake--build build--target check webget
,在get_URL
还没有完成的时候会有报错和警告,完后之后就可以得到 Test Passed 的信息
本小节主要是需要实现 get_URL 的基本功能
实现代码:来自 kimi
void get_URL(const string &host, const string &path)
{
// 创建一个 TCP 套接字
TCPSocket socket;
// 解析主机地址并连接
Address server_address(host, "http");
socket.connect(server_address);
// 构造 HTTP 请求
string request = "GET " + path + " HTTP/1.1\r\n";
request += "Host: " + host + "\r\n";
request += "Connection: close\r\n\r\n";
// 发送请求
socket.write(request);
// 读取响应
string response;
while (!socket.eof()) {
response += socket.read();
}
// 打印响应内容
cout << response << endl;
// 报错信息,Lab 自带
cerr << ... << endl;
cerr << ... << endl;
}
第一版代码固然是有误的,根据 GNU 的提示,是 line 22 的 socket.read() 必须要含有一个参数。所以可以假设将数据读入到 buffer 中,再把 buffer 加入 response
#include "socket.hh"
#include <cstdlib>
#include <iostream>
#include <span>
#include <string>
using namespace std;
void get_URL(const string& host, const string& path) {
// create TCP socket
TCPSocket socket;
// parse the address and connect
Address server_address(host, "http");
socket.connect(server_address);
// create HTTP request
string request = "GET " + path + " HTTP/1.1\r\n";
request += "Host: " + host + "\r\n";
request += "Connection: close\r\n\r\n";
// send the request
socket.write(request);
// read the response
string response;
string buffer; // 用于存储每次读取的数据
while (!socket.eof()) {
socket.read(buffer); // 读取数据到 buffer
response += buffer; // 将 buffer 的内容追加到 response
buffer.clear(); // 清空 buffer 以便下一次读取
}
// print the response
cout << response << endl;
}
int main(int argc, char* argv[]) {
try {
if (argc <= 0) {
abort(); // For sticklers: don't try to access argv[0] if argc <= 0.
}
auto args = span(argv, argc);
// The program takes two command-line arguments: the hostname and "path" part of the URL.
// Print the usage message unless there are these two arguments (plus the program name itself, so arg count = 3 in total).
if (argc != 3) {
cerr << "Usage: " << args.front() << " HOST PATH\n";
cerr << "\tExample: " << args.front() << " stanford.edu /class/cs144\n";
return EXIT_FAILURE;
}
// Get the command-line arguments.
const string host{args[1]};
const string path{args[2]};
// Call the student-written function.
get_URL(host, path);
} catch (const exception& e) {
cerr << e.what() << "\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
这次的 cmake 可以通过了,但是这时候发现...文档给的命令有点问题...或者是我的打开方式不对...在 ./apps 在貌似没有 webget 文件夹啊
问题解决了!
执行./apps/webget cs144.keithw.org /hello
输出正常
rain0832@ubuntu:~/CS144/minnow/build$ ./apps/webget cs144.keithw.org /hello
HTTP/1.1 200 OK
Date: Tue, 25 Feb 2025 12:21:33 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!
Function called: get_URL(cs144.keithw.org, /hello)
Warning: get_URL() has not been implemented yet.
原来是因为,目录大概是这样的
minnow -> 有 apps 和 build
其中 build 是 cmake 创建的执行文件,而 apps 里面的则是 webget.cc 文件
build里面也包含了一个 apps,这个 apps 里面的则是 webget...
这时候把根目录改成 ../build 再执行官方给的之林那个即可,这时候可以得到上面的结果。
发现和 Section 2 的差不多。
但是有 2 lines 的提示,get_URL() 还没完全实现。貌似两行的 err 是 cerr 引起的,删去之后,重新 cmake 发现好多了。
rain0832@ubuntu:~/CS144/minnow/build$ ./apps/webget cs144.keithw.org /hello
HTTP/1.1 200 OK
Date: Tue, 25 Feb 2025 12:30:59 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!
于是回去更改 get_URL 的代码,更改了一些
完整的 webget.cc
的代码
#include "socket.hh"
#include <cstdlib>
#include <iostream>
#include <span>
#include <string>
using namespace std;
void get_URL( const string& host, const string& path )
{
// create TCP socket
TCPSocket socket;
// parse the address and connect
Address server_address(host, "http");
socket.connect(server_address);
// create HTTP request
string request = "GET " + path + " HTTP/1.1\r\n";
request += "Host: " + host + "\r\n";
request += "Connection: close\r\n\r\n";
// send the request
socket.write(request);
// read the response
string response;
string buffer;
while(!socket.eof())
{
socket.read(buffer);
response += buffer;
buffer.clear();
}
// extract the body part of the HTTP response
size_t body_start = response.find("\r\n\r\n") + 4;
if (body_start != string::npos)
{
cout <<response.substr(body_start);
}
else
{
// print the response
cout << response << endl;
}
}
int main( int argc, char* argv[] )
{
try {
if ( argc <= 0 ) {
abort(); // For sticklers: don't try to access argv[0] if argc <= 0.
}
auto args = span( argv, argc );
// The program takes two command-line arguments: the hostname and "path" part of the URL.
// Print the usage message unless there are these two arguments (plus the program name
// itself, so arg count = 3 in total).
if ( argc != 3 ) {
cerr << "Usage: " << args.front() << " HOST PATH\n";
cerr << "\tExample: " << args.front() << " stanford.edu /class/cs144\n";
return EXIT_FAILURE;
}
// Get the command-line arguments.
const string host { args[1] };
const string path { args[2] };
// Call the student-written function.
get_URL( host, path );
} catch ( const exception& e ) {
cerr << e.what() << "\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
最终成功通过测试...
rain0832@ubuntu:~/CS144/minnow$ cmake --build build --target check_webget
Test project /home/rain0832/CS144/minnow/build
Start 1: compile with bug-checkers
1/2 Test #1: compile with bug-checkers ........ Passed 0.71 sec
Start 2: t_webget
2/2 Test #2: t_webget ......................... Passed 2.26 sec
100% tests passed, 0 tests failed out of 2
Total Test time (real) = 2.98 sec
Built target check_webget
任务完成
主要实现的函数如下,虽然没有任何一行代码是我写的...我也不直到为什么要这么写...钻研一下,基本理解代码框架结构。一共 17 行代码,耗时 3 天......效率泪目
void get_URL( const string& host, const string& path )
{
// create TCP socket 创建 套接字...
TCPSocket socket;
// parse the address and connect 解析地址,把网址转为 IP地址 + 80 端口
Address server_address(host, "http");
// 连接服务器
socket.connect(server_address);
// create HTTP request
// 对某网址 发起 HTTP 请求
// GET 关键字 + path 路径 + HTTP 版本
string request = "GET " + path + " HTTP/1.1\r\n";
// 请求 主机字段
request += "Host: " + host + "\r\n";
// 请求 一旦响应就进行连接关闭。额外的空行表示结束
request += "Connection: close\r\n\r\n";
// send the request
// 写入请求,发送给服务器
socket.write(request);
// read the response
string response; // 接收响应
string buffer; // 先读入缓存
// eof 表示响应的结束
while(!socket.eof())
{
socket.read(buffer);
response += buffer;
buffer.clear();
}
// extract the body part of the HTTP response
// find 能找到响应头,也是 \r\n\r\n 表示结束
// 结束之后就是我们要的响应
size_t body_start = response.find("\r\n\r\n") + 4;
// 如果 body_start == string::npos 说明没找到 \r\n\r\n 这时候 整个响应就是正文...直接 print
if (body_start != string::npos)
{
cout <<response.substr(body_start);
}
else
{
// print the response
cout << response << endl;
}
}
4. An in-memory reliable byte stream
实现字节流。
实现一个内存中的可靠字节流对象,模拟网络中的可靠字节流。
提供写入和读取接口,并实现流量控制,限制内存使用。
通过自动化测试,并确保实现的性能满足要求(速度大于 0.1 Gbit/s)。
4.1. Writer
void push( std::string data );
// Push data to stream, but only as much as available capacity allows.
void close();
// Signal that the stream has reached its ending. Nothing more will be written.
bool is_closed() const;
// Has the stream been closed?
uint64_t available_capacity() const;
// How many bytes can be pushed to the stream right now?
uint64_t bytes_pushed() const;
// Total number of bytes cumulatively pushed to the stream
void push( std::string data );
推送数据 stream ,但必须在容量大小之内
void close();
表示这个 stream 已经 end 了,不需要再写入任何东西
bool is_closed() const;
判断 stream 是否 close
uint64_t available_capacity() const;
当前 stream 还有多少 bytes可以 push
uint64_t bytes_pushed() const;
当前 stream 中总共 push 的 bytes
4.2. Reader
std::string_view peek() const;
// Peek at the next bytes in the buffer
void pop( uint64_t len );
// Remove `len` bytes from the buffer
bool is_finished() const;
// Is the stream finished (closed and fully popped)?
bool has_error() const;
// Has the stream had an error?
uint64_t bytes_buffered() const;
// Number of bytes currently buffered (pushed and not popped)
uint64_t bytes_popped() const;
// Total number of bytes cumulatively popped from stream
std::string_view peek() const;
peek buffer 的 下一个 bytes
void pop( uint64_t len );
从缓存区移除 len 大小 bytes
bool is_finished() const;
判断 stream 是否 close 或 full pop
bool has_error() const;
判断 stream 是否有 error
uint64_t bytes_buffered() const;
当前 buffer 有多少个 bytes
uint64_t bytes_popped() const;
当前已经移除的 bytes 数量
4.3. 代码设计
这几天大概自己试着写了一下代码,也一边看课程一遍 Coding 尝试了许多次。自己还是不大能理解其思路,也没有完全理解 Lab 的精髓。
CS 144 的 Lab Docs 像是一个谜语人
-- 广大网友
跟着网上的 开源仓库 敲了一下代码,试图理解思路。
不知道为什么,25年的 buffer_ 也要求要初始化了。
byte_stream.cc
其中一个难点是返回值的 string_view 的 peek()
生命周期:
std::string_view
不拥有数据,因此它引用的底层数据(buffer_
)必须在std::string_view
的生命周期内有效。如果
buffer_
被修改或销毁,std::string_view
将引用无效数据。
线程安全:
如果
Reader
类在多线程环境中使用,需要确保对buffer_
的访问是线程安全的。
另外需要注意各种函数之间的引用关系,还有一些私有变量的定义。
总的来说 Lab0 还是很有挑战的,对于我来说,自由度也很大,也可能跟我的代码能力比较弱有关系。
#include "byte_stream.hh"
using namespace std;
ByteStream::ByteStream( uint64_t capacity )
: capacity_(capacity), buffer_(), bytes_pushed_(0), bytes_popped_(0), closed_flag_(false)
{}
void Writer::push( string data )
{
// invail operation
if(is_closed() || has_error())
{
return ;
}
// data less? or the capacity()
const size_t len = min(data.length(), available_capacity());
buffer_.append(data.substr(0, len));
bytes_pushed_ += len;
return ;
}
void Writer::close()
{
closed_flag_ = true;
return ;
}
bool Writer::is_closed() const
{
return {closed_flag_};
}
uint64_t Writer::available_capacity() const
{
// total capacity - buffer.
// buffer = push - pop.
return {capacity_ - (bytes_pushed_ - bytes_popped_)};
}
uint64_t Writer::bytes_pushed() const
{
return {bytes_pushed_};
}
// challenge1
// offer only-veiw buffer, instead of buffer_copy
string_view Reader::peek() const
{
// list init string_view object
// object -> pointer to -> buffer
return {this->buffer_};
}
void Reader::pop( uint64_t len )
{
if(bytes_buffered() == 0)
{
return ;
}
len = min(len, buffer_.length());
// remove data from buffer_.
buffer_.erase(0, len);
bytes_popped_ += len;
return ;
}
bool Reader::is_finished() const
{
// if close and buffer.clear -> can finish
return {closed_flag_ && bytes_buffered() == 0};
}
uint64_t Reader::bytes_buffered() const
{
return {bytes_pushed_ - bytes_popped_};
}
uint64_t Reader::bytes_popped() const
{
return {bytes_popped_};
}
byte_stream.hh
#pragma once
#include <cstdint>
#include <string>
#include <string_view>
class Reader;
class Writer;
class ByteStream
{
public:
explicit ByteStream( uint64_t capacity );
// Helper functions (provided) to access the ByteStream's Reader and Writer interfaces
Reader& reader();
const Reader& reader() const;
Writer& writer();
const Writer& writer() const;
void set_error() { error_ = true; }; // Signal that the stream suffered an error.
bool has_error() const { return error_; }; // Has the stream had an error?
protected:
// Please add any additional state to the ByteStream here, and not to the Writer and Reader interfaces.
uint64_t capacity_;
std::string buffer_;
uint64_t bytes_pushed_ {};
uint64_t bytes_popped_ {};
bool closed_flag_ {};
bool error_ {};
};
class Writer : public ByteStream
{
public:
void push( std::string data ); // Push data to stream, but only as much as available capacity allows.
void close(); // Signal that the stream has reached its ending. Nothing more will be written.
bool is_closed() const; // Has the stream been closed?
uint64_t available_capacity() const; // How many bytes can be pushed to the stream right now?
uint64_t bytes_pushed() const; // Total number of bytes cumulatively pushed to the stream
};
class Reader : public ByteStream
{
public:
std::string_view peek() const; // Peek at the next bytes in the buffer
void pop( uint64_t len ); // Remove `len` bytes from the buffer
bool is_finished() const; // Is the stream finished (closed and fully popped)?
uint64_t bytes_buffered() const; // Number of bytes currently buffered (pushed and not popped)
uint64_t bytes_popped() const; // Total number of bytes cumulatively popped from stream
};
/*
* read: A (provided) helper function thats peeks and pops up to `max_len` bytes
* from a ByteStream Reader into a string;
*/
void read( Reader& reader, uint64_t max_len, std::string& out );
cmake
测试结果如下
rain0832@ubuntu:~/CS144/minnow$ cmake --build build --target check0
Test project /home/rain0832/CS144/minnow/build
Start 1: compile with bug-checkers
1/11 Test #1: compile with bug-checkers ........ Passed 28.10 sec
Start 2: t_webget
2/11 Test #2: t_webget ......................... Passed 4.14 sec
Start 3: byte_stream_basics
3/11 Test #3: byte_stream_basics ............... Passed 0.04 sec
Start 4: byte_stream_capacity
4/11 Test #4: byte_stream_capacity ............. Passed 0.03 sec
Start 5: byte_stream_one_write
5/11 Test #5: byte_stream_one_write ............ Passed 0.03 sec
Start 6: byte_stream_two_writes
6/11 Test #6: byte_stream_two_writes ........... Passed 0.02 sec
Start 7: byte_stream_many_writes
7/11 Test #7: byte_stream_many_writes .......... Passed 0.19 sec
Start 8: byte_stream_stress_test
8/11 Test #8: byte_stream_stress_test .......... Passed 0.06 sec
Start 37: no_skip
9/11 Test #37: no_skip .......................... Passed 0.02 sec
Start 38: compile with optimization
10/11 Test #38: compile with optimization ........ Passed 14.14 sec
Start 39: byte_stream_speed_test
ByteStream throughput (pop length 4096): 7.68 Gbit/s
ByteStream throughput (pop length 128): 1.79 Gbit/s
ByteStream throughput (pop length 32): 0.45 Gbit/s
11/11 Test #39: byte_stream_speed_test ........... Passed 0.78 sec
100% tests passed, 0 tests failed out of 11
Total Test time (real) = 47.57 sec
Built target check0
测试通过
4.4. 提交代码
最后就是传统的 Git Submit
这里 Docs 介绍了 Cmake 的一些其他方法,除了进行代码编译,还可以进行代码格式调整和检查 C++ 规范。
cmake --build build --target format
to normalize the coding style
规范代码格式
cmake --build build --target tidy
完善符合 C++ 规范的代码
Last updated