译文
本文最后更新于 139 天前,其中的信息可能已经有所发展或是发生改变。

CS144-0

2.1 获取一个网页

  1. 打开你的虚拟机终端,输入 telnet cs144.keithw.org http 命令,然后按 Enter。这将开启一次到服务器 cs 144. Keithw. Org 上 HTTP 服务的连接。
  2. 一旦连接成功,键入 GET /path HTTP/1.1 并按 Enter。注意这里的/path 应替换为你想要获取的网页路径。
  3. 键入 Host: cs144.keithw.org,然后按 Enter。这给服务器提供你访问的网站主机名。
  4. 键入 Connection: close 后按 Enter。这是告诉服务器你的请求已完成,服务完成后可关闭连接。
  5. 最后,再按一次 Enter,发送一个空行,告诉服务器你的 HTTP 请求
  6. 此时服务器将发送一个 HTTP 响应,这应该与您在 Web 浏览器中看到的响应非常相似。

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

  1. 在终端打开连接到 SMTP 服务器的 telnet 会话: telnet 148.163.153.234 smtp
  2. 一旦连接成功,在命令行中输入 HELO mycomputer.stanford.edu 并按 Enter 键。
  3. 输入 MAIL FROM: yourid@stanford.edu,替换 yourid 为你的实际 ID。
  4. 输入 RCPT TO: yourid@stanford.edu,同上,替换 yourid 为你的实际 ID
  5. 输入 DATA,这将告诉 SMTP 服务器你现在要开始传输邮件内容。
  6. 输入你的邮件内容。当你完成后,只需在新的一行中输入 . 就可以结束你的消息。
  7. 执行 QUIT 断开与邮件服务器的连接。然后,检查你的电子邮件。你应该已经收到了你刚刚发送的邮件。

2.3 监听和连接

  1. 在你的虚拟机的一个终端窗口中,运行 netcat -v -l -p 9090。这将使你的虚拟机开始以 9090 端口监听连接。
  2. 在你的虚拟机的另一个终端窗口中,运行 telnet localhost 9090
  3. 当连接成功时,你可以在两个窗口中互相输入内容。一个窗口输入的内容可以在另一个窗口看到。
  4. 要终止连接,你可以在正在运行 netcat 的窗口中键入 ctrl -C。

希望这样的解释能帮到你。如果你还有疑问,不管是关于上述步骤还是需要更详细的解释,都可以告诉我。
在这个实验中,你将编写一个名为 “webget” 的程序,用于获取网页内容。程序会使用 Linux 和大多数其他操作系统提供的流套接字特性。流套接字为两个程序之间创建一个可靠的双向字节流。你需要利用这个特性,编写一个既在本地运行,又与互联网上的另一台计算机(例如,运行 Apache 或 nginx 的 Web 服务器)的程序进行交互。

这是如何工作的:你在一个套接字写入的字节,最终将按照同样的顺序从对方计算机的套接字中输出。但需要注意,互联网并没有提供一个本质上的可靠字节流服务。互联网实际上做的是尽力将一些短数据片段(叫做互联网数据报)送达给目标机器。每个数据报包含一些源地址和目标地址的元数据和有效数据。网络将尽力递交每个数据报,但这并不可靠,数据报可能会丢失、交付顺序错误、交付后内容被改变,甚至被复制然后多次交付。将不可靠的数据报转化为可靠字节流的任务,通常是两端的操作系统完成的。

两个计算机必须协作来确保字节流中的每个字节都能够被正确的顺序送达对方。他们还需要互相声明各自能接受对方多少量的数据,保证不会发送超过对方愿意接受的数量。所有的这些都是通过一个从 1981 年就已经确定的策略完成的,叫做传输控制协议,或者 TCP。

实验步骤:

  1. 在你的虚拟机上,获取实验的源代码: git clone https://github.com/cs144/minnow
  2. 进入 Lab 0 目录: cd minnow
  3. 创建一个目录来编译实验室软件: cmake -S . -B build
  4. 编译源代码: cmake --build build
  5. 在构建目录外部,打开并开始编辑 writeups/check 0. Md 文件。
  6. 在文件 ../apps/webget. Cc 中,找到并实现 get_URL 函数。你需要使用 HTTP 协议,使用 TCPSocket 和 Address 类。
  7. ![[Pasted image 20231116124548.png]]
    1. 通过运行 make 来编译程序。
  8. 通过运行 ./apps/webget cs144.keithw.org /hello 来测试你的程序。你可以实验不同的 http URL。
  9. ![[Pasted image 20231116124652.png]]
  10. 当程序正常运行后,你可以运行 cmake --build build --target check_webget 进行自动测试。
    ![[Pasted image 20231116124323.png]]

最后一步重要的注意事项是:你的程序需要能够与不仅仅是测试中使用的主机名和路径一起工作的各种主机名和路径一起工作,因为评分人员将尝试使用不同的主机名和路径来运行你的 webget 程序。

在本次实验中,你将在内存中实现一个可靠的字节流抽象。字节在”输入”端写入,在”输出”端以相同的顺序读出。每个流有上限,写入结束后不能再增加字节。字节流还用于流量控制,以免占用过多内存。字节流会限制写入的字节数以确保不会超过存储容量,只有当读取者读取并清除字节时,才可以写入更多字节。你不用担心并发问题,你的字节流是单线程的。

具体步骤如下:

  1. 打开 src/byte stream.hhsrc/byte stream.cc 文件,实现给定接口的对象。
  2. 写入者接口:
  • void push( std::string data ); // 将数据推入流中,但只推入可用容量的数据。
  • void close(); // 表示流已经结束,不会有更多数据写入。
  • void set_error(); // 表示流出现错误。
  • bool is_closed() const; // 判断流是否已关闭。
  • uint64_t available_capacity() const; // 当前可以推入流的字节数。
  • uint64_t bytes_pushed() const; // 已推入流的总字节数。
  1. 读取者接口:
  • void pop( uint64_t len ); // 从缓冲区移除 ‘len’ 字节。
  • bool is_finished() const; // 判断流是否已完成(关闭并完全弹出)。
  • bool has_error() const; // 判断流是否发生过错误。
  • uint64_t bytes_buffered() const; // 当前缓冲的字节数量(推入但尚未弹出的)。
  • uint64_t bytes_popped() const; // 已从流中弹出的总字节数。
  1. 使用 cmake --build build --target check0 命令运行自动化测试。

![[Pasted image 20231116214506.png]]

![[Pasted image 20231116214546.png]]
![[Pasted image 20231116214614.png]]
![[Pasted image 20231116214635.png]]
2.3 gbit/s
注意所有测试都通过后,check0 测试将对你的实现进行速度基准测试。为了满足课程目标,你的速度需要超过 0.1 Gbit/s。在优秀的硬件上,它可能超过 10 Gbit/s。

在未来四周,你将实现一个提供同样接口的系统,不限于内存,而是在不可靠的网络上。这就是传输控制协议,是世界上最普遍的计算机程序的实现之一。

以下是目前为止你所需要进行的提交步骤:

  1. 在你的提交中,只应修改 webget.cc 以及 src 顶层的源代码文件(byte stream.hhbyte stream.cc)。请不要对任何测试或 util 中的辅助程序进行修改。
  2. 遵循下列步骤准备提交: (a) 确保所有的改动都已经提交至你的 Git 仓库。你可以运行 git status 来确认没有未处理的改动。 (b) 运行 cmake --build build --target format 格式化代码,使其符合编码规范。 (c) 运行 cmake --build build --target check0 进行自动化测试,确保你的代码通过所有测试。 (d) 可选步骤:运行 cmake --build build --target tidy 根据提示进行代码改进,以遵循良好的 C++ 编程实践。
  3. 完成 writeups/check0.md 文件的编辑,记述此次作业花费的时间及你的其他备注。
  4. “如何提交”的具体步骤将在截止日期前公布。
  5. 如果遇到任何问题,例如在实验会议中或通过在 EdStem 上发布的问题,都请及时向课程工作人员通报。祝你好运,欢迎来到 CS 144!

记住在编程过程中持续地进行小步提交,你还可以时常运行 cmake --build build --target formatcmake --build build --target check0 以确保你的代码无误并符合规定的格式。有任何问题,随时通过课程平台询问课程工作人员。

cs144-1

这个观点认为,TCP 实现被视为全球最广泛使用的非琐碎计算机程序。实验课程将要求你以模块化的方式构建一个 TCP 实现。还记得你在 Checkpoint 0中刚刚实现的 ByteStream 吗?在接下来的实验中,你将需要通过网络进行传输两个 ByteStream:一个“出站”ByteStream,用于一个本地应用写入一个套接字,并且你的 TCP 将发送给对等方,以及一个“入站”ByteStream,用于从对等方传入的数据,这些数据将被本地应用读取。

1 开始

你的TCP实现将使用你在Checkpoint 0中所使用的相同的Minnow库,有额外的类和测试。 开始如下步骤:

  1. 确保你已经提交了你的Checkpoint 0的所有解决方案。请不要修改src目录以外的任何文件,或webget.cc。否则,你可能在合并Checkpoint 1的开始代码时遇到问题。
  2. 在实验分配的存储库中,运行 git fetch 来检索实验分配的最新版本。
  3. 下载Checkpoint 1的起始代码,运行 git merge origin/check1-startercode .
  4. 确保你的构建系统设置正确:cmake -S . -B build
  5. 编译源代码:cmake –build build
  6. 打开并开始编辑 writeups/check 1. Md 文件。这是你的实验报告的模板,将包含在你的提交中

在这个和下一个实验室中,你将实现一个 TCP 接收器:这个模块接收数据报并将其转变为一个可靠的字节流,以便应用程序从套接字中读取,就像你在检查点 0 中从 web 服务器读取字节流的 webget 程序一样。

TCP 发送器是将其字节流分割成短的段(每个子字符串不超过大约 1,460 字节)以使它们每个都能装进数据报中。但网络可能会重新排序这些数据报,或者丢弃它们,或者多次交付它们。接收器必须将这些段重新组装成他们开始的连续字节流。

在此实验室中,你将编写负责这种重组的数据结构:Reassembler。它会收到子字符串,由字节的字符串和该字符串在更大流中的第一个字节的索引组成。流的每一个字节都有自己的唯一索引,从零开始计数。

这就是接口的样子:

// 将新的子字符串插入到要重新组装的ByteStream中。
void insert( uint64_t first_index, std::string data,
bool is_last_substring, Writer& output );
// Reassembler本身存储了多少字节?
uint64_t bytes_pending() const;

你为什么要这样做?TCP 对重排序和重复的健壮性来自于它将字节流的任意摘录重新组装成原始流的能力。在一个单独的可测试的模块中实现这一点会让处理传入的段变得更容易。

Reassembler. Hh 头文件描述了 Reassembler 类的完整(公共)接口。你的任务是实现这个类。你可以向 Reassembler 类添加任何私有成员和成员函数,但你不能改变它的公共接口。

CS 144: 2023 年春季计算机网络导论课程

2.1 Reassembler 内部应该存储什么?

Insert 方法告知 Reassembler 关于 ByteStream 的新摘录,以及它在整个流中的位置(子字符串开始的索引)。

因此,原则上,Reassembler 将需要处理三类知识:

  1. 字节流中的下一个字节。一旦知道这些字节,Reassembler 应该立即将这些推送到 Writer。
  2. 字节符合流的可用容量,但是暂时无法写入,因为早期的字节还未知。这些应该在 Reassembler 内部存储。
  3. 超出流可用容量的字节。这些应被丢弃。Reassembler 不会存储任何不能立即推送到 ByteStream 的字节,或者一旦早期的字节已知就可以使用的字节。

这个行为的目标是,无论如何接收到子字符串,都要限制 Reassembler 和 ByteStream 的内存使用量。我们已经在下面的图片中说明了这一点。“容量”是两者的上限:

  1. 在重新组装的 ByteStream 中缓冲的字节数(显示为绿色),以及
  2. 可以由”未组装”子字符串使用的字节数(显示为红色)
    ![[Pasted image 20231117080843.png]]
    ![[Pasted image 20231117081109.png]]

在你实现 Reassembler 并进行测试时,你可能会发现这张图片很有用——什么是”正确”的行为并不总是自然而然的。

3 开发和调试建议

  1. 在编译之后,你可以使用 cmake –build build –target check 1 测试你的代码。
  2. 请重新阅读 Lab 0 文档中的“使用 Git”的部分,并记住要将代码保留在主分支的 Git 存储库中。做出小的提交,使用良好的提交信息来标识改变了什么以及为什么。
  3. 请努力使你的代码对将其用于风格和健全性评分的 CA 可读。合理且明确地为变量命名。用注释来解释复杂或微妙的代码片段。使用“防御性编程”-明确检查函数的前提条件或不变性,并在有任何错误时抛出异常。在你的设计中使用模块化 – 标识共同的抽象和行为,并在可能的情况下将它们分离出来。重复的代码块和庞大的函数会使你的代码难以阅读。
  4. 请也遵循 Checkpoint 0 文档中描述的“现代 C++”风格。Cppreference 网站( https://en.cppreference.com )是一个很好的资源,尽管你不需要 C++的任何复杂特性来完成这些实验。(有时你可能需要使用 move ()函数来传递一个不能复制的对象。)
  5. 如果你的构建卡住并且不确定如何修复它们,你可以擦除你的构建目录(rm -rf build – 请小心不要误写,否则会擦除你告诉系统的任何东西),然后再运行 cmake -S . -B build。

4 提交

  1. 在你的提交中,只在 src 目录中修改. Hh 和. Cc 文件。在这些文件中,请根据需要添加必要的私有成员,但请不要改变任何类的公共接口。
  2. 在交有任何作业之前,请按顺序运行:
    (a) 确实你已经将所有的更改提交到 Git 仓库。你可以运行 git status 确保没有未处理的更改。记住:在编码时进行小的提交。
    (b) cmake –build build –target format(规范编码风格)
    (c) cmake –build build –target check 0(确保自动测试全部通过)
    (d) 可选:cmake –build build –target tidy(建议按照良好的 C++编程实践进行改进)
  3. 在 writeups/check 1. Md 中写一份报告。这份文件应该是一份大约 20 到 50 行的文档,每行不超过 80 个字符,以便阅读。报告应包含以下部分:
    CS 144:2023 年春季计算机网络导论
    (a) 程序结构和设计。描述你的代码中的高级结构和设计选择。你不需要详细讨论你从初始代码中继承的内容。利用这个机会,突出重要的设计方面,并为批改的助教提供更大的详细信息。你强烈建议使用子标题和大纲使写作更加易读。请不要简单地将你的程序翻译成一段英文。
    (b) 实现挑战。描述你发现最 troubling 的代码部分,并解释为什么。反思一下你是如何克服这些挑战的,以及什么帮助你最终理解给你带来困扰的概念。你是如何试图确保你的代码保持你的假设,不变性,和前提条件的,你觉得这在哪些方面容易或困难?你是如何调试和测试你的代码的?
    (c) 剩余的错误。尽量详细地指出并解释代码中仍然存在的错误(或未处理的边缘情况)。
  4. 在你的写作中,还请填写完成作业需要的时间和任何其他评论。
  5. “如何交作业”的具体操作在截止日期前会被公布。
  6. 请尽快让课程工作人员在实验室环节,或者在 Ed 上发布问题知道任何问题。祝你好运。

cs144-2

实验检查点 2:TCP 接收器
截止日期:5 月 1 日,上午 11 点
10%的奖金:如果在 4 月 24 日星期一上午 11 点前提交。
协作政策:与检查点 0 相同。请不要查看其他学生的代码或过去版本的这些作业的解决方案。请在您的报告中完全披露任何合作者或任何灰色地带 — 披露是最佳政策。
0 概观
建议:在实现之前阅读整个实验文档。
在检查点 0 中,你实现了一个流控制字节流(ByteStream)的抽象。在检查点 1 中,你创建了一个重组器,它接受来自同一字节流的一系列子字符串,并将它们重组回原始流中。
这些模块在你的 TCP 实现中将被证明是有用的,但是它们中的没有一项具体到传输控制协议的详细信息。现在这一点将发生改变。在检查点 2 中,你将实现 TCPReceiver,这是 TCP 实现中处理传入字节流的部分。

TCPReceiver 从对等的发送者那里接收消息(通过 receive ()方法),并将它们转换为对重组器的调用,最终写入传入的 ByteStream。应用程序从这个 ByteStream 中读取,就像你在实验 0 中从 TCPSocket 中读取一样。

同时,TCPReceiver 也生成回送到对等发送者的消息,通过 send ()方法。这些”接收器消息”负责告诉发送者:

  1. “首个未组装”字节的索引,被称为”确认编号”或”ackno”。这是接收器需要从发送器那里获取的第一个字节。
  2. 输出 ByteStream 中的可用容量。这被称为”窗口大小”。

结合起来,ackno 和窗口大小描述了接收器的窗口:TCP 发送器允许发送的一系列索引。使用窗口,接收器可以控制传入数据的流量,让发送器限制它发送的数据量,直到接收器准备好接收更多数据。我们有时将 ackno 称为窗口的“左边缘”(TCPReceiver 感兴趣的最小索引),将 ackno + 窗口大小称为“右边缘”(刚好超过 TCPReceiver 感兴趣的最大索引)。

当你编写 Reassembler 和 ByteStream 时,你已经完成了实现 TCPReceiver 所涉及的大部分算法工作;这个实验是关于如何将这些通用类连接到 TCP 的详细信息。最困难的部分将涉及思考 TCP 将如何表示每个字节在流中的位置——这被称为“序列号”。

1 开始
你的 TCP 接收器的实现会使用你在检查点 0 和 1 中使用的同样的 Minnow 库,以及额外的类和测试。要开始:

  1. 确保你已经提交了所有的检查点 1 的解决方案。请不要修改 src 目录最顶层之外的任何文件,或 webget. Cc。否则,你可能在合并检查点 1 的启动代码时遇到麻烦。
  2. 在实验作业的存储库内部,运行 git fetch –all 以检索最新版本的实验作业。
  3. 通过运行 git merge origin/check 2-startercode 下载检查点 2 的启动代码。(如果你已经将“origin” remote 重命名为其他名称,你可能需要在这里使用不同的名称,例如 git merge upstream/check 2-startercode。)
  4. 确保你的构建系统设置正确:cmake -S . -B build
  5. 编译源码:cmake –build build
  6. 打开并开始编辑 writeups/check 2. Md 文件。这是你的实验报告的模板,并将包含在你的提交中。

2 检查点 2:TCP 接收器
TCP 是一个协议,可靠地在不可靠的数据报上传输一对流控制字节流(每个方向一个)。两个方,或者说“对等者”,参与 TCP 连接,并且每个对等方同时作为“发送者”(发送其自己的传出字节流)和“接收者”(接收传入字节流)。

这周,你将实现 TCP 的“接收者”部分,负责接收来自发送者的消息,重新组装字节流(包括它的结束,当那发生时),并确定应该发送回发送者用于确认和流控制的消息。

这里应该对应 ack,和 syn,seq

⋆为何我要这样做?这些信号对 TCP 在不可靠的数据报网络上提供流控制的,可靠的字节流服务的能力至关重要。在 TCP 中,确认指的是,“接收器需要下一个字节的索引是多少,以便它可以重新组装更多的 ByteStream?”这告诉发送者它需要发送或重新发送哪些字节。流控制意味着,“接收器对哪些索引的范围感兴趣并愿意接收。” (取决于其可用容量)。这告诉发送者它被允许发送多少。

2.1 在 64 位索引和 32 位 seqno 之间转换
作为热身,我们需要实现 TCP 的索引表示方式。上周你创建了一个重组器,它重新组装子字符串,每个独立的字节有一个 64 位的流索引,流中的第一个字节的索引总是零。一个 64 位的索引足够大以至于我们可以把它当作永不溢出。

2^64 是真的大

然而,在 TCP 头中,空间是宝贵的,并且流中的每个字节的索引不是用 64 位索引表示,而是用 32 位的“序列号”,或者“seqno”表示。这添加了三个复杂点:

  1. 你的实现需要计划 32 位整数的环绕。
    TCP 中的流可以任意长——TCP 可以发送的 ByteStream 的长度没有限制。但是 2^32 字节只有 4 GiB,这并不大。一旦一个 32 位序列号计数到 2^32 – 1,流中的下一个字节将有序列号零。
  2. TCP 序列号从一个随机值开始:为了避免对早先在同一端点之间的连接的旧段产生混乱,TCP 试图确保序列号不能被猜测并且不太可能重复。因此,一个流的序列号不会从零开始。流中的第一个序列号是一个被称为初始序列号(ISN)的随机 32 位数。这是表示“零点”或 SYN(流的开始)的序列号。其余的序列号在此后表现正常:数据的第一个字节将有 ISN+1(mod 2^32)的序列号,第二个字节将有 ISN+2(mod 2^32)的序列号,等等。
  3. 逻辑开始和结束每个占用一个序列号:除了确保收到所有的数据字节,TCP 还确保字节流的开始和结束可靠地被接收。因此,在 TCP 中,SYN(stream-beginning)和 FIN(stream-ending)控制标志被分配序列号。每个序列号占用一个序列号。(SYN 标志占用的序列号是 ISN。)流中的每个数据字节也占用一个序列号。记住,SYN 和 FIN 并不是流本身的一部分,它们不是“字节”——他们表示字节流本身的开始和结束。
    ![[Pasted image 20231120080924.png]]

这些序列号(seqnos)在每个 TCP 段的头部被传输。(并且,再次,有两个流 – 每个方向一个。每个流有独立的序列号和不同的随机 ISN。)有时讨论“绝对序列号”(始终从零开始并且不包裹)的概念,以及“流索引”(你已经在你的重组器中使用的:每个字节在流中的索引,从零开始)是有帮助的。

考虑仅包含三个字母字符串’cat’的字节流。如果 SYN 恰好有 seqno 2^32 – 2,那么每个字节的 seqnos,绝对 seqnos 和流索引是:
元素 syn c a t fin
Seqno 2^32 – 2 2^32 – 1 0 1 2
绝对序列号序列 0 1 2 3 4
流索引 0 1 2
![[Pasted image 20231119231423.png]]

2.2 实现 TCP 接收器
恭喜你正确实现了包裹和解包的逻辑!如果这次获胜发生在实验课上,我们会握你的手(或者,后 COVID 时代,用肘碰你)。在这个实验的其余部分,你将实现 TCPReceiver。它将(1)接收来自其对等发送者的消息,并使用重组器重新组装 ByteStream,以及(2)发送回对等发送者的包含确认号码(ackno)和窗口大小的消息。我们预计这会总计需要大约 15行代码。

2.2.1 receive () (接收)
每次从对等的发送者接收到一个新的段时,都会调用此方法。此方法需要:

  • 如果有必要,设置初始序列号。第一个带有 SYN 标志的段的序列号是初始序列号。你需要跟踪这个值,

3 开发和调试建议

  1. 在 tcp_receiver. Cc 文件中实现 TCPReceiver 的公共接口(以及你想要的任何私有方法或函数)。您可以在 tcp_receiver. Hh 中添加您喜欢的任何私有成员到 TCPReceiver 类中。
  2. 您可以使用 cmake –build build –target check 2 来测试你的代码.
  3. 请重新阅读 Lab 0 在“使用 Git”一节的内容,并记住将代码保留在它所在的主分支的 Git 库中. 做小的提交,使用好的提交信息,标识出什么改变了以及为何改变。
  4. 请使您的代码对将为其打风格分的 CA 易读。对变量使用合理且清晰的命名约定。使用注释来解释代码中复杂或微妙的部分。使用“防御性编程”——显式检查函数的前提条件或不变式,如果有任何错误,就抛出异常。在你的设计中使用模块化——识别常用的抽象和行为并在可能的时候提取它们。重复代码块和庞大的函数将使你的代码更难以跟踪。
  5. 同样,请遵守在 Checkpoint 0 文档中描述的“现代 C++”风格。Cppreference 网站 ( https://en.cppreference.com ) 是一个很好的资源,尽管你在做这些实验时不需要 C++的任何复杂功能。

4 提交

  1. 在您的提交中,请只改动 src 目录中的. Hh 和. Cc 文件。在这些文件中,请按需要自由添加私有成员,但请不要更改任何类的公共接口。
  2. 在提交任何作业前,请按顺序执行以下操作:
    (a) 确保您已经将所有更改提交到 Git 仓库。您可以运行 git status 确保没有未完成的更改。记住:在编码时做小的提交。
    (b) cmake –build build –target format (标准化编码风格)
    (c) cmake –build build –target check 0 (确保通过自动化测试)
    (d) 可选:cmake –build build –target tidy (提出改进建议,以遵循良好的 C++编程实践)
  3. 在 writeups/check 2. Md 中写一份报告。这个文件应该是一个大约 20 到 50 行的文档,每行不超过 80 个字符,以便阅读。报告应包含以下部分:
    CS 144: 计算机网络入门,2023 年春季
    (a) 程序结构和设计。描述你的代码中的高层结构和设计选择。你不需要详细讨论从初始代码中继承的部分。利用这个机会来强调重要的设计方面,在这些区域提供更多的细节,以便你的评分 TA 理解。你强烈建议使这个报告尽可能的易读,通过使用子标题和大纲。请不只是简单的把你的程序翻译成一个英文段落。
    (b) 实现挑战。描述您发现最为困扰的代码部分,并解释原因。反思你是如何克服这些挑战的,以及哪些帮你最终理解了给你带来困扰的概念。你试图如何确保你的代码维护了你的假设,不变量和前提条件,你发现这在什么方面容易或困难?你如何调试和测试你的代码?
    (c) 剩余的漏洞。尽可能地指出并解释代码中剩余的任何漏洞(或未处理的边界情况)。
  4. 在你的报告中,请也填写这个作业花费你的小时数和任何其他评论。
  5. 如果在实验课上出现任何问题,或者在 Ed 上发布问题,請尽快通知课程人员。祝你好运!

5 额外学分
对测试套件的改进将被授予额外学分。在 tests 目录下的一个文件(例如 minnow/tests/recv connect. Cc)中增加一个测试用例,该测试用例可以捕获到现有测试套件还未捕获的,人们可能会合理地制造的真实错误。

请在 EdStem 上发布你的测试(公开发布是可以的),以便我们可以查看,并决定是否将其添加到整体的测试套件中。(这个机会会一直保持开放——例如,如果你在第 7 周为 Reassembler 找到了一个好的附加测试,那也是很好的。)

cs144-3

实验检查点 3:TCP 发送方
截止日期:星期一,5 月 8 日,上午 11 点
10%奖励:如果在星期一,5 月 1 日,上午 11 点前提交
合作政策:与检查点 0 相同。请不要查看其他学生的代码或过去版本的解决方案。请在您的报告中全面披露任何合作者或任何灰色地带-披露是最好的政策。
0 概述
建议:在实施之前阅读整个实验文档。
在检查点 0 中,您实现了流控制字节流(ByteStream)的抽象。
在检查点 1 和 2 中,您实现了从不可靠数据报中传输的段到传入字节流的工具:Reassembler 和 TCPReceiver。
现在,在检查点 3 中,您将实现连接的另一侧。TCPSender 是一种工具,它将出站字节流转换为将成为不可靠数据报有效载荷的段。最后,在检查点 4 中,您将结合以前实验室的工作来创建一个工作的 TCP 实现:包含 TCPSender 和 TCPReceiver 的 TCPPeer。您将使用它与同学和互联网上的同行进行通信-真正使用 TCP 的服务器。

1 入门
您的 TCPSender 的实现将使用与检查点 0-2 中使用的 Minnow 库相同的库,其中包含其他类和测试。开始操作:

  1. 确保您已经提交了所有检查点 1 的解决方案。请不要修改顶级 src 目录以外的任何文件,或者 webget. Cc。否则,您可能无法合并检查点 1 的起始代码。
  2. 在实验室作业的存储库中运行 git fetch –all 以检索实验室作业的最新版本。
  3. 通过运行 git merge origin/check 3-startercode 下载检查点 2 的起始代码。(如果您已将“origin”远程重命名为其他名称,则可能需要在此处使用其他名称,例如 git merge upstream/check 3-startercode。)
  4. 确保您的构建系统已正确设置:cmake -S . -B build
  5. 编译源代码:cmake –build build
  6. 打开并开始编辑 writeups/check 3. Md 文件。这是您的实验报告模板,将包含在您的提交中。
    CS 144:计算机网络导论 2023 年春季
  7. 提醒:请在工作时频繁进行小的提交到本地 Git 存储库。如果您需要帮助确保您做得正确,请向同学或教学人员寻求帮助。您可以使用 git log 命令查看 Git 历史记录。

2 检查点 3:TCP 发送方
TCP 是一种协议,可通过不可靠的数据报可靠地传输一对流控制字节流(每个方向一个)。两个参与方参与 TCP 连接,每个参与方都是另一个的对等方。每个对等方同时充当“发送方”(发送其自己的传出字节流)和“接收方”(接收传入字节流)。

本周,您将实现 TCP 的“发送方”部分,负责从 ByteStream(由某个发送方应用程序创建和写入)读取,并将流转换为一系列传出 TCP 段。在远程端,TCP 接收器 1 将这些段(到达的那些段 – 它们可能不是全部都到达)转换回原始字节流,并将确认和窗口通告发送回发送方。

您的 TCPSender 的责任是:

  • 跟踪接收方的窗口(接收带有确认号和窗口大小的传入 TCPReceiverMessages)
  • 在可能的情况下填充窗口,通过从 ByteStream 读取、创建新的 TCP 段(包括必要的 SYN 和 FIN 标志)并发送它们。发送方应继续发送段,直到窗口已满或传出 ByteStream 没有更多数据可发送。
  • 跟踪已发送但尚未被接收方确认的段 – 我们称之为“未完成”的段
  • 如果自发送以来已经过了足够的时间,并且尚未得到确认,重新发送未完成的段
    ⋆我为什么要这样做?基本原则是发送接收方允许我们发送的任何内容(填充窗口),并继续重传,直到接收方确认每个段。这称为“自动重复请求”(ARQ)。发送方将字节流分割成段并发送它们,只要接收方的窗口允许。由于您上周的工作,我们知道远程 TCP 接收器只要至少收到每个带索引标签的字节一次,就可以重建字节流 – 无论顺序如何。发送方的任务是确保接收方至少收到每个字节一次。
    1
    重要的是要记住,接收方可以是任何有效 TCP 接收器的实现 – 它不一定是您自己的 TCPReceiver。关于互联网标准的有价值之处在于它们如何在端点之间建立共同语言,否则它们可能行为不同。
    CS 144:计算机网络导论 2023 年春季
    2.1 TCPSender 如何知道一个段丢失了?
    您的 TCPSender 将发送一堆 TCPSenderMessages。每个都包含一个(可能为空的)来自传出 ByteStream 的子字符串,用序列号标记其在流中的位置,并在流的开头和结尾处标记 SYN 标志和 FIN 标志。

除了发送这些段外,TCPSender 还需要跟踪其未完成的段,直到它们占据的序列号已被完全确认。周期性地,TCPSender 的所有者将调用 TCPSender 的 tick 方法,表示时间的流逝。 TCPSender 负责查看其未完成的 TCPSenderMessages 集合,并确定最早发送的段是否已经超过了没有确认的时间(即,所有序列号都没有得到确认)。
如果是这样,就需要重新传输(再次发送)。

下面是“等待时间过长”的规则。2 您将实现这个逻辑,它有点详细,但我们不希望您担心隐藏的测试用例试图让您陷入困境或将其视为 SAT 上的文字问题。我们将在本周为您提供一些合理的单元测试,并在实验室 4 中完成整个 TCP 实现后进行更全面的集成测试。只要您通过这些测试 100% 并且您的实现合理,您就会没问题。
⋆我为什么要这样做?总体目标是让发送方检测到何时段丢失并需要重新发送。在适当的时候等待重新发送的时间很重要:您不希望发送方等待太长时间重新发送一个段(因为这会延迟字节流向接收应用程序传输),但您也不希望它重新发送一个段,如果发送方只是等待一点时间,接收方就会确认该段 – 这会浪费互联网的宝贵容量。

  1. 每隔几毫秒,您的 TCPSender 的 tick 方法将被调用,并提供一个参数,告诉它自方法上次调用以来已经过去了多少毫秒。使用这个来维护 TCPSender 已经存在的总毫秒数。请不要尝试从操作系统或 CPU 调用任何“时间”或“时钟”功能 – tick 方法是您唯一可以访问时间流逝的方式。这使得事情确定并可测试。
  2. 当 TCPSender 构造时,它会得到一个参数,告诉它重传超时(RTO)的“初始值”。 RTO 是在重新发送未完成的 TCP 段之前等待的毫秒数。 RTO 的值会随着时间的推移而改变,但“初始值”保持不变。启动代码将 RTO 的“初始值”保存在名为 initial retransmission timeout 的成员变量中。
    2 这些基于 TCP 的“真实”规则的简化版本:RFC 6298,建议 5.1 至 5.6。这里的版本有点简化,但您的 TCP 实现仍然可以与互联网上的真实服务器进行通信。
    CS 144:计算机网络导论 2023 年春季
  3. 您将实现重传计时器:可以在某个时间启动的警报,一旦过了 RTO,警报就会响起(或“过期”)。我们强调,这种时间流逝的概念来自于调用 tick 方法 – 而不是通过获取实际的时间。
  4. 每当发送一个包含数据的段(在序列空间中的非零长度)时(无论是第一次还是重传),如果计时器没有运行,请启动它以便在 RTO 毫秒后过期(对于当前 RTO 的值)。
    所谓的“过期”,我们的意思是在将来的某个毫秒数后时间将用完。
  5. 当所有未完成的数据都得到确认时,停止重传计时器。
  6. 如果调用 tick 并且重传计时器已过期:
    (a) 重新传输尚未完全确认的最早(序列号最低)段。您需要在某个内部数据结构中存储未完成的段以便执行此操作。
    (b) 如果窗口大小不为零:
    i. 跟踪连续重传的次数,并增加它,因为您刚刚重新传输了一些内容。您的 TCPConnection 将使用此信息来判断连接是否无望(连续重传次数太多)并需要中止。
    Ii. 将 RTO 的值加倍。这称为“指数回退” – 它减缓了糟糕网络上的重传以避免进一步堵塞。
    (c) 重置重传计时器并启动它,使其在 RTO 毫秒后过期(考虑到您可能刚刚将 RTO 的值加倍!)。
  7. 当接收方给发送方一个确认号,确认成功接收了新数据(确认号反映了比任何先前确认号更大的绝对序列号):
    (a) 将 RTO 设置回其“初始值”。
    (b) 如果发送方有任何未完成的数据,重新启动重传计时器,使其在 RTO 毫秒后过期(对于当前 RTO 的值)。
    (c) 将“连续重传”计数重置为零。
    您可以选择在单独的类中实现重传计时器的功能,但这取决于您。如果您这样做,请将其添加到现有文件(tcp sender. Hh 和 tcp sender. Cc)。

CS 144:计算机网络导论 2023 年春季
2.2 实现 TCP 发送方
好的!我们已经讨论了 TCP 发送方的基本概念(给定一个传出 ByteStream,将其拆分为段,将它们发送给接收方,如果它们没有得到足够快的确认,继续重新发送它们)。我们还讨论了何时得出一个未完成的段丢失并需要重新发送的结论。

现在是时候讨论您的 TCPSender 将提供的具体接口了。它需要处理五个重要事件:

  1. Void push ( Reader& outbound stream );
    要求 TCPSender 从传出字节流填充窗口:从流中读取并生成尽可能多的 TCPSenderMessages,只要有新字节可读且窗口中有可用空间。

您需要确保发送的每个 TCPSenderMessage 都完全位于接收方的窗口内。使每个单独的消息尽可能大,但不要大于 TCPConfig:: MAX PAYLOAD SIZE(1452 字节)给出的值。

您可以使用 TCPSenderMessage:: sequence length () 方法计算一个段所占用的序列号总数。请记住,SYN 和 FIN 标志也占用一个序列号,这意味着它们占用窗口中的空间。
⋆如果窗口大小为零,我应该怎么做?如果接收方已通告零窗口大小,则 push 方法应假装窗口大小为 1。发送方可能会发送一个被拒绝(未确认)的单字节,但这也可能促使接收方发送一个新的确认段,其中揭示了更多空间已经打开。
如果没有这个,发送方将永远不知道它被允许开始发送。
这是您的实现应具有的唯一特殊情况。 TCPSender 不应实际记住窗口大小为 1 的错误值。特殊情况仅在 push 方法中。另外,需要注意的是,即使窗口大小为 1,窗口仍然可能已满。

  1. Std::optional maybe send ();
    这是 TCPSender 实际发送 TCPSenderMessage(如果需要)的机会。
  2. Void receive ( const TCPReceiverMessage& msg );
    从接收方接收一条消息,传达窗口的新左(= ackno)和右(= ackno +窗口大小)边缘。 TCPSender 应查看其未完成段的集合,并删除任何已被完全确认的段(确认号大于段中的所有序列号)。
    CS 144:计算机网络导论 2023 年春季
  3. Void tick ( const size t ms since last tick ):
    时间已过 – 自上次调用此方法以来的毫秒数。发送方可能需要重新传输一个未完成的段。
  4. Void send empty message ():TCPSender 应生成并发送一个零长度的消息,并正确设置序列号。这在对等方希望发送 TCPReceiverMessage(例如,因为它需要确认来自对等方的发送方的某些内容)并需要生成一个 TCPSenderMessage 以配合它时很有用。
    注意:像这样的段,它们在序列号中占用的长度为零,不需要被视为“未完成”并且不会被重新传输。

要完成检查点 3,请查看 src/tcp sender. Hh 中的完整接口,然后在 tcp sender. Hh 和 tcp sender. Cc 文件中实现完整的 TCPSender 公共接口。我们希望您添加私有方法和成员变量,可能还有一个辅助类。
2.3 常见问题和特殊情况

  • 我如何“发送”一条消息?
    在 maybe send () 被调用时返回它。
  • 等等,我既要“发送”一个段,又要跟踪该段作为未完成的段,以便我知道稍后要重新传输什么?我不是必须复制每个段吗?这是浪费吗?
    当您发送包含数据的段时,您可能需要在内部数据结构中保留一份副本,以便跟踪可能的重传的未完成段。事实证明,这并不浪费,因为 TCPSenderMessage 的有效负载存储为引用计数的只读字符串(一个 Buffer 对象)。所以别担心 – 它实际上并没有复制有效负载数据。
  • 在 receive 方法通知之前,我的 TCPSender 应该假定接收方的窗口大小是多少?
    一个。
  • 如果确认只部分确认了一些未完成的段,我应该尝试剪掉已确认的字节吗?
    TCP 发送方可以这样做,但是为了这个课程,没有必要变得花哨。将每个段视为未完成,直到它被完全确认 – 所有它占用的序列号都小于确认号。
  • 如果我发送三个包含“a”、“b”和“c”的单独段,它们从未得到确认,我可以稍后在一个包含“abc”的大段中重新传输它们吗?还是我必须单独重新传输每个段?
    再次:TCP 发送方可以这样做,但是为了这个课程,没有必要变得花哨。只需跟踪每个未完成的段,当重传计时器过期时,再次发送最早的未完成段。
  • 我应该将空段存储在我的“未完成”数据结构中并在需要时重新传输它们吗?
    不 – 应该跟踪的唯一未完成的段是那些传输某些数据的段 – 即占用某些长度的序列空间。占用零序列号的段(无有效负载、SYN 或 FIN)不需要被记住或重新传输。
  • 如果在此 PDF 发布后还有更多常见问题解答,我可以在哪里阅读?
    请定期查看网站( https://cs144.github.io/lab faq. Html)和 Ed。

以下是对您提供的文本的翻译:

3 开发和调试建议

  1. 在文件 tcp_sender. Cc 中实现 TCPSender 的公共接口(以及您想要的任何私有方法或函数)。您可以在 tcp_sender. Hh 中的 TCPSender 类中添加任何私有成员。
  2. 您可以使用 cmake –build build –target check 3 来测试您的代码。
  3. 请重新阅读“使用 Git”部分,在 Checkpoint 0 文档中,并记住将代码保留在主分支上分发的 Git 仓库中。进行小的提交,使用好的提交信息来标识更改了什么以及为什么更改。
  4. 请努力使您的代码对将为其评分风格的 CA(课程助理)来说易于阅读。对变量使用合理且清晰的命名约定。使用注释来解释复杂或微妙的代码部分。使用“防御性编程”——明确检查函数的前置条件或不变量,并在任何错误时抛出异常。在设计中使用模块化——识别常见的抽象和行为,并在可能时将它们分解出来。重复的代码块和巨大的函数将使跟踪您的代码变得困难。

4 提交

  1. 在您的提交中,请仅更改 src 目录中的 .hh 和 .cc 文件。在这些文件中,您可以根据需要添加私有成员,但请不要更改任何类的公共接口。
  2. 在提交任何作业之前,请按顺序执行以下操作:
    (a) 确保您已将所有更改提交到 Git 仓库。您可以运行 git status 来确保没有未完成的更改。记住:在编码时进行小的提交。
    (b) cmake –build build –target format (规范编码风格)
    (c) cmake –build build –target check 3 (确保自动化测试通过)
    (d) 可选:cmake –build build –target tidy (建议遵循良好的 C++ 编程实践)
  3. 在 writeups/check 3. Md 中写一份报告。这个文件应该是大约 20 到 50 行的文档,每行不超过 80 个字符,以便更容易阅读。报告应包含以下部分:
    (a) 程序结构与设计。描述在您的代码中体现的高层结构和设计选择。您不需要详细讨论您从起始代码中继承的内容。使用这个机会来突出重要的设计方面,并为您的评分助教提供更多细节,以便他们理解。强烈建议您通过使用小标题和大纲使这篇写作尽可能易于阅读。请不要简单地将您的程序翻译成一段英文。
    (b) 实现挑战。描述您发现最棘手的代码部分,并解释为什么。反思您是如何克服这些挑战的,以及最终是什么帮助您理解了给您带来麻烦的概念。您是如何尝试确保您的代码维护了您的假设、不变量和前置条件的,以及在哪些方面您发现这容易或困难?您是如何调试和测试您的代码的?
    (c) 剩余的错误。尽可能最好地指出并解释代码中仍然存在的任何错误(或未处理的边缘情况)。
  4. 请填写作业花费的小时数和任何其他评论。
  5. 如果在实验室课程中或通过在 Ed 上发布问题遇到任何问题,请尽快让课程工作人员知道。祝您好运!

5 额外学分
对测试套件的改进将获得额外学分。在 tests 目录中的一个文件中添加一个测试用例(例如 minnow/tests/recv_connect. Cc),该测试用例能够捕获到现有测试套件尚未捕获的一个真实的、人们可能合理犯的错误。请在 EdStem 上发布您的测试(公开发布也没关系),这样我们可以看一下,并决定是否将其添加到整体测试套件中。(这个机会将保持开放——例如,如果您在第 7 周为 Reassembler 找到了一个好的附加测试,那也很好)

cs144-4

实验室检查点 4:向下深入堆栈(网络接口)
截止日期:5月29日,上午11点
合作政策:与检查点 0 相同。请不要查看其他学生的代码或以前这些作业的解决方案。请在你的报告中充分披露任何合作者或任何不确定的地方——披露是最好的政策。

0 概览
在本周的实验室中,你将深入堆栈并实现一个网络接口:这是连接遍布全球的互联网数据报和在一个跳点上行进的链路层以太网帧之间的桥梁。这个组件可以“置于”你之前实验室中的 TCP/IP 实现之下,但它也将在不同的环境中使用:当你在实验室 6 中构建路由器时,它将在网络接口之间路由数据报。图 1 显示了网络接口如何适应这两种设置。

在过去的实验室中,你编写了一个 TCP 实现,可以与任何使用 TCP 的其他计算机交换 TCP 段。这些段实际上是如何传递到对等方的 TCP 实现的呢?正如我们讨论过的,有几个选项:

  • TCP-in-UDP-in-IP。TCP 段可以在用户数据报的有效负载中传输。在正常的(用户空间)设置中工作时,这是最容易实现的:Linux 提供了一个接口(一个“数据报套接字”,UDPSocket),它允许应用程序仅提供用户数据报的有效负载和目标地址,而内核则负责构建 UDP 头、IP 头和以太网头,然后发送数据包到适当的下一跳。内核确保每个套接字具有独占的本地和远程地址以及端口号的组合,而且由于内核是将这些写入 UDP 和 IP 头的,它可以保证不同应用程序之间的隔离。
  • TCP-in-IP。在常见用法中,TCP 段几乎总是直接放在互联网数据报内部,不需要在 IP 和 TCP 头之间的 UDP 头。这就是人们所说的“TCP/IP”。这有点难以实现。Linux 提供了一个接口,称为 TUN 设备,它允许应用程序提供一个完整的互联网数据报,而内核负责其余部分(编写以太网头,实际通过物理以太网卡发送等)。但现在应用程序必须自己构造完整的 IP 头,而不仅仅是有效负载。
  • TCP-in-IP-in-Ethernet。在上述方法中,我们仍然依赖于 Linux 内核来处理网络堆栈的一部分。每次你的代码将 IP 数据报写入 TUN 设备时,Linux 都必须构造一个适当的链路层(以太网)帧,并将 IP 数据报作为其有效负载。这意味着 Linux 必须弄清楚下一跳的以太网目的地址,给定下一跳的 IP 地址。如果它不知道这个映射,Linux 会广播一个查询,询问“谁声明了以下 IP 地址?你的以太网地址是什么?”然后等待响应。

这些功能由网络接口执行:一个组件,它将出站 IP 数据报转换为链路层(例如,以太网)帧,反之亦然。(在真实系统中,网络接口通常有类似 eth0、eth1、wlan0 等名称。)在本周的实验室中,你将实现一个网络接口,并将其放在你的 TCP/IP 堆栈的最底层。你的代码将产生原始以太网帧,这些帧将通过一个称为 TAP 设备的接口交给 Linux —— 类似于 TUN 设备,但更低级别,因为它交换原始链路层帧而不是 IP 数据报。

大部分工作将在查找(和缓存)每个下一跳 IP 地址的以太网地址中进行。这个协议称为地址解析协议,或 ARP。

我们为你提供了单元测试,这些测试将对你的网络接口进行全面测试。在实验室 6 中,你将在 TCP 之外的上下文中使用相同的网络接口,作为 IP 路由器的一部分。1 开始

  1. 确保你已经提交了对检查点 1 的所有解决方案。请不要修改 src 目录顶层之外的任何文件,或 webget.cc。否则,你可能在合并检查点 4 的起始代码时遇到问题。
  2. 当你在实验室作业的仓库中时,运行 git fetch –all 来检索最新版本的实验室作业。
  3. 通过运行 git merge origin/check4-startercode 下载检查点 4 的起始代码。(如果你已经将“origin”远程重命名为其他名称,你可能需要使用不同的名称,例如 git merge upstream/check4-startercode。)
  4. 确保你的构建系统设置正确:cmake -S . -B build
  5. 编译源代码:cmake –build build
  6. 打开并开始编辑 writeups/check4.md 文件。这是你实验报告的模板,将包含在你的提交中。
  7. 提醒:在工作时,请在你的本地 Git 仓库中频繁地进行小的提交。如果你需要帮助以确保你正确地做到了这一点,请向同学或教学人员寻求帮助。你可以使用 git log 命令查看你的 Git 历史。

2 检查点 4:地址解析协议
你在这个实验室的主要任务将是实现 NetworkInterface 的三个主要方法(在 network_interface.cc 文件中),维护从 IP 地址到以太网地址的映射。映射是一个缓存,或者说是“软状态”:NetworkInterface 保留它是为了效率,但如果它必须从头开始,映射将自然重新生成,而不会造成问题。

  1. void NetworkInterface::send_datagram(const InternetDatagram &dgram, const Address &next_hop);
    当调用者(例如,你的 TCPConnection 或路由器)想要发送一个出站的互联网(IP)数据报到下一跳时,将调用此方法。
    这是你的接口的工作,将这个数据报转换为以太网帧并(最终)发送它。
  • 如果目的地以太网地址已知,立即发送。创建一个以太网帧(类型为 EthernetHeader::TYPE_IPv4),将有效载荷设置为序列化的数据报,并设置源地址和目的地址。
  • 如果目的地以太网地址未知,广播一个 ARP 请求以获取下一跳的以太网地址,并将 IP 数据报排队,以便在收到 ARP 回复后发送。
    例外:你不想用 ARP 请求淹没网络。如果网络接口在过去五秒钟内已经发送了关于相同 IP 地址的 ARP 请求,请不要发送第二个请求——只需等待第一个请求的回复。
    同样,直到你了解目的地以太网地址前,将数据报排队。
  1. `optional< InternetDatagram > NetworkInterface::recv_frame(const EthernetFrame &frame);
    当一个以太网帧从网络到达时,将调用此方法。代码应该忽略任何不是发往网络接口的帧(意味着,以太网目的地要么是广播地址,要么是存储在 ethernet_address 成员变量中的接口自己的以太网地址)。
  • 如果入站帧是 IPv4,将有效载荷解析为 InternetDatagram,并且如果成功(意味着 parse() 方法返回 true),则将结果的 InternetDatagram 返回给调用者。
  • 如果入站帧是 ARP,将有效载荷解析为 ARPMessage,并且如果成功,记住发件人的 IP 地址和以太网地址之间的映射 30 秒。(从请求和回复中学习映射。)此外,如果它是一个 ARP 请求,询问我们的 IP 地址,发送一个适当的 ARP 回复。
  1. std::optional<EthernetFrame> maybe_send();
    这是 NetworkInterface 实际发送 EthernetFrame(如果它想要)的机会。
  2. void NetworkInterface::tick(const size_t ms_since_last_tick);
    随着时间的推移,将调用此方法。到期的 IP 到以太网的映射将过期。
    请不要将数据报的最终目的地(在数据报自己的头部作为目的地地址)与下一跳混淆。在这个实验室中,你只需要关心下一跳的地址。

你可以通过运行 cmake – – build build – – target check4 来测试你的实现。
这个测试不依赖于你的 TCP 实现。3 问答

  • 你们预计会有多少代码?
    总的来说,我们预计实现(在 network_interface.cc 中)将总共需要大约 100-150 行代码。
  • 我该如何“发送”一个以太网帧?
    当 maybe_send() 被调用时返回它。
  • 我应该使用什么数据结构来记录下一跳 IP 地址和以太网地址之间的映射?
    由你决定!
  • 我如何将以 Address 对象形式传入的 IP 地址转换为我可以写入 ARP 消息的原始 32 位整数?
    使用 Address : : ipv4_numeric() 方法。
  • 如果 NetworkInterface 发送了 ARP 请求但从未收到回复怎么办?我应该在某个超时后重新发送吗?使用 ICMP 向原始发送者发出错误信号?
    在现实生活中,是的,这两种情况都会发生,但在这个实验室中不用担心。在现实生活中,如果接口无法得到其 ARP 请求的回复,它最终会通过互联网将 ICMP “主机不可达”发送回原始发送者。
  • 如果一个 InternetDatagram 在队列中等待学习下一跳的以太网地址,但这个信息永远不会来,我应该在某个超时后丢弃数据报吗?
    同样,在现实生活中答案肯定是“是”,但在这个实验室中不用担心。
  • parse() 和 serialize() 是如何工作的?
    parse() 接收一个 T & obj 和 vector< Buffer >& buffers。成功时,它会用结果填充 obj 并返回 true。否则,它返回 false。
    serialize() 接收一个 T & obj 并将结果作为 vector<Buffer>返回。
  • 如果这个 PDF 出来后我在哪里可以读到更多的常见问题?
    请定期检查网站(https://cs144.github.io/lab_faq.html)和 EdStem。

4 开发和调试建议

  1. network_interface.cc 文件中实现 NetworkInterface 的公共接口(以及你想要的任何私有方法或函数)。你可以根据需要向 network_interface.hh 中的 NetworkInterface 类添加任何私有成员。
  2. 你可以使用 cmake --build build --target check4 来测试你的代码。
  3. 请重新阅读检查点 0 文档中的“使用 Git”部分,并记住在主分支上保持代码在分发的 Git 仓库中。使用好的提交信息进行小的提交,以标识发生了什么变化以及为什么变化。
  4. 请努力使你的代码易于阅读,以便给你打分的 CA 理解。对变量使用合理清晰的命名约定。使用注释来解释复杂或微妙的代码部分。使用“防御性编程”——明确检查函数的前提条件或不变量,并在任何东西出错时抛出异常。在设计中使用模块化——确定常见的抽象和行为,并在可能时分解它们。重复的代码块和巨大的函数会使跟随你的代码变得困难。

5 提交

  1. 在你的提交中,请只对 src 目录中的 .hh 和 .cc 文件进行更改。在这些文件中,你可以根据需要添加私有成员,但请不要更改任何类的公共接口。
  2. 在提交任何作业之前,请按顺序执行以下操作:
    (a) 确保你已经将所有更改提交到 Git 仓库。你可以运行 git status 来确保没有未解决的更改。记住:在编码时进行小的提交。
    (b) cmake --build build --target format(规范编码风格)
    (c)cmake --build build --target check4(确保自动化测试通过)
    (d) 可选:cmake --build build --target tidy(建议改进以遵循良好的 C++ 编程实践)
  3. 在 writeups/check4.md 中写一份报告。这个文件应该是一个大约 20 到 50 行的文档,每行不超过 80 个字符,以便更容易阅读。报告应该包含以下部分:
    (a) 程序结构和设计。描述你的代码体现的高层结构和设计选择。你不需要详细讨论你从起始代码中继承的内容。利用这个机会突出重要的设计方面,并为你的评分助教提供更多细节以便理解。强烈建议你通过使用小标题和提纲使这篇写作尽可能易读。请不要简单地将你的程序翻译成一段英文。
    (b) 实现挑战。描述你发现最麻烦的代码部分,并解释为什么。反思你是如何克服这些挑战的,以及最终帮助你理解给你带来麻烦的概念的是什么。你是如何尝试确保你的代码维护你的假设、不变量和前提条件的,以及在哪些方面你发现这很容易或困难?你是如何调试和测试你的代码的?
    (c) 剩余的错误。尽可能指出并解释代码中仍然存在的任何错误(或未处理的边缘情况)。
  4. 请填写作业花费的小时数和任何其他评论。
  5. 如果在实验室会话中或通过在 Ed 上发布问题遇到任何问题,请尽快通知课程工作人员。祝你好运!

cs144-5

————————————————————————————–

0 协作政策协作政策:与检查点0相同。请不要查看其他学生的代码或以前版本的解决方案。在你的报告中,请完全披露任何合作者或任何灰色地带——公开披露是最好的政策。

1 概述 在本周的实验检查点中,您将在现有的NetworkInterface之上实现一个IP路由器。路由器有几个网络接口,并且可以在任何一个接口上接收互联网数据报。路由器的工作是根据路由表转发它获得的数据报:一个规则列表,告诉路由器对于任何给定的数据报,

  • 要发送到哪个接口
  • 下一跳的IP地址 您的任务是实现一个路由器,可以为任何给定的数据报找出这两件事。 (您不需要实现创建路由表的算法,例如RIP、OSPF、BGP或SDN控制器——只需实现遵循路由表的算法。) 您对路由器的实现将使用Minnow库和一个新的Router类,以及测试,这些测试将在模拟网络中检查您的路由器功能是否正常。检查点5构建在您在检查点4中实现的NetworkInterface之上,但不使用您之前实现的TCP堆栈。IP路由器无需了解TCP、ARP或以太网(仅限IP)。我们预计您的实现大约需要30-60行代码。(scripts/lines-of-code工具从起始代码打印“Router: 61 lines of code”,对于我们的示例解决方案则是“109 lines of code”。)

2 开始

  1. 确保您已经提交了检查点4的所有解决方案。请不要修改src目录顶层外的任何文件,或webget.cc。否则,您可能在合并检查点5起始代码时遇到问题。
  2. 在实验室作业的存储库中运行 git fetch –all 以获取最新版本的实验室作业。
  3. ![[Pasted image 20231201213459.png]]
  4. 通过运行git merge origin/check5-startercode来下载检查点5的起始代码。 (如果您已将“origin”远程重命名为其他内容,则可能需要使用不同的名称,例如git merge upstream/check5-startercode。)
  5. 确保您的构建系统设置正确:cmake -S . -B build
  6. 编译源代码:cmake –build build
  7. 打开并开始编辑writeups/check5.md文件。这是您实验报告的模板,并将包含在您的提交中。
  8. 提醒:在工作时,请频繁进行小的提交到您的本地Git仓库。如果您需要帮助以确保您正在正确执行此操作,请向同学或教学人员寻求帮助。您可以使用git log命令查看您的Git历史记录。

3 实现路由器 在这个实验室中,你将实现一个Router类,该类可以:

  • 跟踪路由表(转发规则列表或路由),
  • 将其收到的每个数据报转发:
    • 到正确的下一跳
    • 在正确的传出NetworkInterface上。

您的实现将添加到router.hh和router.cc框架文件中。在开始编码之前,请回顾router.hh中新Router类的文档。

以下是您将实现的两种方法,以及我们在每种方法中的期望:

void add_route(uint32_t route_prefix,uint8_t prefix_length,optional<Address> next_hop, size_t interface_num);

此方法向路由表中添加一条路由。你需要在 Router 类中添加一个私有成员的数据结构来存储这些信息。这个方法所要做的就是保存路由以供后续使用。

路由部件含义是什么?

路由是一个“匹配-动作”规则:它告诉路由器,如果一个数据报是发往特定网络(一系列 IP 地址),并且如果该路由被选为最具体的匹配路由,则路由器应将数据报转发到特定的下一跳接口上。

也就是找一条最优路径

“匹配”是指:数据报是否发往这个网络?路由前缀和前缀长度共同指定了一系列可能包括数据报目的地的 IP 地址(一个网络)。路由前缀是一个32位数值 IP 地址。前缀长度是一个介于0到32(包括)之间的数字;它告诉路由器前缀中有多少个最重要的比特位是有效的。例如,要表示到网络“18.47.0.0/16”的路由(这匹配任何32位 IP 地址,其中第一和第二字节分别为18和47),路由前缀将是305070080(18 × 2^24 + 47 × 2^16),前缀长度将是16。任何目的地为“18.47.x.y”的数据报都会匹配。[[什么意思-lab5-1]]

“动作”是指:如果路由匹配且被选择,应执行什么操作。如果路由器直接连接到相关网络,则下一跳将是一个空的可选项。在这种情况下,下一跳是数据报的目的地址。但是,如果路由器通过某个其他路由器连接到所述网络,那么下一跳将包含沿路径上下一个路由器的 IP 地址。接口号给出了应当用来将数据报发送到下一跳的路由器的 NetworkInterface 的索引。你可以通过 interface(interface_num) 方法来访问这个接口。[[什么意思-lab5-2]]

void route();

这里才是真正的考验。这个方法需要将每个传入的数据报路由到下一跳,并通过适当的接口输出。它需要实现IP路由器中“最长前缀匹配”的逻辑,找到最佳的路由路径。这意味着:

  • 路由器搜索路由表以找到与数据报的目的地址相匹配的路由。所谓“匹配”,我们指的是目的地址的最高有效前缀长度位与路由前缀的最高有效前缀长度位是相同的。
  • 在匹配的路由中,路由器选择具有最大前缀长度值的路由。这是最长前缀匹配路由。
  • 如果没有匹配的路由,路由器将丢弃该数据报。
  • 路由器减少数据报的TTL(生存时间)。如果TTL已经为零,或在减量后达到零,则路由器应丢弃数据报。
  • 否则,路由器会将修改后的数据报通过适当的接口(interface(interface num).send datagram())发送到适当的下一跳。

Internet设计的美在于此(或者至少是一个成功的抽象):路由器从不考虑TCP、ARP或以太网帧。路由器甚至不知道链路层的样子。路由器只考虑Internet数据报,并且只通过NetworkInterface抽象与链路层交互。当遇到像“链路层地址是如何解析的?”或“链路层是否有自己独特的与IP不同的寻址方案?”或“链路层帧的格式是什么?”或“数据报的有效负载含义是什么?”等问题时,路由器就不关心了。

4 测试

您可以通过运行cmake --build build --target check5来测试您的实现。这将在图2所示的特定模拟网络中测试您的路由器。

5 Q & A

  • 我应该使用什么数据结构来记录路由表? 由你决定!但没必要弄得太复杂。对于每个数据报来说,需要O(N)的工作量是完全可以接受的,其中N是路由表中的条目数。如果你想做更有效率的事情,我们鼓励你先获得一个工作实现,然后再进行优化,并且无论你选择实现什么,请务必进行详细的文档和注释。
  • 我如何将Address对象形式的IP地址转换为我可以写入ARP消息的原始32位整数? 使用Address::ipv4_numeric()方法。
  • 我如何将原始32位整数形式的IP地址转换为Address对象? 使用Address::from_ipv4_numeric()方法。
  • 我如何比较两个32位IP地址的最高有效N位(其中0 ≤ N ≤ 32)? 这可能是这项任务中“最棘手”的部分——让逻辑正确。编写一个小型的C++测试程序(一个简单的独立程序)或向Minnow添加一个测试以验证您对相关C++操作符的理解并检查您的逻辑可能是值得的。 请记住,在C和C++中,将32位整数移位32位可能会产生未定义的行为。测试在尝试检测此类情况下运行您的代码。您可以通过从minnow目录运行./build/tests/router直接运行路由器测试。
  • 如果路由器没有到目的地的路由,或者如果 TTL(生存时间)变为零,它不应该向数据报的来源发送一个 ICMP 错误消息吗?
    在现实中,这样做确实会很有帮助。但在这个实验室中并不是必需的——丢弃数据报就足够了。(即使在现实世界中,也不是每个路由器在这些情况下都会向源地址发送 ICMP 消息。)
  • 我在哪里可以阅读此 PDF 发布后更多的常见问题?
    请定期检查网站 ( https://cs144.github.io/lab_faq.html ) 和 EdStem。

6 提交

  1. 在您的提交中,请只对 src 目录中的. Hh 和. Cc 文件进行更改。在这些文件中,您可以根据需要添加私有成员,但请不要更改任何类的公共接口。
  2. 在提交任何作业之前,请按顺序执行以下操作:
    (a) 确保你已经将所有修改提交到 Git 仓库。您可以运行 git status 来确保没有未提交的更改。记住:编码时要小步提交。
    (b) 运行 cmake –build build –target format(以规范化代码风格)
    (c) 运行 cmake –build build –target check 5(确保自动测试通过)
    (d) 可选:运行 cmake –build build –target tidy(建议改进以遵循良好的 C++编程实践)
  3. 编写报告 writeups/check 5. Md。这个文件应该是大约 20 到 50 行的文档,每行不超过 80 个字符,以便于阅读。报告应包含以下部分:
    (a) 程序结构和设计。描述代码中体现的高级结构和设计选择。您不需要详细讨论从启动代码继承的内容。利用这个机会来突出重要的设计方面,并为理解这些方面提供更多的细节给您的评分助教。强烈鼓励使用小标题和概要来使这篇报告尽可能易读。请不要简单地将程序翻译成一段英文。
    (b) 实现挑战。描述你觉得最棘手的代码部分,并解释为什么。反思你是如何克服这些挑战的,最后是什么帮助你理解了困扰你的概念。你是如何尝试确保代码维护你的假设、不变性和前置条件的,以及你发现这样做容易还是困难?你是如何调试和测试代码的?
    (c) 剩余的错误。尽可能指出并解释代码中仍然存在的任何错误(或未处理的边缘情况)。
  4. 请填写完成作业所需的小时数和任何其他评论。
  5. 如果在实验室课程中或通过在 EdStem 上提问遇到任何问题,请立即通知课程工作人员。

[[此方法需要将每个传入的数据报从适当的接口路由到下一跳。它需要实现IP路由器的“最长前缀匹配”逻辑]],以找到最佳路由。它先在路由表中搜索与数据报目的地址匹配的路由,所谓“匹配”意思是目的地址的最高有效前缀长度位与路由前缀的最高有效长度位相同。在匹配的路由中,路由器选择前缀长度值最大的路由。如果没有匹配的路由,路由器将丢弃数据报。同时路由器递减数据报的 TTL(生存时间),如果 TTL 为零,路由器将丢弃数据报。否则,路由器在适当的接口上将修改后的数据报发送到下一跳。(interface(interface num)send datagram())

cs144-6

————————————————————————————

0 Collaboration Policy

合作政策: Checkpoint 6 要求您与一位小组成员合作——您需要连接并可能需要一起调试。总体而言,合作规则与 checkpoint 0 相同。请不要查看其他学生的代码或先前版本这些任务的解决方案。请在您的报告中完全披露任何合作者或任何灰色地带——公开是最好的政策。

1 Overview

在课程进行到这一点时,你已经实现了互联网基础设施的重要部分。从可靠字节流的 Checkpoint 0,到传输控制协议的 Checkpoints 1–3,IP/Ethernet 网络接口的 Checkpoint 4 和 IP 路由器的 Checkpoint 5,你已经完成了大量编码工作!

在这个可选的 Checkpoint 中,如果你之前的 Checkpoints 状态都很好,你无需进行任何编码。相反,为了总结你的成就,你将使用你之前所有的实验室内容来创建一个真实的包含你的网络堆栈(主机和路由器)与班上另一名学生实现的网络堆栈进行通信的网络。

本 Checkpoint 需要以两人为一组进行。你将需要与一位实验室伙伴(班上的另一名学生)合作。请使用实验室会话找到实验室伙伴,如果你不能参加实验室会话,请使用 EdStem。如有必要,相同的学生可以多次担任“实验伙伴”。

这个 Checkpoint 是可选的:如果你对至少五个早期 Checkpoints 的主观成绩感到满意,你不需要做这个。但我们仍然鼓励你去做——让一个真正的网络运行并包含你构建的互联网所有部分是有趣且有益的!

2 Getting started

  1. 请确保你已经提交了 ​​Checkpoint 1 的所有解决方案。请不要修改 src 目录顶层外的任何文件或 webget.cc。否则你可能在合并 Checkpoint 6 启动代码时遇到麻烦。
  2. 在实验室作业的 repository 中运行 git fetch –all 来检索最新版本的实验室作业。
  3. 通过运行 git merge origin/check6-startercode 下载 Checkpoint 6 的启动代码。(如果你把 “origin” 远端重命名为别的东西,你可能需要在这里使用不同的名称,例如 git merge upstream/check6-startercode。)
  4. 确保你的构建系统已正确设置:cmake -S . -B build
  5. 编译源码:cmake –build build
  6. 打开并开始编辑 writeups/check6.md 文件。这是你实验室报告的模板,将被包括在你的提交中。
  7. 提醒:在你工作时,请频繁地在你的本地 Git 仓库进行小的提交。如果你需要帮助以确保你正在正确做这件事,请向同学或教学人员寻求帮助。您可以使用 git log 命令查看您的 Git 历史。

3 网络实验在这个实验室中,您将创建一个真实的网络,结合你的网络堆栈和另一个学生实现的网络堆栈。你们每个人都将贡献一个主机(包括你的可靠的字节流、TCP 实现和网络接口)以及一台路由器:

复制代码
NetworkInterface NetworkInterface
“host” “host_side”
192.168.0.50 192.168.0.1
| |
|—————————————-|
Client Router
|
NetworkInterface
“internet_side”
10.0.0.192
——————————
| TCPConnection |
| TCPSender TCPReceiver |
| StreamReassembler |
| ByteStream |
——————————
ByteStream
可靠的字节流
Internet Protocol
实验室 1-4 实验室 1-4
Lab 0 Lab 5
Lab 6
Lab 5
Route: 192.168.0.0/16 (direct)
Route: 10.0.0.0/8 (direct) route: 172.16.0.0/12 via 10.0.0.172

NetworkInterface NetworkInterface
“host” “host_side”
172.16.0.100 172.16.0.1
| |
|—————————————-|
Server Router
|
NetworkInterface
“internet_side”
10.0.0.172
——————————
| TCPConnection |
| TCPSender TCPReceiver |
| StreamReassembler |
| ByteStream |
——————————
ByteStream
可靠的字节流
Internet Protocol
实验室 1-4 实验室 1-4
Lab 0 Lab 5
Lab 6
Lab 5
Route: 172.16.0.0/12 (direct)
Route: 10.0.0.0/8 (direct) route: 192.168.0.0/16 via 10.0.0.192
![[Pasted image 20231202145744.png]]

因为大概率你或你的实验伙伴将位于一个网络地址转换器(NAT)的后面,双方之间的网络连接将通过中继服务器(cs 144. Keithw. Org)流动。我们已经将你的代码结合成一个新的应用程序,可在 build/apps/endtoend 中找到。

以下是运行它的步骤:

  1. 在和实验伙伴一起做这些步骤之前,请先自己尝试。你可以在你的虚拟机上使用两个不同的窗口或终端扮演客户端和服务器的角色。这样你的网络将包括两份你的代码副本(主机和路由器)自己与自己通信。这比与陌生人通讯更容易调试!
    当这些步骤在自己身上有效之后,再与实验伙伴尝试。决定你们两个中谁将充当“客户端”和“服务器”。一旦成功,你们始终可以交换角色并再次尝试。
  2. 要使用中继,请挑选一个介于 1024 至 64000 之间的随机偶数。这将表示你的实验组,并且需要与同时工作的任何其他实验组不同,所以请确实挑选一个随机数。并且它需要是一个偶数。
    如下例,我们假设你选择了“3000”:
  3. “服务器”学生从 build 目录运行:
   ./apps/endtoend server cs144.keithw.org 3000

(用你实际的数字替换“3000”)。
如果一切顺利,“服务器”将打印类似以下输出:

   $ ./apps/endtoend server cs144.keithw.org 3000
   DEBUG: 网络接口具有以太网地址 02:00:00:5e:61:17 和 IP 地址 172.16.0.1
   DEBUG: 网络接口具有以太网地址 02:00:00:cd:e7:e0 和 IP 地址 10.0.0.172
   DEBUG: 添加路由 172.16.0.0/12 => (直接) 接口0
   DEBUG: 添加路由 10.0.0.0/8 => (直接) 接口1
   DEBUG: 添加路由 192.168.0.0/16 => 10.0.0.192 在接口1
   DEBUG: 网络接口具有以太网地址 5a:75:4e:8b:20:00 和 IP 地址 172.16.0.100
   DEBUG: 正在监听传入的连接...
  1. “客户端”学生从 build 目录运行:
   ./apps/endtoend client cs144.keithw.org 3001

(用加 1 之后的随机数替换“3001”)。
如果一切顺利,“客户端”将打印类似以下输出:

   $ ./apps/endtoend client cs144.keithw.org 3001
   DEBUG: 网络接口具有以太网地址 02:00:00:41:c7:5b 和 IP 地址 192.168.0.1
   DEBUG: 网络接口具有以太网地址 02:00:00:e6:66:d9 和 IP 地址 10.0.0.192
   DEBUG: 添加路由 192.168.0.0/16 => (直接) 接口0
   DEBUG: 添加路由 10.0.0.0/8 => (直接) 接口1
   DEBUG: 添加路由 172.16.0.0/12 => 10.0.0.172 在接口1
   DEBUG: 网络接口具有以太网地址 26:05:12:4a:8a:c9 和 IP 地址 192.168.0.50
   DEBUG: 从 192.168.0.50:57005 连接...
   DEBUG: 连接至 172.16.0.100:1234...
   成功连接至 172.16.0.100:1234。
   CS144: 计算机网络导论 春季2023
   并且“服务器”将打印额外一行:
   新连接来自 192.168.0.50:57005。
  1. 如果你看到了预期的输出,恭喜你—两台计算机已经成功地完成了 TCP 握手! (a) 照顾好自己(遵守适当的社交距离规则)—你们应该为此感到自豪! (b) 现在是时候交换数据了。在一个窗口中输入内容,看看是否会在另一个窗口中显示。尝试反向输入。 (c) 尝试结束连接。输入完毕后按下 ctrl-D。这将会终止该方向上的字节流写入,同时继续接收来自对端直到对端也结束字节流的传入数据。验证这一过程是否发生。 (d) 当两边都结束了字节流,且一方在挥手等待几秒钟之后,两个程序应该会优雅地退出。
  2. 如果你没有看到预期的输出,可能是时候开启“调试模式”了。运行“endtoend”程序并添加一个额外参数:在命令行末尾加上“debug”。这会打印出所有被交换的以太网帧,并让你能够看到所有ARP和TCP/IP帧。
  3. 一旦你在自己计算机的两个窗口之间搭建网络并成功运行,那么尝试与实验室搭档(及其所实现的功能)重复以上步骤。

4 发送文件 当基本对话通信看起来没问题时,尝试通过网络发送一个文件。同样,如果一切顺利,再尝试与实验室搭档做此操作。以下是步骤: 要写入一个一兆字节大小的随机文件至“/tmp/big.txt”: dd if=/dev/urandom bs=1M count=1 of=/tmp/big.txt 服务器一接受到传入连接就发送文件: ./apps/endtoend server cs144.keithw.org 偶数端口 < /tmp/big.txt 客户端关闭其输出流并下载文件: cs144.keithw.org 奇数端口 > /tmp/big-received.txt 比较两个文件以确保它们完全相同: sha256sum /tmp/big.txt 或 sha256sum /tmp/big-received.txt 如果SHA-256散列值匹配,则可以几乎肯定文件正确无误地传输。

CS144:2023年春季计算机网络导论

5 额外学分作为些许额外的学分,如果一切工作都完美无瑕,我们鼓励你做些创造性的事情并将其写到你的报告中。请随意修改 endtoend.cc 程序。你可以创建一个涉及更多学生同时参与的更复杂网络,或者做一些我们未曾预料到的事情。(明确说明:这不是必须的。)

  1. 在 writeups/check 6. Md 这个文件中写一篇报告。这份报告应大约包含 30 到 70 行内容,每行不超过 80 个字符,以便于阅读。报告应该包含以下几个部分:
  • 单人部分
    – 你的实现是否成功地与它的副本开始并结束了一次对话?
    – 它是否成功传输了一个一兆字节大小的文件,并且接收后内容完全相同?
    – 如有,请描述为了通过这些步骤而做出的任何代码改动。
  • 小组部分
    – 你的实验伙伴是谁(以及他们的 SUNet ID,例如 winstein)?
    – 你的实现和伙伴的实现是否成功地相互之间开始并结束了一次对话(每个实现充当“客户端”或者“服务器”)?
    – 你们是否成功地在两个实现之间传输了一个一兆字节大小的文件,并且接收后内容完全相同?
    – 请描述为了通过这些步骤而做出的任何代码改动,无论是由你还是你的实验伙伴完成。
  • 创意部分
    – 如果你做了一些“创意挑战”,请在这里夸耀一番!
  1. 如果确实需要对源代码进行更改,请只更改 src 顶层目录下的. Hh 和. Cc 文件。在这些文件中,你可以随意添加私有成员,但请不要更改公共接口。
  2. 请不要添加额外的文件——自动评分器不会查看它们,并且你的代码可能无法编译。
  3. 请填写这项作业花费了你多少小时以及其他任何评论。
  4. 如果在实验课上或者通过在 EdStem 发帖遇到任何问题,请尽快通知课程工作人员。

Checkpoint 7: 网络测量

截止日期:课程结束时(6月7日下午5点)

0 协作策略

协作政策:与checkpoint 0相同。请在你的报告中充分披露任何合作者或任何不确定区域—披露是最好的政策。

1 概述

到目前为止,你已经实现了互联网基础设施的重要部分。这个checkpoint是关于对实际的互联网进行测量,并报告特定路径的长期统计数据。 此checkpoint是可选的:如果你对至少五个较早的checkpoints的主观评分感到满意,那么你无需完成此项。但我们仍然鼓励你做它——这很有趣,而且你可能会学到一些东西!

2 收集数据

  1. 挑选一个远端主机位于互联网上,其从你的虚拟机(VM)到该主机的RTT(往返时延)至少有100毫秒。一些可能:
    • www.cs.ox.ac.uk (英国牛津大学计算机科学部web服务器)
    • 162.105.253.58 (中国北京大学计算机中心)
    • www.canterbury.ac.nz (新西兰坎特伯雷大学web服务器)
    • 41.186.255.86 (卢旺达MTN)
    • 首选:自己选择一个距离您至少100ms RTT的节点
  2. 使用mtrtraceroute命令追踪你的VM和该主机之间的路由。
  3. 运行至少两小时的ping来收集这条互联网路径的数据。使用类似ping -D -n -i 0.2 hostname | tee data.txt的命令将数据保存在“data.txt”文件中。(-D参数让ping记录每行的时间戳,-i 0.2使其每0.2秒发送一个“echo请求”ICMP消息。-n参数使其跳过尝试使用DNS来将回复的IP地址反向解析为主机名。)
  4. 注意:每0.2秒发送一个默认大小的ping是可以的,但请不要以比这更快的速度给任何人发送流量。

3 数据分析

如果你每秒发送五个ping请求持续两小时,你将发送约36000次echo请求(= 5 × 3600 × 2),预计其中大多数会在ping输出中收到答复。请使用您选择的编程语言和绘图工具准备一份报告(PDF格式),该报告至少包含以下信息:

  1. 整个时间间隔内的总体投递率是多少?换句话说:收到的echo回复与发送的echo请求的数量的比例是多少?
  2. 最长连续成功ping的串是多久(连续有回复)?
  3. 最长的丢失阵列是多久(全部未回复)?
  4. 随着时间推移,“数据包丢失”事件的独立性或相关性如何?换句话说:
    • 假设echo请求#N收到了回复,那么echo请求#(N+1)也成功被回复的概率是多少?
    • 假设echo请求#N未收到回复,那么echo请求#(N+1)得到成功回复的概率又是多少?
    • 这些数字(条件投递率)与第一个问题中的总体“无条件”包投递率相比如何?丢失是散发的还是成串的?
  5. 在整个时间间隔内观察到的最小RTT(往返时间)是多少?(这可能是对真实的MinRTT的合理近似……)
  6. 在整个时间间隔内观察到的最大RTT是多少?
  7. 制作一个RTT与时间函数的图表。x轴标出实际的时间(覆盖超过2小时的期间),y轴应该是毫秒数表示的RTT。
  8. 可选:制作一个观察到的RTT分布的直方图。分布的粗略形状是什么?
  9. 可选:绘制“ping #N的 RTT”与“ping #N+1的 RTT”的相关性图。x轴应为第一个RTT的毫秒数,y轴为第二个RTT的毫秒数。RTT随时间的相关性有多紧密?
  10. 您从数据中得出什么结论?网络路径的行为是否符合您的预期?查看图表和摘要统计数据时,有什么(如果有的话)让您感到惊讶?

请通过Gradescope以PDF格式提交您的报告,并祝您有一个愉快的夏天!如果您喜欢这门课程,一些很好的后续课程包括CS155(计算机与网络安全)、CS244(网络高级主题)和CS249i(现代互联网)。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇
Document