1 在你的计算机上安装 GNU/Linux

CS144 的作业需要 GNU/Linux 操作系统和一个支持 C++ 2023 标准的最新 C++ 编译器。请选择以下三个选项之一:

  1. 推荐:安装 CS144 VirtualBox 虚拟机镜像(说明在 https://stanford.edu/class/cs144/vm_howto/vm-howto-image.html)。
  2. 使用我们课程的优惠券代码,使用 Google Cloud 虚拟机(说明在 https://stanford.edu/class/cs144/vm_howto)。
  3. 运行 Ubuntu 24.04 版本,然后安装所需的软件包:
    1
    2
    sudo apt update && sudo apt install git cmake gdb build-essential clang \
    clang-tidy clang-format gcc-doc pkg-config glibc-doc tcpdump tshark
  4. “风险自负”地使用其他 GNU/Linux 发行版,但请注意,你可能会在此过程中遇到障碍,并且需要能够熟练调试它们。你的代码将在 Ubuntu 24.04 LTS 和 g++ 13.3 环境下进行测试,并且必须在这些条件下正确编译和运行。
  5. 如果你有一台 2020-24 年的 MacBook(带有 ARM64 M 系列芯片),VirtualBox 将无法成功运行。请改为安装 UTM 虚拟机软件和我们的 ARM64 虚拟机镜像,地址:https://stanford.edu/class/cs144/vm_howto/。

解答

本文在Ubuntu 24.04环境下实现。

科学配置

  1. 利用clash科学上网
    参考 https://root-hbx.github.io/carrot-world/BLOG/Linux/ubuntu-basic/
    上述操作可在浏览器内科学上网,但终端仍然不行。

    浏览器通常会自动使用系统设置的代理或者有专门的插件(如 SwitchyOmega)来配置代理,所以当你开启 Clash 时,浏览器知道通过 Clash 来访问网络。

    但是,终端(Terminal)里的命令行工具(比如 curl, wget, git, ping,以及包管理器如 apt, yum, brew 等)默认情况下不会自动使用你在 Clash 里设置的代理。它们会尝试直接连接互联网,如果直接连接不通,就会失败。

  2. 为终端配置代理
    你需要手动为你的终端会话设置代理环境变量,告诉命令行工具通过 Clash 的本地代理端口访问网络。
    编辑 ~/.bashrc 文件 (如果不存在则创建):

    1
    nano ~/.bashrc

    在文件末尾添加以下两行 (或三行,如果也用 all_proxy):

    1
    2
    3
    export http_proxy="http://127.0.0.1:7897"
    export https_proxy="http://127.0.0.1:7897"
    # export all_proxy="socks5://127.0.0.1:7897" # 可选

    保存文件 (在 nano 中是 Ctrl+O,然后回车,再 Ctrl+X 退出)。
    然后使配置生效:

    1
    source ~/.bashrc

    或者关闭当前终端并重新打开一个新的终端。


2 手动进行网络操作

让我们开始使用网络。你将手动完成两项任务:获取一个网页(就像 Web 浏览器一样)和发送一封电子邮件(就像电子邮件客户端一样)。这两项任务都依赖于一种称为可靠双向字节流的网络抽象:你将在终端中输入一个字节序列,同样的字节序列最终将以相同的顺序传递到另一台计算机(服务器)上运行的程序。服务器会用它自己的字节序列进行响应,并传送回你的终端。

2.1 获取网页

  1. 在网页浏览器中,访问 http://cs144.keithw.org/hello 并观察结果。

  2. 现在,你将手动完成浏览器所做的事情。
    (a) 在你的虚拟机(或你自己的计算机——例如 macOS 上的终端程序)上,运行 telnet cs144.keithw.org http。这会告诉 telnet 程序在你的计算机和另一台计算机(名为 cs144.keithw.org)之间打开一个可靠的字节流,并与该计算机上运行的特定服务进行通信:即用于万维网的超文本传输协议(HTTP)的“http”服务。¹

    • ¹计算机的名称有一个数字等效项(104.196.238.229,一个 IPv4 地址),服务的名称也是如此(80,一个 TCP 端口号)。我们稍后会讨论这些。
      如果你的计算机设置正确并且已连接到互联网,你将会看到:
    1
    2
    3
    4
    user@computer:~$ telnet cs144.keithw.org http
    Trying 104.196.238.229...
    Connected to cs144.keithw.org.
    Escape character is '^]'.

    如果你需要退出,按住 ctrl 键并按 ] 键,然后键入 close 并按回车。
    (b) 键入 GET /hello HTTP/1.1 并按回车。这告诉服务器 URL 的路径部分。(从第三个斜杠开始的部分。)
    © 键入 Host: cs144.keithw.org 并按回车。这告诉服务器 URL 的主机部分。(http:// 和第三个斜杠之间的部分。)
    (d) 键入 Connection: close 并按回车。这告诉服务器你已完成请求,并且它应在完成回复后立即关闭连接。
    (e) 再按一次回车键:回车。这会发送一个空行,并告诉服务器你已完成 HTTP 请求。
    (f) 如果一切顺利,你将看到与浏览器看到的相同的响应,前面是 HTTP 头部,它们告诉浏览器如何解释响应。

  3. 作业: 现在你已经知道如何手动获取网页了,向我们展示一下你会怎么做!使用上述技术获取 URL http://cs144.keithw.org/lab0/sunetid ,将 sunetid 替换为你自己的主 SUNet ID。你将在 X-Your-Code-Is: 头部中收到一个秘密代码。保存你的 SUNet ID 和该代码,以包含在你的实验报告中。

解答

  1. 结果如下图所示。
  2. 结果如下图所示。

2.2 给自己发送一封电子邮件

现在你已经知道如何获取网页了,是时候发送一封电子邮件了,同样是使用可靠的字节流连接到另一台计算机上运行的服务。

  1. SSH 到 sunetid@cardinal.stanford.edu(以确保你在斯坦福的网络上),然后运行 telnet 148.163.153.234 smtp。² “smtp”服务指的是简单邮件传输协议(Simple Mail Transfer Protocol),用于发送电子邮件。如果一切顺利,你将会看到:

    1
    2
    3
    4
    5
    user@computer:~$ telnet 148.163.153.234 smtp
    Trying 148.163.153.234...
    Connected to 148.163.153.234.
    Escape character is '^]'.
    220 mx0b-00000d03.pphosted.com ESMTP mfa-m0214089
    • ²这些说明也可能在斯坦福网络之外有效,但我们不能保证。
  2. 第一步:向邮件服务器标识你的计算机。键入 HELO mycomputer.stanford.edu 并按回车。等待看到类似“ 250 Hello cardinal3.stanford.edu [171.67.24.75], pleased to meet you ”的信息。

  3. 下一步:谁在发送邮件?键入 MAIL FROM: sunetid@stanford.edu 并按回车。将 sunetid 替换为你的 SUNet ID。³ 如果一切顺利,你将看到“250 2.1.0 Sender ok”。

    • ³是的,可以提供一个虚假的“发件人”地址。电子邮件有点像邮政服务的真实邮件,因为回邮地址的准确性(主要)依赖于诚信系统。你可以在明信片上写任何你喜欢的回邮地址,电子邮件在很大程度上也是如此。请不要滥用这一点——说真的。工程知识伴随着责任!使用虚假“发件人”地址发送邮件是垃圾邮件发送者和犯罪分子常用的手段,这样他们就可以冒充他人。以此开玩笑,假装自己是 santaclaus@northpole.gov 是很有趣的,但请确保你没有欺骗任何收件人。而且:即使收件人知道这是个玩笑,也不要发送伪装成任何斯坦福员工的邮件(否则你可能会触发大学的 IT 安全警报)。
  4. 下一步:谁是收件人?首先,尝试给自己发送一封电子邮件。键入 RCPT TO: sunetid@stanford.edu 并按回车。将 sunetid 替换为你的 SUNet ID。如果一切顺利,你将看到“250 2.1.5 Recipient ok.”

  5. 现在是上传邮件本身的时候了。键入 DATA 并按回车,告诉服务器你准备开始。如果一切顺利,你将看到 “354 End data with .”。

  6. 现在你正在给自己输入一封电子邮件。首先,输入你将在邮件客户端中看到的邮件头。在邮件头的末尾留一个空行。

    1
    2
    3
    4
    5
    354 End data with <CR><LF>.<CR><LF>
    From: sunetid@stanford.edu (回车)
    To: sunetid@stanford.edu (回车)
    Subject: Hello from CS144 Lab 0! (回车)
    (回车)
  7. 键入邮件正文——任何你喜欢的内容。完成后,以单独一行的一个点结束:. 并按回车。预计会看到类似:“250 2.0.0 33h24dpdsr-1 Message accepted for delivery”。

  8. 键入 QUIT 并按回车,以结束与邮件服务器的对话。检查你的收件箱和垃圾邮件文件夹,确保你收到了邮件。

  9. 作业: 现在你已经知道如何手动给自己发送邮件了,尝试给朋友或实验伙伴发送一封,并确保他们收到。最后,向我们展示你可以给我们发送一封。使用上述技术,从你自己发送一封邮件到 cs144grader@gmail.com

2.3 监听与连接

题目

你已经看到了 telnet 的功能:一个客户端程序,它向在其他计算机上运行的程序发起出站连接。现在是时候体验一下作为简单服务器的角色了:那种等待客户端连接的程序。

  1. 在一个终端窗口中,在你的虚拟机上运行 netcat -v -l -p 9090。你应该看到:

    1
    2
    user@computer:~$ netcat -v -l -p 9090
    Listening on [0.0.0.0] (family 0, port 9090)
  2. netcat 保持运行。在另一个终端窗口中,运行 telnet localhost 9090 (也在你的虚拟机上)。

  3. 如果一切顺利,netcat 将会打印出类似“Connection from localhost 53500 received!”的信息。

  4. 现在尝试在任一终端窗口中输入——netcat(服务器)或 telnet(客户端)。注意,你在一个窗口中输入的任何内容都会出现在另一个窗口中,反之亦然。你需要按回车键才能传输字节。

  5. netcat 窗口中,通过键入 ctrl-C 退出程序。注意 telnet 程序也会立即退出。

解答

3 使用操作系统流套接字编写网络程序

在这个热身实验的下一部分,你将编写一个简短的程序,通过互联网获取网页。你将使用 Linux 内核以及大多数其他操作系统提供的一个特性:在两个程序之间创建可靠的双向字节流的能力,一个运行在你的计算机上,另一个运行在互联网上的不同计算机上(例如,像 Apache 或 nginx 这样的 Web 服务器,或者 netcat 程序)。

这个特性被称为流套接字。对于你的程序和 Web 服务器来说,套接字看起来像一个普通的文件描述符(类似于磁盘上的文件,或者 stdinstdout I/O 流)。当两个流套接字连接时,写入一个套接字的任何字节最终都会以相同的顺序从另一台计算机上的另一个套接字中出来。

然而,实际上,互联网并不提供可靠字节流的服务。相反,互联网真正做的唯一事情是尽其“最大努力”将称为互联网数据报的短数据片段传送到它们的目的地。每个数据报都包含一些元数据(头部),用于指定诸如源地址和目标地址(它来自哪台计算机,以及它要发送到哪台计算机)之类的信息,以及一些要传送到目标计算机的负载数据(最多约 1500 字节)。

尽管网络试图传递每个数据报,但在实践中,数据报可能会 (1) 丢失,(2) 乱序传递,(3) 内容被更改后传递,甚至 (4) 重复并多次传递。通常,连接两端的操作系统负责将“尽力而为的数据报”(互联网提供的抽象)转换为“可靠字节流”(应用程序通常需要的抽象)。

这两台计算机必须合作,以确保流中的每个字节最终都能以正确的顺序传递到另一端的流套接字。它们还必须互相告知对方准备接收多少数据,并确保发送的数据量不超过对方愿意接收的量。所有这些都是使用一个于 1981 年制定的、称为传输控制协议(TCP)的商定方案来完成的。

在这个实验中,你将简单地使用操作系统对传输控制协议的现有支持。你将编写一个名为“webget”的程序,它创建一个 TCP 流套接字,连接到一个 Web 服务器,并获取一个页面——就像你在这个实验前面所做的那样。在未来的实验中,你将实现这个抽象的另一面,通过自己实现传输控制协议,从不可靠的数据报中创建可靠的字节流。

3.1 让我们开始——在你的虚拟机和 GitHub 上设置仓库**

  1. 实验作业将使用一个名为“Minnow”的入门代码库。在你的虚拟机上,运行 git clone https://github.com/cs144/minnow 来获取实验的源代码。

  2. 通过键入 cd minnow 进入 minnow 目录。

  3. 在 Web 浏览器中,你将在自己的 GitHub 账户中创建一个仓库来存放你的实验作业解决方案。
    (a) 如果你还没有 GitHub 账户,请在 https://github.com 创建一个。
    (b) 导航到 https://github.com/new 创建一个新仓库。
    © 在你的 GitHub 账户中将仓库命名为“minnow”。
    (d) 确保将仓库设置为“私有(Private)”,这样你的解决方案就不会公开。
    (e) 点击“创建仓库(Create Repository)”。
    (f) 在下一个屏幕上,点击“邀请协作者(Invite collaborators)”,然后点击“添加人员(Add people)”。
    (g) 添加“cs144-grader”作为协作者(这将使我们能够看到并评分你的代码,同时保持其私有性)。

  4. 回到你的虚拟机,通过运行以下命令将 GitHub 仓库注册为一个目标:git remote add github https://github.com/username/minnow(将“username”替换为你的实际 GitHub 用户名)。这会在你的本地实验作业副本(在你的虚拟机上)和你在 GitHub 上的副本(你将用它来备份本地副本并进行评分)之间创建一个关联。

  5. 运行 git push github 将入门代码发送到你的 GitHub 仓库。如果一切顺利,你将看到几行文本打印出来,最后是:* [new branch] main -> main。如果你看到错误消息,请仔细检查你是否正确执行了上述步骤。此命令会将你的代码上传到你在 GitHub 上的私有仓库副本,并让我们对你的提交进行评分。

解答

GitHub 从 2021 年 8 月 13 日起不再支持使用密码进行 Git 操作的身份验证(通过 HTTPS)。你需要使用 Personal Access Token (PAT) 或者 SSH 密钥来代替密码。对于 HTTPS 链接,最直接的替代方法是使用 PAT。

  1. 在 GitHub 上生成一个 Personal Access Token (PAT):

    • 登录你的 GitHub 账户。
    • 点击右上角的你的头像,然后选择 “Settings”。
    • 在左侧边栏,滚动到底部,选择 “Developer settings”。
    • 在左侧边栏,选择 “Personal access tokens”,然后选择 “Tokens (classic)”。
    • 点击 “Generate new token”,然后选择 “Generate new token (classic)”。
    • 给你的 Token 起一个描述性的名字,例如 “VM Git Access”。
    • 设置 Expiration (过期时间):选择一个合适的过期时间(例如 30 天,90 天,或者自定义)。为了安全,不建议选择 “No expiration”。
    • 选择 Scopes (权限范围):对于推送代码到仓库,你需要勾选 repo 这个大项。这会授予对仓库的完全控制权(包括读写私有仓库)。
    • 点击页面底部的 “Generate token” 按钮。
    • 重要:你会看到一串字符,这就是你的 PAT。立即复制它并保存到一个安全的地方(比如密码管理器或者一个临时的安全文本文件)。一旦你离开这个页面,你就再也看不到这个 Token 了!
  2. 在你的虚拟机上使用 PAT 进行推送:

    • 回到你的虚拟机终端。
    • 当你再次运行 git push github main (或者其他需要认证的 git 命令) 时,它会提示你输入用户名和密码:
    1
    2
    Username for 'https://github.com': jysanne
    Password for 'https://jysanne@github.com':
    • 输入你的 GitHub 用户名。
    • 在提示输入密码 (Password) 的地方,粘贴你刚刚复制的 Personal Access Token (PAT)。按回车,你的代码应该就能成功推送了。
  3. 完成结果如下:

3.2 编译入门代码

  1. 仍在“minnow”目录中,创建一个目录来编译实验软件:cmake -S . -B build
  2. 编译源代码:cmake --build build
  3. 使用你喜欢的文本编辑器(许多学生喜欢通过 SSH 编辑文件的 VS Code,但你可以使用任何你想要的编辑器):打开并开始编辑 writeups/check0.md 文件。这是你的实验检查点报告模板,并将包含在你的提交中。

解答

本文使用 VS Code 的 “Remote - SSH” 扩展连接到虚拟机并编辑文件。

  1. 虚拟机上安装 SSH 服务端

    • 在虚拟机终端中检查:sudo systemctl status ssh
    • 如果未安装,在虚拟机终端中安装:sudo apt update && sudo apt install openssh-server
    • 启动 SSH 服务:sudo systemctl start ssh
    • 确保 SSH 服务开机自启:sudo systemctl enable ssh
  2. 获取 虚拟机的 IP 地址
    在虚拟机终端中输入 ip addr 。找到类似于 192.168.x.x10.x.x.x 的地址(避免 127.0.0.1,除非你做了特定的端口转发设置)。

    图中为192.168.87.128。

  3. 在主机 VS Code 中安装 “Remote - SSH” 扩展**

  4. 连接到你的虚拟机**

  5. 安装完扩展后,VS Code 的左下角会出现一个绿色的图标,通常显示为 >< 。点击这个图标。或者,你也可以按 F1 (或 Ctrl+Shift+P) 打开命令面板,然后输入 Remote-SSH: Connect to Host... 并选择它。

  6. 在弹出的选项中,选择 “+ Add New SSH Host…”

  7. 它会提示你输入 SSH 连接命令。按照以下格式输入:
    ssh your_vm_username@your_vm_ip_address
    例如:ssh jysanne@192.168.87.128

  8. 接下来,它会问你选择哪个 SSH 配置文件进行更新。通常选择第一个默认的即可。

  9. 右下角会弹出一个提示,说 “Host added!”。现在你可以再次点击左下角的绿色 >< 图标,或者再次打开命令面板 (F1) 输入 Remote-SSH: Connect to Host...。这次,你应该能在列表中看到你刚刚添加的主机(例如 jysanne@192.168.1.105),选择它。

  10. 一个新的 VS Code 窗口会打开,并开始连接到你的虚拟机。

    • 选择平台第一次连接时,它可能会问你远程操作系统的类型。选择 Linux
    • 输入密码:输入你在虚拟机上设置的密码,然后按回车。(注意:输入密码时屏幕上不会显示任何字符,这是正常的。)
  11. 如果一切顺利,VS Code 窗口左下角的绿色部分会显示 SSH: your_vm_ip_address,表示你已成功连接。

  12. 一旦连接成功,点击 VS Code 左上角的 “文件” (File)菜单,然后选择 “打开文件夹…” (Open Folder…)**。或者,在左侧的资源管理器视图中,点击 “打开文件夹” 按钮。

  13. 导航到你的 minnow 项目所在的目录。

3.3 现代 C++:基本安全但仍然快速和低级

CS144 实验作业采用现代 C++ 风格完成,使用2011 年及以后特性以尽可能安全地编程。有关此风格请参阅 C++核心指南( http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines )。

基本思想是确保每个对象都设计有尽可能小的公共接口,具有大量内部安全检查且难以被不当使用,并且知道如何自行清理。我们希望避免“配对”操作(例如 malloc/free,或 new/delete),在这些操作中,配对的后半部分可能不会发生(例如,如果函数提前返回或抛出异常)。取而代之的是,操作在对象的构造函数中发生,而相反的操作则在其析构函数中发生。这种风格被称为“资源获取即初始化”(Resource acquisition is initialization),或 RAII。

特别地,我们希望你:

  • 使用 https://en.cppreference.com 上的语言文档作为资源。(我们建议你避免使用 cplusplus.com,因为它更有可能过时。)
  • 绝不使用 malloc()free()
  • 绝不使用 newdelete
  • 基本上绝不使用原始指针(*),并且仅在必要时使用“智能”指针(unique_ptrshared_ptr)。(你在 CS144 中不需要使用这些。)
  • 避免使用模板、线程、锁和虚函数。(你在 CS144 中不需要使用这些。)
  • 避免使用 C 风格字符串(char *str)或字符串函数(strlen()strcpy())。这些非常容易出错。请改用 std::string
  • 绝不使用 C 风格转换(例如,(FILE *)x)。如果必须,请使用 C++ 的 static_cast(你通常在 CS144 中不需要这个)。
  • 优先通过 const 引用传递函数参数(例如:const Address & address)。
  • 除非需要修改,否则将每个变量声明为 const
  • 除非需要修改对象,否则将每个方法声明为 const
  • 避免使用全局变量,并为每个变量赋予尽可能小的作用域。
  • 在提交作业之前,运行 cmake --build build --target tidy 以获取有关如何改进与 C++ 编程实践相关的代码的建议,并运行 cmake --build build --target format 以一致地格式化代码。

关于使用 Git: 实验以 Git(版本控制)仓库的形式分发——这是一种记录更改、为调试提供版本检查点以及跟踪源代码来源的方法。请在你工作时进行频繁的小提交,并使用能够识别更改内容和原因的提交信息。 理想的状态是每次提交都应该能够编译,并且稳步地通过越来越多的测试。进行小的“语义化”提交有助于调试(如果每次提交都能编译并且消息清晰地描述了该提交所做的一件事,那么调试起来会容易得多),并通过记录你随时间的稳步进展来保护你免受作弊指控——这是一项有用的技能,将有助于任何包含软件开发的职业。评分员将阅读你的提交信息,以了解你如何开发解决方案。如果你还没有学习如何使用 Git,请在 CS144 的办公时间寻求帮助,或查阅教程(例如,https://guides.github.com/introduction/git-handbook )最后,虽然我们要求你通过使用 GitHub 上的私有仓库来备份和提交代码,但请确保你的代码不被公开访问。

3.4 阅读 Minnow 支持代码

为了支持这种编程风格,Minnow 的类将操作系统函数(可以从 C 调用)封装在“现代”C++ 中。我们为你提供了 C++ 封装器,用于封装我们希望你从 CS 111 中广泛熟悉的概念,尤其是套接字和文件描述符。

请阅读 util/socket.hhutil/file_descriptor.hh 文件中的公共接口(public:之后的部分)。(请注意,SocketFileDescriptor 的一种类型,而 TCPSocketSocket 的一种类型。)

3.5 编写 webget

现在是时候实现 webget 了,这是一个使用操作系统的 TCP 支持和流套接字抽象通过互联网获取网页的程序——就像你在这个实验前面手动操作时一样。

  1. build 目录中,用文本编辑器或 IDE 打开文件 ../apps/webget.cc

  2. get_URL 函数中,实现本文件中描述的简单 Web 客户端,使用你之前使用的 HTTP (Web) 请求格式。使用 TCPSocketAddress 类。

  3. 提示:

    • 请注意,在 HTTP 中,每行必须以“\r\n”结尾(仅使用“\n”或 endl 是不够的)。
    • 不要忘记在你的客户端请求中包含“Connection: close”行。这告诉服务器,在此请求之后,它不应等待你的客户端发送更多请求。相反,服务器将发送一个回复,然后立即结束其出站字节流(从服务器套接字到你的套接字的那个)。当你的套接字在读取完来自服务器的整个字节流后达到“EOF”(文件结束)时,你就会发现你的入站字节流已经结束。这就是你的客户端如何知道服务器已完成其回复的方式。
    • 确保读取并打印来自服务器的所有输出,直到套接字达到“EOF”(文件结束)——单次调用 read 是不够的。
    • 我们预计你大约需要编写十行代码。
  4. 通过运行 cmake --build build 编译你的程序。如果你看到错误消息,你需要先修复它才能继续。

  5. 通过运行 ./apps/webget cs144.keithw.org /hello 来测试你的程序。这与你在浏览器中访问 http://cs144.keithw.org/hello 时看到的内容相比如何?

与第 2.1 节的结果相比如何?请随意进行实验——用你喜欢的任何 http URL 来测试它!

  1. 当它看起来工作正常时,运行 cmake --build build --target check_webget 来运行自动化测试。在实现 get_URL 函数之前,你应该期望看到以下内容:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ cmake --build build --target check_webget
    Test project /home/cs144/minnow/build
    Start 1: compile with bug-checkers
    1/2 Test #1: compile with bug-checkers ........ Passed 1.02 sec
    Start 2: t_webget
    2/2 Test #2: t_webget ....................***Failed 0.01 sec
    Function called: get_URL(cs144.keithw.org, /nph-hasher/xyzzy)
    Warning: get_URL() has not been implemented yet.
    ERROR: webget returned output that did not match the test's expectations
    完成作业后,你将看到:
    1
    2
    3
    4
    5
    6
    7
    8
    $ cmake --build build --target check_webget
    Test project /home/cs144/minnow/build
    Start 1: compile with bug-checkers
    1/2 Test #1: compile with bug-checkers ........ Passed 1.09 sec
    Start 2: t_webget
    2/2 Test #2: t_webget .................... Passed 0.72 sec

    100% tests passed, 0 tests failed out of 2
  2. 评分员将使用与 make check_webget 运行的主机名和路径不同的主机名和路径来运行你的 webget 程序——因此请确保它不仅仅适用于单元测试中使用的主机名和路径。