UCB-CS168-互联网导论笔记-全-

UCB CS168 互联网导论笔记(全)

1:课程介绍

在本节课中,我们将要学习这门课程的核心内容、课程结构以及相关的教学安排。我们将首先探讨“互联网”、“协议”和“架构”这三个关键术语的含义,然后介绍课程的后勤信息,包括教学团队、评分政策和课程大纲。

什么是互联网?🌐

首先,我们来探讨“互联网”的含义。这个词实际上有两种非常不同的含义,取决于你交谈的对象。

如果你询问我、助教或本学期的你们,术语“互联网”对我们来说意味着类似管道基础设施的东西。它是将所有需要通信的设备连接起来的基础设施。构建和运行该基础设施所需的技术,可能是大多数人从未听说过的术语,例如 TCPBGPDNSISIS 等。

然而,另一种含义是,如果你询问你的父母、祖辈或许多朋友,他们认为的“互联网”往往是建立在上达基础设施之上的应用生态系统。这包括像 Netflix、YouTube、Twitter、Facebook 等应用。

对于本课程,当我们谈论“互联网”时,我们真正使用的是第一种定义。这门课程是关于我们如何构建支撑所有这些应用的基础设施。至少对我来说,如何构建这个支持所有应用的全球基础设施,是两者中更有趣的问题。

接下来,让我更深入地解释一下我所说的基础设施或管道基础设施是什么意思。我们经常喜欢给出的一个类比是,互联网基础设施就像管道系统。整个庞大而复杂的生态系统和基础设施都是为了将水输送到你家。这就是管道基础设施。你打开水龙头收集水,你用这些水做什么有很多不同的事情,而管道基础设施实际上并不关心。这就是我所说的基础设施的含义。

现在,让我们具体拆解一下这个互联网基础设施,它由哪些组件构成?

从高层次看,有三个重要的组成部分:

  1. 端主机:这些是任何使用互联网进行通信的设备。我们每个人都有太多这样的设备:你的 iPad、智能手机、笔记本电脑。像谷歌这样的公司拥有数千甚至数百万台托管内容的服务器,它们也是互联网上的端主机。如今,我们还有电视、汽车等设备使用互联网。我最喜欢的例子之一是像心脏起搏器这样的设备,如今你可以获得一个能直接将你的健康状况传达给医生的起搏器。因此,使用互联网的设备范围正在不断扩大,种类极其多样。在本课程中,我们将用一个蓝点来表示它们,并称之为端主机。
  2. 交换机:这些设备不一定运行应用程序,但它们帮助这些端主机进行通信。所以它们的工作是帮助其他设备通信。交换机可能是你家里 10 美元的家用路由器,也可能是 AT&T 在其全球骨干网中部署的 1000 万美元的路由器。当你将手机配置为热点时,它也是互联网上的一个交换机。
  3. 链路:这些是连接端主机与交换机以及连接交换机与其他交换机的链路。同样,就像端主机和交换机一样,链路的种类也多种多样:铜缆、同轴电缆、光纤链路、无线链路等。在电气工程系,你可以上一整门课来学习如何构建光纤链路、如何构建激光器、如何获得良好的信号强度等。同样,也有多门课程教授如何构建无线链路或无线技术。在本课程中,我们不会深入探讨任何特定链路技术的细节。我们的网络视图处于稍高的层次。在本课程中,我们大部分时间将所有这些不同的链路技术或低级技术抽象为仅仅是一条链路,它用于互连两个端点。

因此,从基本构成来看,这就是互联网基础设施的组成。但如果没有一个额外的要素,这幅图景就不完整。那就是,互联网不是一个想法或概念,它是一个人造物,一个你可以触摸、可以探测的实际事物。因为它是一个人造物,并且是一个商业人造物,我们需要有人来构建、运营、管理和升级这个基础设施。

互联网的独特之处在于,没有单一的实体或组织来运行互联网。相反,互联网有各种各样的公司或机构,每个都拥有、运营和管理互联网的一部分。它们负责自己的网络部分。这些可能是像 AT&T 这样的公司,或者像加州大学伯克利分校这样的机构。伯克利有一个完整的网络团队来构建、运营和管理伯克利的网络基础设施。事实上,我们有多个团队:校园网络、每个系都有自己的网络等。像 AWS 这样的公司也有庞大的网络。随着时间的推移,情况发生了变化:过去是像 AT&T 和 Verizon 这样的公司拥有和运营网络基础设施,然后有公司在此基础上构建应用。但随着云计算的发展,这种世界观已经改变,现在云提供商都构建和运营大型全球网络。例如,当今世界上最大的网络实际上是谷歌的骨干网。

不过,需要记住的重要一点是,当我们谈论这些提供商时,我们称它们为 ISP 或互联网服务提供商。重要的是,它们在行政上、经济上是独立的实体,并且独立运营。因此,AT&T 可以按照自己选择的方式构建和运行其网络,而无需与谷歌或加州大学伯克利分校协商或达成一致。这看起来可能不是一个技术因素,而是一个经济财务问题,但它实际上对我们如何构建以及技术如何工作有着深远的影响。

这是因为我们必须记住,这些 ISP 中的每一个都处于竞争环境中。AT&T 与 Verizon 竞争,谷歌与 AWS 和 Azure 竞争。因此,它们是相互合作的竞争实体,以提供网络连接。但由于它们独立行动并且是竞争实体,它们并不总是透露人们可能希望它们透露的信息。例如,如果 AT&T 的一条链路出现故障,AT&T 可能不想告诉任何人。它们会尽一切努力使其不影响其他用户,但它们不会告诉所有人:“嘿,我的链路坏了。”因此,现在,如果你是 Verizon 或谷歌,为了你的用户,你必须弄清楚某处是否存在故障,并且你必须决定:“哦,看起来 AT&T 表现不佳,所以我实际上要把我的流量发送到其他地方。”这就是为什么有时作为用户,如果你的互联网连接不好,你打电话给你的 ISP,他们似乎也不太清楚问题出在哪里,或者需要很长时间才能弄清楚问题所在。这是因为有多个 ISP 各自为政。

因此,尽管这源于它们是独立的行政实体这一事实,但它对我们如何设计技术有着巨大的影响。试想一下,你正在构建一个系统,当出现故障时你得不到通知,或者你对故障发生的位置没有可见性。因此,你开始设计其他方法。例如,你可能会从多个有利位置发送流量,然后说:“哦,每次我的流量到达西海岸的 AT&T 时,我开始看到性能下降。这就是我判断 AT&T 网络有问题的方式。”然后你决定如何应对。因此,你现在构建了一整套故障检测方法,以适应不同公司运营基础设施不同部分这一生态系统。

这就是 ISP。这实际上就是我们在这里看到的,一个相当完整、高层次但完整的互联网基础设施视图。本学期的很多内容将包括理解整个图景如何运作,以及深入探讨该基础设施的各个部分及其工作原理,从而使这幅图景随着时间的推移变得越来越详细。

互联网的任务:数据传输 📦

我们用这个基础设施做什么?拥有这整个基础设施的意义是什么?关于互联网的一个好处是,我可以用非常简洁的一句话来总结互联网的工作或任务:互联网的工作,或者说这个基础设施的真正工作,是在端主机之间传输数据。我们构建所有这些基础设施就是为了完成这一项任务:在端主机之间传输数据。

所以,如果我这里有一个端主机,它有一些数据要发送,我需要做的是通过网络找到一条从源到目的地的路径,然后沿着该路径移动数据。这看起来像是一个非常简单的任务,但实际上并不像听起来那么简单。例如,让我们想想寻找路径:你必须找到一条由这些交换机和链路组成的路径序列,将你从源带到目的地。你必须确保该路径有足够的容量来发送数据。你必须确保在特定时间点该路径上没有故障。即使你完成了所有这些,你还必须确保该路径满足 ISP 的需求,即 ISP 运行其基础设施时,对你通过该特定路径发送数据没有意见。例如,如果谷歌说“绝对不行,你不能给我发送该流量”,那么这就不是一条可行的路径,你必须考虑到这一点。所以,它并不像看起来那么简单,但从概念上理解很简单。

此外,虽然这看起来像是一个非常简单的任务,甚至是非常显而易见的任务——当然,你的基础设施就是为了在端主机之间传输数据而设计的——但这个目标实际上比乍看起来更微妙、更有智慧。在以下意义上,很容易忽视这个目标的智慧:我并没有说互联网的目标是在端主机之间传输 YouTube 数据。如果那是互联网的目标,我们就会有一个传输 YouTube 数据的互联网,而 Netflix 将不得不构建自己的互联网来传输 Netflix 数据。但我们拥有的不是这样。相反,我们拥有的是一个非常通用的基础设施。

你可能会想,是的,这似乎很明显。为什么你会为 YouTube 建一个互联网,为 Netflix 建另一个互联网呢?但值得注意的是,实际上在互联网出现之前,人们构建的所有通信网络都是为特定用途而建的。一个很好的例子是电话网络。电话网络是一个为承载音频流量这一目标而构建的全球通信基础设施。因此,电话网络唯一做的就是承载语音流量,仅此而已。而在互联网上,早期的互联网设计者构建了这一个通信基础设施,它满足了各种需求。当他们最初构建这个基础设施时,还没有电子邮件,没有万维网,没有 Netflix,没有 YouTube,没有 TikTok。然而,他们构建了一个基础设施,因为它被设计成通用的,所以能够支持所有这些年来出现的应用。因此,我认为这是一个特别优雅的通信基础设施目标。

什么是协议?🤝

我们在这里讨论的第二个术语是“协议”。当我们谈论协议时,让我回到我们之前的那幅图,但让我只关注端点,暂时忘记那些交换机和链路。我们设定互联网的目标是传输数据。但我们都知道我们用互联网做什么。这就是我们使用互联网的方式。

因此,如果你稍微深入研究一下这些应用中的任何一个,任何互联网应用都是一个分布式应用。在某个地方,两个端点都有一些代码。如果你查看那段代码,你会看到类似这样的内容:假设 Alice 想发送一些东西给 Bob。在某个地方会有一个发送消息的调用:“我要给 Bob 发送一条消息。”而在 Bob 的代码中,某个地方会说:“我要接收一条消息。”所以,当你出去工作,如果你的工作涉及编写代码,总会有那么一个时刻,你必须编写一个分布式应用。实际上,我想不出我们今天编写的任何应用不是分布式应用。没有人再编写单 CPU 代码了,这已经不存在了。

因此,当你被分配编写该应用时,即使你对交换机和链路以及互联网的管道一无所知或不关心,你也必须为你的应用中的端点如何交换消息编写一个规范。你如何使用那个发送代码?所以,你可以设计一个规范,看起来像是我称之为协议的东西,但在我的消息交换中,有人必须说“你好”,然后另一个端点必须回复“你好”。然后 Alice 说:“你能给我那个文件吗?”然后 Bob 回复文件。这是一种完全合理、明智的设计这种必要规范的方式。它告诉你双方预期的行为。

这并不是定义规范的唯一方式。你可以不采用那种非常礼貌的对话方式,而是说我希望 Alice 的通信方式是:Alice 说“你好”,然后 Alice 非常激进地说“给我文件,给我那个文件,给我那个文件”,直到她得到文件为止。哪个更好?这个激进的规范更好吗?还是之前的那个?这显然不好吗?实际上,这并不明显不好。这真的取决于你的目标是什么。如果你的目标是尽快获得那个文件,并且你有充足的网络容量可以消耗,那么这完全是实现它的方式。如果你想更谨慎,确保双方都准备好了,那么之前的规范就是。

然而,一旦你定义了你的规范,它就决定了你应用的行为,也在一定程度上决定了你应用的性能。因此,你会发现无数人在优化这些规范。但一旦你定义了它,双方都期望遵守。例如,如果你的设计是那个非常礼貌的规范,那么当 Alice 这样做时,就违反了你的规范。可能是因为你的代码有错误,这是不可接受的行为。或者也许 Alice 是恶意的。这很糟糕。

因此,协议是通信实体将交换的消息的规范。它是这些消息的语法和语义。它非常像对话惯例。当我们见面时,我们脑海中都有这些关于如何沟通的惯例,比如等待对方回应,说一些礼貌的话。

设计一个好的协议实际上比最初看起来要难,因为你意识到,在互联网上,你必须考虑双方所有可能的行为。所以,如果 Alice 说了“你好”,而 Bob 很长时间都没有回复“你好”,该怎么办?Alice 应该再说一次“你好”吗?Alice 应该尝试联系别人吗?如果我尝试对另一个人说“你好”,然后 Bob 回来说“你好,我在这里”,该怎么办?如果 Bob 在通信过程中崩溃了,该怎么办?你必须考虑每一种可能性。这就是协议设计既有趣又常常导致大量错误的原因。人们发现消息规范或协议规范实际上会导致无尽的错误和调试过程。

什么是架构?🏗️

我们在这里的最后一个术语是“架构”。要理解架构,这有点最难解释,而且我认为这门课程在某些方面最深入地讨论了系统架构。因此,在我开始谈论互联网架构的含义之前,我会带你稍微深入一些细节。

然后,我将讨论为什么研究互联网。为什么你应该关心所有这些?有一个简单而明显的答案可以给出,那就是互联网已经并且正在继续改变几乎一切,改变我们生活的方式。想想如果没有互联网,你怎么会知道来这里。我们生活的几乎每个方面都受到互联网的影响、帮助或辅助,从你如何获取新闻,到国家的经济如何起伏等等。实际上,当你想到互联网的变革性方面时,你几乎必须追溯到像电话系统这样的技术,才能想到一个如此改变社会的技术。这有点酷。而且它发生的时间并不长。你可能出生在一个互联网无处不在的时代,你只是假设如此。但对我来说,情况并非如此。我比你早一代,我确实在年轻时(本科阶段)就接触到了互联网。所以我有一个没有互联网的童年。你可能无法想象那是什么样子。但那就是现实。仅仅一代人的时间,就改变了整个全球的方式。

但你已经知道这些了。你不需要我告诉你互联网很重要。所有这些只是告诉你它很重要,但你已经知道了。我想让你思考的是,为什么互联网很有趣?作为计算机科学家和技术人员,为什么互联网很有趣?实际上,网络作为一个领域并不那么古老。与算法、甚至人工智能、编译器、编程语言等领域相比,网络领域相对较新。网络领域的首批教员仍然健在,甚至不是我们系里最资深的教员。他大约在 2000 年左右加入。因此,在计算机科学领域,这是一个相对较新的领域。

为什么网络是一个领域,即使在计算机科学家中间也常常是一个谜。例如,如果你问理论学家,他们经常会问这样的问题:你的互联网的形式模型是什么?或者什么是优化?但互联网没有形式模型,如果我们有的话会很酷,但我们没有概念,当然也没有关于拥有最优互联网意味着什么的大概念。这对理论学家来说非常奇怪。你如何解决一个没有定义最优性的问题?甚至对于操作系统或传统软件计算机科学家来说,他们会想,我们一直都有进程间通信,比如我们一直都有进程和它们通信的概念。那么,现在通信发生在网络上,有什么特别的呢?硬件人员往往是最困惑的。如果你是 CPU 架构师,你知道你的 CPU 每个周期在做什么。你确切地知道一条指令执行需要多少个周期。有明确定义的性能基准指导整个行业和研究。CPU 设计师可以竞争并回来说,我的 CPU 运行速度快 X%,功耗低 1%,你就知道什么是成功。但在互联网上,情况就不那么清楚了。所以他们会问你,互联网的性能基准是什么?或者互联网今天的表现如何?没有一个简单的答案。我不能上网说互联网让我感到羞耻,但这并不意味着互联网没有形式。谈论互联网上的性能实际上意味着什么?

这些都是网络作为一个领域让人感到困惑的一些特点。我没有一个简洁的答案给你,但你知道,这正是网络的样子,也是为什么它有趣的原因。我认为向人们解释为什么它有趣的最好方式——我会说,这是一个领域仍然相对年轻的标志,就像我必须花半个小时谈论为什么互联网有趣一样——是思考互联网的几个定义性特征。

一个是网络与互联网的区别。我们拥有通信技术或网络技术的时间远比互联网长。即使我们敲鼓时,那也是一种通信技术。因此,互联网不是关于一种新的或特定的网络技术。它不是关于以太网,不是关于蜂窝网络,不是关于 5G、6G 或 Wi-Fi。我们一直都有这些,我们将永远继续拥有这些。互联网比这更大。相反,互联网的目标实际上是连接不同的网络。当你阅读构建原始互联网的团队的目标和动机的开场白时,他们的理由是:我们已经有了这些孤立的网络区域。我们这里有无线电网络,这里有国家实验室的高速网络。我们希望能够互连所有这些。所以,这不是关于如何构建一个特定的网络,而是关于如何互连。这本身就是一个不寻常的目标。如何衡量互连网络的好坏?本质上,这不是用我们过去使用的传统指标和语言来定义的目标。

与此同时,能够说“我将互连不同的网络”是非常强大的。为了互连不同的网络,我们尝试做什么或实现什么?我们尝试提出一种共同的语言,通过这些网络在接口处能够理解双方的数据。这引出了我认为可能是互联网的关键定义性方面,即它是一个联邦系统。设计上,它旨在成为一个系统,每个 ISP、每个实体都可以按照自己想要的方式构建自己的网络。然后我们只需要想办法合作,使这两个网络能够交互。

这有一个奇怪的含意,因为互操作性是最重要的目标,我们有成千上万的 ISP 相互连接。但这意味着它引发了一个根本性的挑战:如何互连竞争实体?你有竞争的网络提供商,然而它们获得价值的唯一方式就是彼此合作。AT&T 不能说我只连接谷歌,不连接亚马逊,因为即使它们这样做,它们也会失去所有客户,而每个人都想连接。所以它们必须与人连接。谷歌不能告诉其客户我不连接亚马逊。所有谷歌的客户都是。无论如何,它们必须想办法合作。它们通过这种共同语言或共同协议进行合作,我们将开发这种协议。

但这确实导致了商业因素和技术因素之间的持续拉锯战。因此,从你设计这些 ISP 之间共同语言或共同协议的角度来思考。你应该暴露什么信息?如果你把它看作一个 API,谷歌应该向亚马逊暴露什么信息?如果你从寻找路径和发送数据的技术角度思考,你应该暴露各种信息:你应该说这是我的拓扑结构,这是我当前的负载,这是通过这些链路的成本,这是我正在经历的故障。如果你暴露所有这些信息,那么对方就能很好地工作,在正确的时间、正确的路径上发送适量的流量。这样我们就会有一个非常高效、高性能、非常可靠的网络。

但如果你诚实的话,你并不真的想分享所有这些信息,因为这是你的秘密配方。这是你使网络运行更高效或更具成本效益的方法。更糟糕的是,如果你的网络实际上并不那么好,你肯定不想暴露这些信息。因此,你陷入了这种来回拉锯:是的,你可以设计一个在性能上最优、或在成本上最优、或在某方面最优的协议,但它是否与 ISP 的激励相一致?它们之间实际结算资金的方式是否与你在技术层面所要求的相匹配?因此,你面临着技术因素与围绕激励和经济等现实世界因素之间的持续拉锯。

这也使创新复杂化。我说过,我们实现联邦系统的方式是采用这种共同语言,我们说同一种语言,因此我们可以来回交换数据。但如果你是一个商业实体,你知道,你如何获胜?你通过差异化获胜,通过拥有别人没有的创新或功能获胜。但如果我有一个很棒的功能,我无法向客户或他人暴露,因为如果它不是共同语言的一部分,就没有人可以使用它。那么它有什么用?就像拥有只有你自己理解的个人词汇表一样。因此,很难弄清楚如何在联邦环境中进行创新。同样,升级也是如此。我们谈论从 IPv4 升级到 IPv6 已经 25 年了。你不能有一天说:“嘿,整个互联网今天都要升级。每个 ISP 都要从 IPv4 升级到 IPv6。”这不是一个选项。因此,你必须考虑:我有一个网络,有些人有 IPv4,有些人有 IPv6,有些人混合使用更多,我们如何在这种环境中工作?这些都是我们在设计互联网时必须解决和处理的基本目标。

然而,联邦虽然带来了所有这些挑战,但它也实现了这种巨大的规模。我想我们都见过这些统计数据,我就不用它们来给你留下深刻印象了。值得注意的是,实际上联邦实现了这种巨大的规模,因为它说每个人都可以建立自己的网络,然后我们把它们整合在一起,变得更大,规模迅速扩大。我们在互联网上看到的所有这些令人印象深刻的统计数据,我们的领域现在涵盖了这种互联网规模。互联网规模的应用,互联网规模的云,这已经成为我们计算机科学语言的一部分。

然而,所有这些多样性和动态范围也带来了巨大的性能异构性。例如,如果我查看数据中心中相邻的两台服务器,它们可以在微秒级别进行通信。如果我向卫星发送流量,可能需要几秒钟。同样,数据包丢失率也可能有很大差异,从极其可靠的链路到可能丢弃你大部分流量(例如某些海底电缆可能丢弃 90% 的流量)的链路。因此,每次我们设计时,都必须为这种极端的异构性进行设计。我想强调的是,这里谈论的是六到八个数量级的差异。例如,CPU 设计师经常说,我将从 32 GB 内存升级到 64 GB 内存,或者我的缓存从 16 KB 升级到 32 KB,每个人都会为此感到兴奋和关注。那是 2 倍的差异。从 1 到 100,每个人都必须考虑和处理,每个人都会担心。但在互联网上,这种六到八个数量级的差异不仅存在于不同时间点,而且在任何时间点,互联网上都存在六到八个数量级的多样性。

所以现在,当你回到那个协议时,那个非常保守的来回说“你好”的协议与那个只是发送所有流量的协议,你必须考虑这是在哪种链路上运行。如果它通过卫星链路运行,其性能将与在数据中心通过 10Gb 以太网链路运行非常不同。因此,这种异构性以及为这种异构性设计代码,再次成为我们设计互联网规模系统时需要考虑的一部分。

它不仅延伸到性能,还延伸到我们看到的端点类型、应用类型、用户类型,包括恶意用户现在也是系统的一部分。再加上另一个基本约束:异步操作。在发送数据方面,我们无法超越光速。所以,如果你考虑一下,设想一个非常简单的协议或消息交换,你在伯克利的机器向纽约发送一条消息。假设你希望加速数据传输,你能期望的最好情况是,从纽约来回一趟,只考虑地理距离并假设以光速传播,大约需要 27 毫秒才能发送一条消息并得到回复。如果我想到一个 3 GHz 的 CPU,这是今天相当标准的 CPU,那就是我的机器空闲等待 27 毫秒。这段时间是,如果你想一想,每秒的周期数,大约是 8400 万个周期。如果我说现代 CPU 每个周期可以执行一条指令,这是相当合理的,那么在你的消息往返纽约期间,你的 CPU 本可以执行 8400 万条指令,或者做其他事情。所以我认为你不会在等待消息返回时停滞。因此,它会继续执行某些指令。这意味着当我的消息返回时,我的系统状态与发送消息时相比已经发生了根本性的变化。它不再是同一个状态了。发送消息的纽约机器的状态在我听到消息时也已经改变了。这导致了我们必须处理通信反馈总是延迟的这一事实。我们必须在设计应用时处理这种异步性。

因此,当我听说一个故障时,它实际上是在世界范围内发生的。我发送的所有流量发生了什么?故障发生时我在哪里?实际上,你想想所有这些消息,它们丢失了吗?我怎么知道它们丢失了?它们发生了什么?再加上所有这些,还有故障点。从网络接口卡、交换机到链路再到末端的机器,路径上有很多组件。路径上可能有成百上千个组件。如果你说你有 50 个组件,每个组件在 99% 的时间内都是可靠的,这意味着通信有 40% 的几率会出问题。然后你还有异步性。你实际上需要一些时间才能听到那个故障。所以,再次强调,在极端规模、极端异构性、异步性和易故障性的情况下处理故障。

互联网的设计者实际上是第一个开发出如何应对这种情况的模板的人。这种故障处理方式现在几乎是所有云系统或我们在互联网上构建的任何系统所遵循的方式。最后是持续演进,我之前提到过,我们在 70 年代最初开始构建互联网时,甚至 90 年代或 2000 年代的互联网,与我们今天拥有的互联网完全不同。再次强调,这意味着你不能为单一故事设计。这不像说我在为 YouTube 设计,或者我在为语音设计。你实际上是在为非常广泛的潜在应用进行设计,包括你尚未想到的应用。

因此,互联网是一个联邦系统,规模巨大,极其异构,异步,易故障且不断变化。回到关于互联网架构或互联网设计是科学还是艺术的问题,它太复杂,无法用理论模型来建模。你如何建模我告诉你的所有这些约束?此外,能运行的代码并不意味着什么。假设我写了一段代码,一种寻找路径的新方法。我的代码编译了,我测试了它。但它不符合 ISP 想要的激励。那么我的代码编译了意味着什么?没什么意义。我没有设计它来实际满足客户的需求。我甚至如何测试它?我可以在一个有 10 台机器的实验室里测试它。但这并不能告诉我它是否能在具有故障等的互联网规模上运行。性能基准肯定不是。我可以测量互联网上的路径。互联网上有数十亿条路径。我无法测量所有路径。即使我今天测量了一条路径的性能,五分钟后它也会改变,因为不同的用户和不同的应用会发送流量。一年后,当你有了完全不同的应用时,它肯定会改变。因此,如何测量互联网上的性能是一个巨大的领域。人们为此召开会议,整个公司致力于尽可能好地测量互联网。

因此,所有这些再次表明,互联网的创造需要一种新的设计范式。当人们开始构建互联网时,计算机科学家没有必要的工具来构建互联网。他们没有所需的语言。因此,他们发明了自己的语言。这实际上就是你们在这门课中将要学习的内容。它影响了互联网。所以,当你学习这些时,你将学习互联网是如何构建的。但这些原则延续了下来。我会说,所有的云计算现在都建立在这些原则之上。任何时候你构建一个在互联网上大规模运行的分布式应用,你都会调用其中一些原则。

因此,我说互联网设计是一种设计语言。它有点像一种模式或模板,用于如何基于一组特定的设计原则构建系统。这些就是我们整个学期将要详细讨论的设计原则。今天我不期望你们能理解它们,我只是把它们列出来让你们看看。值得注意的是,几乎所有这些原则,尤其是它们的组合,以前从未做过,在当时是激进的背离。

让我举一个具体的例子来说明这一点:尽力而为服务模型的概念。我这么说是什么意思?当你构建一个系统时,在这种情况下是一个网络或互联网,你必须回答的问题是:网络应该支持什么样的服务模型?我的意思是,几乎可以把它看作一个 API 或合同。端主机可以期望什么?用户可以从网络期望什么?这就是我所说的服务模型,就像这个合同。互联网的设计者思考了这个问题。你会如何回答?对你来说,什么看起来像一个合理的、有用的合同?有一些相当明显的可能性。一个是,你给我网络数据,我保证将数据传送到目的地。这似乎是一个有价值、直观、有用的合同。更好的是,你可以说,我保证数据将在某个时间范围内,比如下一毫秒内交付。这实际上更有用,因为你可以围绕它构建商业模式,我们都在竞争什么。或者你可以有一个合同说,你给我你的数据,我会尝试传送它,并且我会给你一个确认,告诉你我成功了还是失败了。在互联网背景下,这可能是更明智的合同想法,因为我说过有故障。所以我会告诉你我是否成功了。互联网实际上没有采用这些中的任何一种。相反,互联网服务模型是我们所说的尽力而为服务模型。互联网保证它会尝试去做。你给网络数据,它会尝试将其交给目的地。它可能成功,也可能不成功。如果成功了,它不会告诉你它成功传送了你的数据;如果不成功,它也不会告诉你它没有成功传送你的数据。它可能会默默地丢弃。这取决于你。因此,在某种意义上,这是一个极其弱的合同。但它也是一个非常强大的合同,因为它几乎降低了你对要互连的网络期望的门槛。想象一下,如果我们说过,每个想要连接到互联网的网络必须至少有 10 Gbps 的带宽,必须有四条线路的可靠性,必须至少有公平的资源共享等等。我们可能一个网络都得不到。因为很难构建一个满足所有这些要求的网络。这肯定会提高每个网络的门槛。当然,它不可能从互连已经存在的网络开始。

所以,这是互联网设计原则的一个例子,它源于互联网的创建,现在影响着我们所有人。我想 Alex 在 Ed 上发帖说 Zoom 广播将是尽力而为的。我们实际上加入了新加坡,因为我们都知道尽力而为是什么意思。

回到互联网设计范式的问题,这些原则现在被现代系统常规采用。云计算,你可以浏览所有具有尽力而为性质的服务,它们都是围绕故障构建的。然而,需要记住的重要一点是,这仅仅是一种设计。这不是关于教条。我不是来这里用互联网架构的五条或十条原则给你们洗脑,你们必须欣赏它们,必须始终应用它们。不是这样的。这只是人们很久以前提出的一种设计。但是,它有问题。其中一些选择在现代互联网的背景下不再那么理想。例如,安全性。这个列表上实际上没有安全性。如果我们必须重新设计互联网,我们知道我们会优先考虑安全性。或者,有些东西早期就是坏主意,或者现在变成了坏主意需要改变。因此,我们——包括研究界和工业界——仍在重新审视和辩论这些重大问题。并不是每个人都在行业的边缘修修补补。我们都在实施对互联网的根本性改变。例如,你可能听说过 SDN(软件定义网络)。这个想法回来说,去中心化有其好处,但它也使管理网络变得困难。所以,如果你必须去编程单个交换机,你就不能有更高级别的抽象,比如你编程整个网络等等。因此,SDN 取得了巨大成功。它现在也有大约 15 年的历史了。所以现在有新的提议。谷歌的 Alex 正在研究一种叫做 DSSDN 的东西,它说,当我们做 SDN 时,我们失去了一些去中心化的好处。我们现在能不能不回到过去,而是从中学习,尝试获得最好的部分?还有很多其他类似的提议。NFV(网络功能虚拟化)是所有这些缩写词之一。大约五年前,这是网络领域非常热门的话题。当你开始在这个领域工作时,我们真的开始质疑这个问题:传统上,我们假设互联网基础设施是“哑”的。我的意思是,它是无状态的。网络内部不存储关于用户或应用的信息。但有很多原因,特别是如果你是 ISP,你可能希望在你的网络中有智能或智能功能。一个例子是,如果你不信任任何主机,你信任你自己的网络基础设施。所以你希望在你的网络中放置智能入侵检测或过滤等功能。因此,NFV 是一种尝试,旨在重新审视架构,以允许网络内部的智能。今天还有一个更热门的话题,那就是边缘计算。如果我们想做智能汽车、VR,我们想非常接近用户,或者出于隐私考虑,你希望数据保存在你的家中、企业或公司中。所以边缘计算应运而生。因此,再次回到重新审视互联网设计集群的问题。

还有分层,无线人员一直对我们称为分层的原则感到非常沮丧,因为他们说,如果你让我违反分层,我可以给你 10 倍的无线性能。因此,所有这些正在进行的事情都在重新审视我们将会讨论的一些设计原则。

因此,回到更高层次,互联网提出了我认为没有其他计算机系统提出的设计挑战。从其创建开始,我们作为一个领域和社区学到了一种新的方式、一种新的方法、一种构建和设计系统的新范式。这塑造了我们如何思考系统模型。不仅仅是互联网,还有任何复杂的分布式系统。当我们思考这些时,我们会推理什么是优先事项。什么是基本约束?什么是无法改变的,比如光速,与可以权衡取舍的事情。什么是抽象?我如何分解问题?这些都是我们在进行系统设计时都会做的事情,而互联网为我们如何推理这些问题提供了一个很好的模板,因为它是一个极其困难的设计问题。这就是为什么回到这个问题。

通过这类问题,这是一堂关于如何架构网络系统的课程。提出和询问这些问题,这就是我们所说的架构分布式系统的含义。当我和在工业界从事开发工作的同事或朋友交谈时,特别是那些资历较深的人,如果你问他们如何度过时间,再次强调,这些人已经在工业界工作了五年以上,他们会告诉你,他们花费超过 50% 的时间在设计讨论中,在设计会议中,与他们的同行争论这些确切的问题。他们花在编写实现该设计的代码上的时间越来越少。因此,学习如何参与、分析或参与设计讨论是极其重要的。

因此,这让我谈到了架构的最后一个话题。网络架构更多的是关于严谨地思考,而不是做严谨的数学。我不会教你复杂的数学,我自己也不懂。但我们要做的是,实际上痴迷地思考为什么我这样设计系统?为什么这是正确的设计而不是另一个?你的考试将是这样的类型:我们给你两种设计,你必须理解其中的权衡。你必须理解权衡,而不是运行基准测试,因为正如我所说,在互联网上,总是有可能得到一个告诉你你想要什么的测量结果。这更多的是关于大局,这更难做到。最终,它更多的是关于实用性而非最优性,权衡我们在设计中所有这些相互冲突的约束。

如果这样做,可以是一件强大的事情,我认为互联网的历史告诉我们这一点。因此,为了总结这部分,我希望 CS168 能教会你互联网是如何工作的。我坚信,只有当你理解了一个高级原则是如何付诸实践时,你才能真正理解它。如果你真正理解了事物是如何工作的,那么你就会理解为什么某个特定的设计选择很重要。所以你将学习所有这些首字母缩写词:TCP、IP、BGP、DNS。但我更希望你们从中带走的是,为什么这些技术以它们现有的方式工作。通过这个过程,我真的希望你们从这门课中学到的是如何推理一个复杂的分布式系统。所以,当你们出去工作,参加那些设计讨论时,因为你们在这里已经练习过了,你们将能够理解这些原则,并能够阐述和推理它们。

课程后勤与安排 📋

接下来,我们将介绍课程的后勤信息,包括教学团队、评分政策和课程大纲。

教学团队

我是 Sylvia Ratnasamy,将是本学期的讲师。我多年前在伯克利获得博士学位,本科在印度学习计算机工程,在那里我学习了晶体管和电路等知识,但幸运的是,自从来到伯克利后我就没再做过那些了。毕业后我在工业界工作了大约十年,于 2011 年回到伯克利担任教职,此后一直在这里。期间还创办了一家初创公司。我刚刚在谷歌待了一年。但无论如何,网络一直是我的工作重点,几乎从我加入伯克利博士项目开始就是如此。所以,正如你所见,我喜欢网络。

我的教学风格:我必须承认,我是一个社交型教师。如果课堂与我互动,我会教得更好。我知道我们今天没有这样做,但在未来的讲座中,我喜欢网络和系统的地方在于设计问题和进行设计讨论。所以,当课堂与我互动,当你们说“我不认为那是一个好的设计”或“你为什么说那是一个好的设计”时,我真的很喜欢。这让我不那么无聊。另外,如果你们还没意识到,我说话很快。你们看起来越无聊,我说话就越快,因为我觉得让我结束他们的痛苦吧。所以我说话越来越快。这是阻止我并提出问题的另一个原因。

助教团队:我们今年非常幸运。Sean 是首席助教,他曾是 CS162 的首席助教,所以你们很多人可能认识他。Alex,你们很多人可能不认识他,但他实际上几年前刚从伯克利获得本科学位,当时他是这门课的助教,与 Scott Shenker 一起。当我听说这个消息时,我立刻抓住了他。项目助教:我们非常幸运有 Silvery 和 Zhong,他们都是高年级博士生。他们实际上在我上次开设这门课时负责运行项目。今年我们有 Tenzin 和 Kenneth 加入项目,为运行项目增加更多力量。很高兴他们都能加入。在讨论课方面,除了 Alex 和 Sean,我们还有 Sarah、Mark 和 Nek。他们都是我和 Scott Shenker 共同领导的网络系统研究小组的研究生。拥有这么多研究生担任助教可能有点不寻常,但你们知道,我们已经两年半没有教这门课了,所以实际上很难甚至不可能找到上过这门课的本科生。因此,要向 Sean 和 Kenneth 大声致敬,他们承担了一门自己实际上没有上过的课程,也向我们所有的博士生和研究生致敬,他们从研究中抽出时间来帮助我教这门课。

注册与候补名单

班级规模不会增加。正如你们所见,我们已经是一个相当小的助教团队了。因此,候补名单上的学生将在有学生退课时被允许加入。在课程方面,我们不处理候补名单。所以请不要向我们发送关于从候补名单中移除的问题,我们无法帮助你。同时注册的学生将在候补名单之后处理。考虑到候补名单几乎有 200 名学生,我认为几率很低,所以我很抱歉。

课程录音

讲座将被录制并在线发布。我们将尽最大努力在当天晚上发布这些录音,这是一个合理的期望。有时会发生意外,所以如果没有发生,请耐心等待我们。讨论课也将被录制。我们将录制一个离线讨论课并发布。我删除了这个:我们本来打算尽力在 Zoom 上直播这个讲座,但这里的 Wi-Fi 真的很差,所以我不确定我们将如何处理这个问题。

讨论课安排

所有讨论课都在周一。这是有意设计的。希望你们能在周末浏览讲座材料,然后在周一带着所有问题去找助教。但它也有助于作为前一周内容的复习。所以当你们周二来上课时,你们已经准备好了所有材料。你们可以去任何你们想去的讨论课。

课程工作量

我被告知这门课是 2-3 个单元,属于轻度超负荷。让我们看看我们能做些什么。我们有两个项目:一个关于路由,一个关于传输设计。课后我们还有自测。每节课后我们都会发布一个自测,我稍后会详细说明。然后今年新的是,我们将尝试一个实验:我们将有一个基于阅读研究论文的家庭作业。我一直觉得有点遗憾,你们在世界上最好的研究型大学,计算机科学顶级研究机构,却很少接触研究。所以我们将阅读这篇研究论文,它实际上讲述了互联网的创建和基本原则。这是一篇很棒的论文,非常易懂。我们将在学期末阅读它,那时你们已经有了基础,然后我们会有一个关于它的家庭作业测验。看看效果如何。然后是考试:期中考试和期末考试。

评分政策

评分主要基于项目和考试,自测占 5%,家庭作业占 5%。

延期与迟交政策

所有这些都在网站上。所以我只是很快地过一遍。如果你有需要延期的理由,请填写延期申请表。Sean 将发布相关信息。对于自测测验没有延期,它们已经非常慷慨了,我稍后会详细说明。项目和家庭作业有迟交处罚,如这里所列。请注意,12 月 9 日之后提交的任何项目将不被接受。我认为这些是相当标准的政策,但如果有任何问题或疑虑,请随时提问。

自测

自测将在每节课后发布。它将在讲座当天下午 5 点前可用,你们有一周的时间填写。它们实际上非常快,课后不应该超过 10 分钟。你的分数不重要,评分不基于测验的正确性,而是基于参与度。你必须尝试。我们会采取措施确保你不是为了填表而盲目填表,而是真正尝试回答问题。目标实际上是让你和我都了解你是否跟上了进度,或者是否理解了讲座的基本概念。它有助于指导我们强调什么。它占你成绩的 5%,所以是轻松的 5%,不要错过。你可以跳过最多三个自测而不受处罚。所以在这方面非常慷慨:这是一个简单的作业,你有一周的时间

2:互联网工作原理概述 🧭

在本节课中,我们将学习互联网如何工作的基础概述。我们将从最底层的链路和数据传输开始,逐步构建对互联网整体运作的理解。课程将涵盖数据如何组织、网络资源如何共享等核心概念,并初步了解数据包在网络中的“一生”。


数据如何穿越互联网 📦

上一节我们介绍了互联网的基本任务是在终端主机之间传输数据。本节中,我们来看看数据是如何被组织并跨越互联网传输的。

首先,当终端主机上的应用程序(例如,一个图像文件)需要发送数据时,它不会一次性发送所有数据。相反,它会将数据分割成称为数据包的单元。数据包是互联网上数据传输的基本单位。

一个数据包主要由两部分组成:

  • 有效载荷:这是应用程序希望发送的实际数据(例如,图像的一部分)。网络本身通常不关心这些数据的内容。
  • 头部:这是附加在有效载荷前面的元数据,包含指导网络如何处理这个数据包的指令。你可以将头部视为网络的API,它告诉交换机如何处理这个数据包,并可能告诉下一个交换机接下来该怎么做。

数据包的大小通常不是固定的,但存在一个上限(例如,以太网的最大数据包大小约为1500字节)。因此,一个大的文件需要被分割成多个数据包进行传输。


数据包的“一生”与转发 🔄

现在我们已经了解了数据包的基本结构,让我们跟随一个数据包,看看它如何在网络中移动。

数据包通过链路从一个设备传输到另一个设备。链路有三个关键性能属性:

  • 带宽:单位时间内可以放入链路的比特数(比特/秒),类比为管道的宽度。
  • 传播延迟:一个比特从链路起点传播到终点所需的时间,由物理距离和介质决定,类比为管道的长度。
  • 带宽延迟积:带宽与传播延迟的乘积,代表链路的容量,即在不取出数据的情况下,链路上能容纳的最大比特数。

数据包通过链路的总延迟 = 传输延迟(将数据包所有比特放入链路的时间) + 传播延迟

当数据包到达一个交换机(或路由器)时,交换机的核心任务是转发数据包。它通过查看数据包头部的目的地址,查询本地的转发表,来决定应该从哪个出站链路将数据包发送出去,以使其更接近目的地。

因此,数据包的“一生”可以概括为:源主机将数据分块成带地址的数据包 -> 数据包通过链路传输 -> 到达交换机 -> 交换机查表并转发到下一跳 -> 重复最后两步,直到数据包到达目的主机。


互联网的核心挑战与主题 🎯

在理解了数据包转发的基本流程后,我们意识到其中隐藏着许多复杂的挑战。本节中,我们来看看这些挑战构成了本课程将要深入研究的核心主题。

以下是构建一个可工作的互联网需要解决的关键问题:

  • 命名与寻址:互联网上的主机如何被命名(如 google.com)?如何被寻址(如 142.250.190.78)?
  • 域名系统:如何将用户友好的名称(如 cnn.com)映射到机器可读的网络地址?这由 DNS 系统完成。
  • 路由:网络中的交换机如何计算出正确的转发表?这涉及到在由交换机和链路组成的网络拓扑图上计算路径,是一个分布式算法问题。
  • IP转发:如何高效地实现数据平面的转发操作?这需要在极短的时间内(如每10纳秒处理一个数据包)完成查表和发送。

这里引出了一个重要区分:控制平面 负责计算和维护转发表(路由算法),其操作时间尺度是网络事件(如链路故障)。数据平面 负责使用转发表实际转发每个数据包,其操作时间尺度是每个数据包的到达。


网络资源共享:统计复用 🤝

互联网需要同时支持大量用户、应用和数据流。这意味着网络资源(如链路带宽)必须被共享。本节中,我们探讨资源共享的基本模型。

资源共享的核心思想是统计复用。其原理是:不同用户或数据流对资源的需求是随时间变化的(峰值需求)。统计复用通过合并这些需求,使得在大多数时候,所有用户的总需求峰值远小于每个用户峰值需求的总和。这就像航空公司不会为每位乘客准备一架私人飞机,而是让大家共享航班座位,从而显著降低成本和提高效率。

统计复用可以在不同粒度上进行(例如,在用户粒度、进程粒度或数据包粒度)。

实现资源共享主要有两种机制:

  1. 预留:在发送数据前,终端主机向网络申请并预留特定数量的资源(如带宽)。如果预留成功,就建立了一条电路,数据可以在这条有保障的路径上传输。完成后,电路被拆除以释放资源。传统电话网络采用这种方式。
  2. 尽力而为:终端主机直接发送数据包,不预先预留。网络中的交换机会尽力转发每个数据包,但如果当前资源不足(如出站链路拥塞),数据包可能会被丢弃。互联网主要采用这种方式。

值得注意的是,电路交换分组交换(即数据包交换)都是实现统计复用的方式,只是资源分配的粒度和时机不同:电路交换在“流”的粒度上预留资源,而分组交换在“每个数据包”的粒度上即时分配资源。


总结 📝

本节课我们一起学习了互联网工作原理的底层基础。我们从数据包的结构和传输开始,理解了头部和有效载荷的作用,以及数据包如何通过链路延迟的计算和交换机的转发在网络中穿行。我们识别出了构建互联网需要解决的核心挑战:命名、寻址、DNS、路由和转发。最后,我们探讨了网络资源共享的根本必要性,并介绍了统计复用的概念及其两种主要实现机制:预留(电路交换)和尽力而为(分组交换)。在接下来的课程中,我们将对这些主题进行更深入的探讨。

3:互联网工作原理概述(续)

概述

在本节课中,我们将继续从底层视角概览互联网的工作原理,完成关于电路交换与分组交换的讨论,并开始从顶层视角审视互联网的架构。我们将探讨这两种交换方式的优劣,并引入互联网的分层架构模型。


电路交换与分组交换的对比

上一节我们介绍了网络资源共享的两种基本方法:电路交换与分组交换。本节中,我们将从几个关键维度来比较这两种方法,以理解为何互联网主要采用分组交换。

以下是评估电路交换与分组交换优劣的四个主要标准:

  1. 对应用程序的抽象:网络向应用程序开发者提供了怎样的编程接口(API)?
  2. 网络资源利用效率:哪种方式能更有效地利用昂贵的网络带宽?
  3. 故障处理能力:当网络中的链路或交换机发生故障时,哪种方式能更快速、简单地恢复?
  4. 实现复杂度:哪种方式的协议和系统设计更简单、更易于大规模部署?

应用程序抽象

  • 电路交换:为应用程序提供预留带宽的抽象。应用程序可以请求特定速率的带宽(如10 Mbps),如果网络确认,则在整个通信期间保证提供该速率。这使得性能可预测、可理解,并且便于运营商基于使用量进行计费。
  • 分组交换:为应用程序提供尽力而为的抽象。应用程序只是发送数据,网络不保证带宽、延迟或可靠性。应用程序获得的性能可能随时变化。

从应用程序开发者的角度看,电路交换提供的确定性保证通常更受欢迎。

网络资源利用效率

分组交换通常在效率上更具优势,原因在于统计复用的粒度不同。

  • 电路交换:以整个数据流为粒度进行资源预留。即使应用程序的流量是突发性的(峰值高、平均低),它也必须为整个会话预留峰值带宽。这可能导致网络资源在大部分时间未被充分利用,并可能因为预留的“碎片”无法满足新流的峰值需求而拒绝服务。
  • 分组交换:以单个数据包为粒度进行资源复用。多个突发性数据流的包可以交错传输,填充彼此的空闲时段,从而更高效地利用链路容量。对于流量平滑的应用(如传统语音通话),两者效率可能相近;但对于突发性强的数据应用(如网页浏览),分组交换的效率优势显著。

此外,对于发送数据量很小但很频繁的应用(如物联网传感器),电路交换中频繁建立和拆除连接的开销可能远大于实际数据传输的开销,效率低下。

故障处理能力

当网络路径中的某个交换机发生故障时:

  • 分组交换
    1. 网络(控制平面)检测到故障,并重新计算一条绕过故障点的路径。
    2. 终端主机无需任何操作,继续发送数据包。新路径上的交换机会自动将包转发到新路径。在路由收敛期间可能会有包丢失,但对终端透明。
  • 电路交换
    1. 网络同样需要检测故障并计算新路径。
    2. 终端主机必须参与恢复:它需要先拆除旧的预留(向原路径发送拆除消息),然后沿着新路径发起新的带宽预留请求。这个过程复杂、耗时,并且新路径可能无法满足原有的带宽预留要求。如果故障影响大量数据流,将引发“信令风暴”。

因此,分组交换在容错性方面简单且健壮得多。

实现复杂度

电路交换的实现远比分组交换复杂,核心挑战在于状态的一致性

建立一个电路预留需要多个网络设备(交换机)就“某个流是否拥有预留”达成一致。在异步、大规模、动态变化的互联网环境中,实现这种分布式共识极其复杂。需要考虑和处理各种边界情况,例如:

  • 预留请求或确认消息丢失怎么办?
  • 计时器超时与确认消息到达的竞争条件如何处理?
  • 预留被拒绝后如何协商?

虽然可以通过复杂的协议(如多次握手、确认、超时重传、状态同步)来解决这些问题,但这显著增加了系统的复杂性和开销。分组交换的核心“存储-转发”逻辑则相对简单直接。

小结与现状

综上所述:

  • 电路交换的推动力:更好的应用性能(保证带宽)、可预测性、便于计费。
  • 分组交换的推动力:更高的资源利用效率、更快的连接启动、更简单的故障恢复、更易于实现的架构。

因此,当今互联网的基石是分组交换

尽管存在如RSVP(资源预留协议)这样的协议,但它并未被广泛用于端到端的动态带宽预留。更常见的是企业购买MPLS专线,这是一种静态配置、昂贵、非统计复用的“专属管道”,用于连接分支机构或承载高优先级流量,与理想的动态电路交换相去甚远。

历史上,在90年代,人们曾认为语音和视频将成为互联网的“杀手级应用”,并因此大力推动电路交换技术。但最终,电子邮件和万维网成为了真正的驱动力。随着链路带宽的增长、编码技术的进步以及自适应应用(如可根据网络状况调整质量的视频流)的出现,分组交换互联网已能很好地支持实时媒体。


深入分组交换:队列与丢包

现在,让我们更深入地看看分组交换网络中的关键机制。考虑一个简单的场景:一个交换机有两条输入链路和一条输出链路。

  • 瞬时过载:在某个时刻,两条输入链路同时有数据包到达,都需要从同一输出链路发出。由于输出链路一次只能发送一个包,交换机需要做出决策。
  • 队列(缓冲区)的作用:交换机使用一个队列(内存缓冲区)来临时存储无法立即发送的包。它会选择一个包发送(例如,先到先服务),将另一个包放入队列。队列吸收了流量的突发性,平滑了输出。
  • 持续过载与丢包:如果输入速率持续超过输出链路容量,队列将被填满。当新包到达而队列已满时,交换机别无选择,只能丢弃这个包。这是网络丢包的主要原因之一。
  • 其他可能性:理论上,交换机可以通知上游设备暂停发送(如流量控制),或将包转发到其他可用路径。但这些方法会引入额外的复杂性和状态管理。在互联网核心,尾部丢弃(丢弃最新到达的包)是常见且简单的策略。

因此,我们需要更新对数据包生命周期的理解:

  1. 包到达交换机。
  2. 交换机可以转发包到下一跳,缓冲(放入队列)包,或者丢弃包。
  3. 重复此过程,直到包到达目的地被丢弃。

数据包的端到端延迟现在包括三部分:传输延迟 + 传播延迟 + 排队延迟


由此引入的新挑战

分组交换的“尽力而为”和“队列管理”特性引出了两个核心挑战,这也是本课程后续的重点:

  1. 可靠数据传输:既然网络可能丢包,我们如何确保应用程序的数据最终能完整、正确地送达目的地?这将是传输层(如TCP协议)的核心任务,也是你的第二个项目主题。
  2. 拥塞控制:每个终端主机独立决定发送速率。如果大家都过快发送,共享链路上的队列会溢出,导致大量丢包,性能急剧下降。如何让所有主机分布式地、自适应地调整发送速率,在保证自身性能的同时不过度占用网络资源,并充分利用可用带宽?这是拥塞控制算法要解决的问题。

互联网的顶层视角:分层架构

现在,让我们切换视角,从顶层(应用程序的角度)来理解互联网是如何组织起来的。构建复杂系统的关键方法是模块化,而互联网采用了严格的分层架构

分层设计思想

类比公司间寄信:

  • CEO(应用数据),交给助理
  • 助理 把信装入信封,写上收件CEO姓名和公司地址(本地地址),交给FedEx
  • FedEx 把信封装入运输袋,贴上路由标签(全球地址),通过其运输网络递送。
  • 到达对方公司后,过程反向进行。

每一层的对等实体(CEO与CEO、助理与助理、FedEx与FedEx)理解相同的信息格式和语义。每一层只关心自己层的封装,利用下一层的服务,并为上一层提供服务。这就是分离关注点抽象

互联网的五层模型

互联网将数据传送任务分解为五个层次,构成一个严格的层级结构(上层仅使用下层的服务):

  1. 物理层:负责在单条链路上传输原始比特流(如光信号、电信号)。
  2. 数据链路层:负责在单个本地网络(如一个以太网、一个Wi-Fi网络)内,实现节点到节点的尽力而为帧(数据包在此层的称呼)传递。它处理本地寻址(如MAC地址)、错误检测等。
  3. 网络层:负责在全球范围内,跨越多个不同网络,实现主机到主机的尽力而为数据包传递。其核心是IP协议,提供全局唯一的IP地址和路由功能。
  4. 传输层:负责在主机上的应用程序进程之间,提供可靠或不可靠的数据传输服务。它处理将应用数据分段成包、重组、可靠性(如重传)、流量控制和拥塞控制。主要协议是TCP(可靠)和UDP(不可靠)。
  5. 应用层:包含具体的网络应用程序(如HTTP、SMTP、DNS)及其协议,直接为用户提供服务。

历史注记:在早期的OSI七层模型中,还有会话层和表示层,但由于过度设计,其功能已被融入应用层,因此互联网实际广泛采用的是这个五层模型。

分层架构的优势

  • 简化设计:各层独立,定义清晰的接口。
  • 技术演进:可以更改某一层的实现而不影响其他层(例如,从以太网升级到Wi-Fi,只要数据链路层接口不变,上层无感知)。
  • 互操作性:不同厂商的设备只要遵循相同的层次协议就能通信。

总结

本节课中我们一起学习了:

  1. 电路交换与分组交换的详细对比,从应用抽象、效率、容错和复杂度四个方面分析了分组交换成为互联网主流技术的原因。
  2. 分组交换网络中的队列与丢包机制,理解了瞬时过载、持续过载以及由此产生的排队延迟和丢包现象。
  3. 分组交换带来的两大核心挑战:可靠数据传输和拥塞控制,它们将是后续课程的重点。
  4. 互联网的分层架构模型,从顶层视角将互联网分解为物理层、数据链路层、网络层、传输层和应用层,理解了这种模块化设计如何通过抽象和分离关注点来管理复杂性。

下一讲,我们将更深入地探讨传输层存在的必要性及其核心协议。

4:设计目标与历史

概述

在本节课中,我们将学习互联网架构的核心设计思想,特别是分层模型、协议栈以及“端到端原则”。我们将探讨互联网如何通过分层结构将复杂的端到端数据传输任务分解,并理解为何将某些功能(如可靠性)置于网络之外是明智的设计选择。


分层架构回顾

上一节我们介绍了互联网的五层模型。本节中,我们来看看这个模型的具体含义和逻辑通信过程。

我们通过“自上而下”的方法分解了互联网的核心任务——将数据从一个终端主机传输到另一个终端主机。最终,我们得到了一个五层任务分解模型:

  • 应用层:建立在可靠数据传输之上,处理应用程序间的通信。
  • 传输层:提供可靠的数据传输服务,将数据转换为数据包。
  • 网络层:提供全局尽力而为的数据包交付,实现端到端的路由。
  • 数据链路层:提供本地尽力而为的数据包交付,在特定链路技术(如以太网)内传输数据包。
  • 物理层:负责在物理媒介上实际传输比特。

全局交付与本地交付的区别

理解网络层(全局交付)和数据链路层(本地交付)的区别至关重要。

考虑一个由多种链路技术(如以太网、光传输网络OTN、蜂窝网络)互联而成的网络:

  • 本地交付:每种链路技术(如以太网)都有自己的寻址和路由方式,知道如何在其自身的网络范围内(例如,一个以太网段)传输数据包。但它不知道如何跨越到另一种技术(如OTN)的网络。
  • 全局交付:网络层(IP协议)的职责,就是利用这些本地交付能力,将它们“缝合”在一起,形成一条从源到目的地的端到端路径。IP协议不重新发明每种链路技术,而是知道如何与它们对接。

这正是“互联网是网络的网络”这一说法的具体体现。


协议与协议栈

上一节我们介绍了分层模型,本节中我们来看看各层之间如何通过“协议”进行通信。

协议的定义

协议是通信实体之间关于如何通信的规范或约定。它包括:

  • 语法:定义了报文(或数据包头部)的确切格式,例如字段的长度和顺序。
  • 语义:定义了接收到报文后应采取的动作和步骤。

多层协议

互联网的每一层都有一个或多个协议来实现该层的功能。协议通常由标准组织(如IETF)定义。

以下是各层常见协议的示例:

需要理解的关键点:

  1. 同一层可以有多个协议:这意味着该层功能有不同的实现方式。
    • 例如,数据链路层既有以太网协议用于以太网链路,也有OTN协议用于光传输链路。
    • 传输层既有提供可靠服务的TCP协议,也有不提供可靠性保证的UDP协议。
    • 应用层有用于网页传输的HTTP协议,也有用于时间同步的NTP协议。
  2. 对等通信:通信双方必须在同一层使用相同的协议(例如,TCP只能与TCP通信,HTTP只能与HTTP通信)。
  3. 协议栈:一个设备(如你的电脑)上实现的一系列特定协议的组合,构成了一个“协议栈”。数据在网络栈中被逐层处理。
  4. 唯一的网络层协议:在整个互联网中,IP协议是网络层唯一的核心协议。它是将所有不同类型网络和应用粘合在一起的“胶水”。(注:IPv4和IPv6是IP协议的不同版本,它们之间不能直接互操作)。

分层的重要属性与优势

分层架构有三个重要属性:

  1. 每一层依赖于其下一层提供的服务。
  2. 每一层为其上一层提供服务。
  3. 每一层只与它的直接上层和下层交互(严格分层)。

这种设计是一种模块化形式,其主要优势在于促进创新。由于层与层之间接口清晰,各层可以独立地进行创新和演进。例如,物理层开发新的光纤技术时,应用层的开发者完全无需关心。这种独立性对于由众多不同厂商参与建设的互联网至关重要。

唯一的例外是网络层(IP层),由于它的唯一性和核心地位,其演进(如从IPv4到IPv6)非常缓慢和困难。


功能在终端与网络间的划分

上一节我们讨论了逻辑上的分层,本节中我们来看看这些功能具体由谁(终端还是网络设备)来实现。

终端主机的实现

终端主机(如你的笔记本电脑、手机)必须实现全部五层。

  • 原因:它既要运行应用程序(应用层),也要能够从物理线缆上收发比特(物理层)。根据严格分层原则,它需要实现中间所有层来连接这两端。

在终端主机内部,功能通常这样分布:

  • 应用层:由应用程序自身实现。
  • 传输层和网络层:通常由操作系统内核中的网络栈实现。这样做的原因是提供通用、可复用的服务,避免每个应用都重新实现数据分片、可靠性保证等复杂逻辑。
  • 数据链路层和物理层:通常由网络接口卡及其驱动程序实现。

端口:将数据包交给正确的应用

当数据包到达目的主机后,操作系统如何知道该将它交给哪个应用程序?答案是端口

需要注意区分两种端口:

  • 物理端口:设备上连接网线的物理接口。
  • 逻辑端口:操作系统中的一个数字标识符,用于标识应用程序与网络栈之间的连接点。数据包头部中包含目的端口号,操作系统根据这个端口号将数据递交给对应的应用程序(更具体地说,是递交给与该端口绑定的套接字)。

网络设备的实现

网络设备(交换机、路由器)只需要实现下三层:物理层、数据链路层和网络层。

  • 原因:它们的核心工作是转发数据包,而不需要理解应用程序数据或提供端到端的可靠性保证。网络不实现传输层和应用层

这就形成了互联网最基本的功能划分视图:

  • 终端主机:实现全部五层。
  • 路由器/交换机:实现下三层(L1-L3)。

数据流:封装与解封装

上一节我们知道了功能在哪里实现,本节中我们来看看一个数据报文是如何实际穿越网络到达目的地的。

逻辑通信与物理路径

协议图中虚线表示的“对等通信”是逻辑通信,即双方理解相同的语言。而数据实际的物理路径是:从源主机的应用层开始,逐层向下传递,通过物理链路传输到网络设备,在网络设备中可能上到网络层进行路由决策,然后再向下传递,通过新的链路传输,最终到达目的主机,再逐层向上传递到目标应用。

封装过程

数据在源主机向下传递时,每一层都会在数据前添加自己的头部(封装):

  1. 应用层生成应用数据
  2. 传输层(如TCP)添加 TCP头部,形成数据段
  3. 网络层(IP)添加 IP头部,形成数据包
  4. 数据链路层(如以太网)添加 以太网头部,形成数据帧
  5. 物理层将帧转换为比特流发送出去。

每一层的头部都包含了该层协议对等通信所需的信息(如目的IP地址、目的端口号、目的MAC地址等)。

解封装与转发过程

数据在传输过程中:

  • 路由器/交换机上:设备根据其实现的层次查看相应的头部。例如,一个路由器会查看IP头部中的目的IP地址,查询路由表,决定从哪个接口转发出去。在转发前,它会去掉旧的链路层头部,并加上新的链路层头部(因为出接口的链路技术可能和入接口不同)。路由器不会查看TCP或HTTP头部
  • 目的主机上:数据帧被网卡接收,操作系统网络栈逐层向上处理,每层移除对应的头部,最终将原始应用数据交给目标端口对应的应用程序。

这个过程完美体现了IP协议作为“粘合剂”的角色:它利用各种链路技术的本地交付能力,通过查看IP头部进行全局路由决策,实现了跨异构网络的端到端通信。


设计哲学:端到端原则

上一节我们详细了解了数据流动的机制,本节中我们从更高视角探讨一个核心设计哲学:为什么互联网选择不将可靠性等功能内置在网络中?

问题的提出:可靠性应该放在哪里?

考虑一个简单的任务:将文件从主机A可靠地传输到主机B。这涉及多个步骤:A读磁盘、操作系统处理、网络传输、B的操作系统处理、B写磁盘。

有两种实现思路:

  1. 方案一(网络实现可靠性):在每一个步骤(包括网络中的每一段链路)都实现可靠性机制(如确认和重传),然后将这些可靠步骤拼接起来。
  2. 方案二(端系统实现可靠性):网络只提供尽力而为的服务。由主机A和主机B的应用程序进行端到端的校验(如计算并核对整个文件的校验和)。如果校验失败,则端到端地重传整个文件。

端到端原则的论证

端到端原则主张,像可靠文件传输这样的功能,正确性只能通过端到端的检查和恢复来保证。理由如下:

  • 方案一(网络可靠)的缺陷:即使网络中的每一跳都保证可靠,仍无法确保端到端的正确性。例如,主机B的应用程序可能在接收文件后、写入磁盘前崩溃;或者某个网络组件可能存在软件缺陷,导致数据静默损坏。除非你能保证所有组件(包括所有网络设备和两端主机的软件)100%正确无误,否则方案一无法提供真正的端到端可靠性。为了正确性,最终你仍然需要在端系统进行端到端的校验
  • 方案二(端到端可靠)的优势:方案二将确保正确性的逻辑放在端系统。网络可以出错(丢包、损坏数据),但这不会导致语义错误(即主机B错误地认为自己拥有一个正确的文件)。最坏情况是传输失败或延迟,但不会静默地接受一个错误文件。端系统只依赖于自身实现的正确性。

因此,端到端原则的结论是:某些功能(如确保数据传输正确性),如果端系统为了正确性最终必须实现它们,那么将这些功能内置到网络中就变得不必要,甚至有害。因为:

  • 它增加了网络的复杂性。
  • 它带来的开销(如延迟、状态维护)会施加给所有流量,即使某些应用不需要这种可靠性。

性能优化与原则的灵活性

端到端原则是一个指导性准则,而非绝对法则。有时,为了性能优化,在网络中增加一些可靠性机制是合理的。例如,如果一段链路的丢包率很高,在该链路上进行局部重传可以显著提高端到端传输的成功率和效率,从而帮助端到端的协议更好地工作。但这仍然是出于性能考虑,正确性的最终责任仍在端系统。

端到端原则的核心思想:在设计分布式系统时,应仔细考虑功能的正确性语义,并将确保该语义的责任放在能够完全、正确实现它的端点,从而保持中间网络简单、通用和高效。


总结

本节课中我们一起学习了互联网架构设计的核心内容:

  1. 分层模型:互联网采用五层模型(应用层、传输层、网络层、数据链路层、物理层)来分解复杂的通信任务,实现了清晰的关注点分离。
  2. 协议栈:每一层通过特定的协议进行对等通信。IP协议是唯一、统一的网络层协议,负责将异构网络互联。
  3. 功能划分:终端主机实现全部五层,而网络设备(路由器/交换机)只实现下三层。操作系统中的网络栈和端口机制是连接应用与网络的关键。
  4. 数据流:通过封装(添加头部)和解封装(移除头部)的过程,数据穿越各层和网络设备,IP头部指导全局路由,各链路层头部指导本地转发。
  5. 端到端原则:这是一个关键的设计哲学,主张将确保应用语义正确性(如可靠传输)的功能置于通信端点,而非网络内部,以保持网络简单并促进创新。网络内部的优化可以出于性能考虑,但不能替代端点的职责。

这种以IP为核心、严格分层、遵循端到端原则的设计,是互联网能够持续增长、适应各种新技术和新应用的根本原因。

5:路由基础

在本节课中,我们将开始探讨网络中的一个基本问题:路由。我们将了解路由器的概念、路由与转发的区别,并学习如何判断路由状态是否正确。

概述与场景设定

今天的课程计划如下:首先,我们将简要说明地址的概念,然后讨论什么是路由器以及为什么需要路由器。接着,我们将介绍路由和转发各自面临的挑战,并对两者进行比较。之后,我们将进入理论部分,学习路由的图表示法和路由状态有效性的概念,并了解如何利用这个概念来验证路由状态。最后,我们将进行一个课堂活动。

需要说明的是,路由的实现方式多种多样。我们今天的初步讨论将主要围绕典型的互联网工作方式展开,并基于此做出许多假设。下周我们将讨论一些替代方案。

地址与主机

回想两周前关于数据包头的讨论,我们说过数据包头必须包含一个特定字段。这个字段是目的IP地址,或者更广义地说,是目的地址。虽然理论上可以构建一个不需要此字段的网络,但对于所有我能轻易想到的网络技术,尤其是互联网,这确实是必需的。

这暗示着主机拥有地址。实际上,主机可能拥有多个地址。原因主要有两个:

  1. 主机可能在不同网络层拥有不同的地址(例如,L2的以太网地址和L3的IP地址)。
  2. 主机可能连接到多个网络,在每个网络上拥有不同的地址(例如,手机同时连接Wi-Fi和蜂窝网络)。

在接下来的路由讨论中,我们将进行更抽象的思考,将主机视为拥有一个单一的抽象地址。

什么是路由器?

路由器是一个中间节点,通常连接多个邻居。在网络图中,我们通常用方块或圆圈来表示路由器。

从实际设备的角度看,路由器有多种形态:

  • 家用Wi-Fi路由器。
  • 数据中心中常见的“机架顶部交换机”(如24端口或96端口的Cisco Catalyst交换机)。
  • 大型ISP中使用的高带宽、多链路的运营商级路由器(如Cisco CRS1)。

在本课程中,我们主要将它们视为方块。

为什么需要路由器?

假设我们有一些主机需要通信。

  • 全连接网络:每对主机之间都直接连接一条链路。这种方式延迟低、带宽专用、健壮性好(单条链路故障不影响其他通信),但扩展性极差(链路数量随主机数呈平方增长)。
  • 共享总线网络:所有主机共享同一根线缆。这种方式成本低、添加主机容易,但带宽共享导致竞争单点故障影响大、且难以扩展到全球规模。

引入路由器作为中间节点是一种折中方案:

  • 不需要指数级数量的链路。
  • 流量不必全部共享单一链路,带宽利用率更高。
  • 如果某条链路故障,可能找到替代路径。

路由的挑战

路由的基本挑战是:当一个数据包到达路由器时,路由器如何知道下一步应该将其发送到哪里,以确保其最终到达预期目的地?

我们希望数据包沿着“好”的路径传输。“好”的定义因上下文而异,可能意味着最低成本、最快速度、最高可靠性或最少跳数。

我们需要路由算法能够适应任意的网络拓扑,因为网络图可能千差万别(如运营商网络、校园网、企业网、数据中心网络),并且网络拓扑是动态变化的(设备故障、链路增加等)。

转发的挑战

转发是路由器在数据包到达时面临的另一个挑战。路由器需要决定将数据包重新发送给哪个邻居,并且这个决策必须非常快速(数据包到达速度可达纳秒级)。

几乎 universally 的解决方案是使用简单的表查找

以下是一个网络示例,路由器R2可能有一个如下所示的转发表:

目的地址 下一跳
A R1
B R3
C R3
D R4
E R4

例如,如果R2收到一个目的地为B或C的数据包,它会将其发送给R3。

在实际底层,路由器需要确定从哪个物理端口发出数据包,因此转发表也可能将下一跳映射到端口号。逻辑上,这两种表示是等价的。

在这种方式下,转发决策仅由数据包头中的目的地址字段和转发表中对应行的值决定。这被称为基于目的地的转发基于目的地的路由,是最基本、最常见的方式。

转发与路由的比较

总结一下路由器做的两件事:转发和路由。

  • 转发:查看数据包的目的地址,查询本地转发表,并将数据包发送到表中指定的邻居。这是一个本地操作,只涉及到达的数据包和本地表。它是路由器数据平面的主要功能,操作时间尺度在数据包到达级别(纳秒级)。
  • 路由:路由器之间相互通信,以确定如何填充转发表。这是一个全局操作,需要了解非本地信息(如目的主机的位臵)。它是路由器控制平面的主要功能,操作时间尺度较慢,通常在网络发生变化时(如链路故障)才需要更新。

路由理论:图表示与有效性

我们可以通过数据包遵循表项所走的路径来绘制图。对于一个特定的目的地,由于每个节点只有一个下一跳,所以路径会形成一个以目的地为根的有向交付树。这是一个覆盖所有节点的有向生成树,所有边都指向根节点。

我们期望路径是“好”的,而“好”的最低要求是数据包最终能到达目的地。因此,能够推理这一点很有用。

我们需要区分两种状态视图:

  1. 本地路由状态:单个路由器上转发表的内容。仅凭此无法评估其有效性。
  2. 全局路由状态:所有路由器上所有转发表内容的集合。这决定了数据包在整个网络中采取的路径。

我们说全局状态是有效的,如果它产生的转发决策总能将数据包送达目的地。路由协议的目标就是计算有效的状态。

那么,如何判断路由状态是否有效呢?对于基于目的地的转发,存在一个简洁的正确性条件,由以下定理描述:

全局路由状态是有效的,当且仅当对于所有目的地,既没有死胡同,也没有环路

  • 死胡同:数据包到达一个节点,但该节点没有对应的下一跳表项(目的地本身不计为死胡同)。
  • 环路:数据包在一组节点中无限循环。

必要性证明:如果存在死胡同或环路,数据包显然无法到达目的地。
充分性证明:假设没有死胡同和环路。数据包不能重复访问同一节点(否则是环路),也不能在到达目的地前停止(否则是死胡同)。因此,它只能在不同节点间移动。由于网络节点数量有限,它最终必然到达目的地节点。

验证路由状态有效性

如何利用这个定理来验证状态有效性?我们聚焦于单个目的地,忽略其他主机。

  1. 对于每个路由器,标记出指向其下一跳的箭头。
  2. 消除所有没有箭头的链路。
  3. 观察剩余的图。状态有效当且仅当剩余的图是一个以目的地为根的有向生成树(所有箭头指向根,无环路)。

让我们看几个例子:

  • 示例1:箭头形成一棵汇聚于目的地A的树。有效
  • 示例2:某个节点没有出箭头,是一个死胡同。无效
  • 示例3:箭头形成一个闭环,与目的地断开。无效
  • 示例4:一个节点有两条出箭头,这在基于目的地的转发中不可能发生。
  • 示例5:存在死胡同。无效。可以通过为该死胡同节点添加一个指向任何邻居的箭头来修复。

检查过程很简单:死胡同(无出箭头)和环路(与目的地断开的循环)都很明显。只需对每个目的地重复此过程,即可判断整个全局状态是否有效。

这个“无环路、无死胡同”的基本规则可以推广到任何基于固定包头进行确定性转发的系统,不仅限于基于目的地的路由。

课堂活动:分布式路由算法模拟

我们进行了一个模拟分布式路由算法的活动。规则如下:

  • 每个人初始“魔法数字”为无穷大。
  • 目标是获得尽可能低的数字。
  • 如果邻居提供给你一个比你当前数字更低的数字,你就接受它作为你的新数字。
  • 一旦你的数字更新,你立即向你的所有邻居提供“你的数字 + 1”。
  • 活动后期,我们引入了“信封”,你需要将信封传递给给你数字的那个人(你的“最佳朋友”,即数字比你小1的邻居)。

这个活动模拟了贝尔曼-福特最短路径算法的分布式异步版本。它展示了如何在分布式、异步的网络环境中,通过本地邻居间的信息交换,逐步计算出到达特定目的地(活动中的“Ian”)的“距离”(即跳数)。信封沿着计算出的“距离”梯度向下传递,最终到达目的地,这模拟了数据包沿着路由表确定的路径转发。

活动中可能出现的问题(如分区、忘记数字、更新错误、丢包、链路故障、恶意行为)也反映了真实网络中路由协议需要应对的挑战。

这类实现贝尔曼-福特风格算法的路由协议被称为距离向量协议。相邻路由器交换一个“距离向量”(即到各个目的地的距离列表)。RIP协议就是这类协议的典型代表。

总结

本节课中,我们一起学习了路由的基础知识。我们了解了路由器的角色、路由与转发的区别及其各自挑战。我们学习了用图论表示路由状态,并掌握了通过检查“无环路、无死胡同”来判断路由状态有效性的关键定理。最后,通过课堂活动,我们直观体验了距离向量路由算法(如贝尔曼-福特算法)如何在分布式网络中运作。下周我们将深入探讨距离向量协议的细节。

6:路由(二)🚦

在本节课中,我们将继续学习路由协议。我们将了解域内与域间路由的区别,深入探讨距离向量路由协议的工作原理,并学习如何处理网络中的链路故障和变化。


域内与域间路由 🌐

上一节我们介绍了路由的基本概念,本节中我们来看看互联网中路由是如何组织的。互联网并非运行一个单一的、庞大的路由协议实例。它是一个“网络的网络”,不同网络内部的最佳路由方式可能各不相同。

因此,互联网采用了一种分层思想:允许各个网络自行选择其内部的路由方式,这被称为域内路由。然后,所有网络再共同商定如何在网络之间进行路由,这被称为域间路由

  • 域内路由:在一个自治系统(AS)内部进行路由。使用的协议称为内部网关协议
  • 域间路由:在不同的自治系统之间进行路由。使用的协议称为外部网关协议,目前互联网主要使用BGP

从路由器角色来看,可以分为:

  • 边界路由器:连接不同自治系统的路由器。
  • 内部路由器:仅在一个自治系统内部工作的路由器。

最小成本路由 ⚖️

一个好的路由首先必须能将数据包送达目的地(无环路、无死路)。其次,我们希望路由是“好”的,通常通过最小化某个“成本”值来实现,这就是最小成本路由

在路由活动中,我们最小化的是跳数。在实际网络中,成本可以代表多种因素:

  • 延迟:数据包从源到目的地的实际时间。
  • 带宽:高带宽链路的成本更低(例如,成本 = 1 / 带宽)。
  • 价格:某些链路(如卫星链路)可能费用高昂。
  • 可靠性:避免不稳定的链路。

我们可以将网络拓扑视为一个带权图,每条边都有一个成本。最小成本路径就是图中路径成本总和最小的那条路径。

公式表示:对于路径 P = (v1, v2, ..., vk),其总成本 Cost(P) = Σ w(vi, vi+1),其中 w 是边的权重。路由算法旨在为每个源-目的地对找到 Cost(P) 最小的路径。

最小成本路由是基于目的地的,并且会形成一棵以目的地为根的生成树,这保证了路径无环。


简单路由与静态路由 🛣️

在深入动态协议之前,我们先了解两类基础的路由条目。

以下是两类通常由管理员配置的“简单”路由:

  1. 到自身的路由:每个路由器都知道到达自己的最佳路径就是“直接到达”,成本为0。
    • 示例目的地:自身路由器, 下一跳:直接, 成本:0
  2. 默认路由:当路由器没有更具体的路由条目时使用的“全能”路由。常见于只有单一出口的网络或主机(如你的手机优先使用Wi-Fi)。
    • 示例目的地:0.0.0.0/0 (任意), 下一跳:某个网关路由器, 成本:X

另一类是静态路由,由网络管理员手动配置。为什么需要手动配置,而不是全部依赖动态协议呢?

  • 覆盖动态协议:管理员可能有特殊策略(如流量监控、避免竞争对手网络)。
  • 连接主机:主机通常不运行路由协议。因此,与主机直接相连的路由器上需要配置指向该主机的静态路由,以告知网络该主机的存在。
    • 示例:在路由器R1上配置 目的地:主机A, 下一跳:直接(端口0), 成本:1

距离向量路由协议 🔄

现在,让我们深入探讨距离向量路由协议,它是早期网络(如ARPANET)和RIP协议的基础,其思想与Bellman-Ford最短路径算法密切相关。

回想我们的课堂活动,你模拟了一个路由器。核心思想是:

  • 每个路由器维护一个路由表,记录到每个目的地的(距离, 下一跳)
  • 路由器只与直接邻居交换信息。
  • 当从邻居收到一个路由通告(例如“我到目的地D的距离是d”)时,路由器计算:新距离 = d + 到我邻居的成本。如果这个新距离小于当前记录的距离,就更新路由表。
  • 更新后,立即向所有邻居触发更新(活动中的做法)。同时,为了可靠性和处理乱序,路由器也会周期性地广播自己的路由表。

算法核心代码逻辑(简化):

# 当从邻居 neighbor 收到到目的地 dest 的通告距离 advertised_distance 时
new_distance = advertised_distance + cost_to(neighbor)
if current_table[dest] is None or new_distance < current_table[dest].distance:
    current_table[dest].distance = new_distance
    current_table[dest].next_hop = neighbor
    # 触发更新,通知其他邻居
    trigger_update_to_all_neighbors_except(neighbor)

我们的课堂活动是这种算法的并行、分布式、异步实现。没有中心控制器,每个节点只有局部信息,通过邻居间的协作最终收敛到全局正确的路由状态。


距离向量的问题与增强 🛡️

基本的距离向量算法在实际中会遇到问题,需要一些增强机制。

1. 无穷计数问题
考虑一个简单链路故障场景:当一条最佳路径失效后,路由器可能会通过其他邻居间接获得一条实际上经过自己的“坏”路径,导致距离值不断循环增加(“计数到无穷大”)。

解决方案:毒性逆转与水平分割

  • 水平分割:如果一个路由条目的下一跳是某个邻居,那么就不要向这个邻居通告该路由。这能避免将路由信息“反射”回来源。
  • 毒性逆转:水平分割的一种变体,不是不发送,而是向下一跳邻居通告该目的地的距离为“无穷大”(例如RIP中是16)。
  • 定义最大值:设定一个最大度量值(如RIP的16跳),超过即视为不可达,从而强制停止计数。

2. 链路故障检测
依赖周期性通告来发现邻居故障可能太慢。如果邻居停止发送通告,如何知道路由失效?

  • 路由失效计时器:为每个路由条目设置一个生存时间。每次收到该路由的更新就刷新计时器。如果长时间未刷新,则认为该路由失效,将其从表中删除。
  • 结合触发更新:当检测到直连链路故障时,立即触发更新,将相关路由的距离设为无穷大,加速收敛。

处理过程示例

  1. 路由器为每个路由条目设置TTL(例如180秒)。
  2. 每次收到该路由的更新,重置TTL。
  3. 如果TTL超时,则标记该路由失效(距离设为无穷大)并触发更新。
  4. 邻居收到“无穷大”距离后,会寻找替代路径,最终网络重新收敛。

总结 📚

本节课我们一起深入学习了路由协议的核心部分。

我们首先区分了域内路由域间路由,理解了互联网分层路由的架构。接着,我们明确了最小成本路由的目标,即寻找成本总和最小的路径。

然后,我们聚焦于距离向量路由协议,你通过课堂活动亲身体验了其分布式、异步的工作原理,它通过邻居间交换距离信息来逐步构建路由表。我们还探讨了该协议面临的挑战:无穷计数问题链路故障检测,并学习了水平分割/毒性逆转路由失效计时器等关键增强机制来解决这些问题。

这些概念是理解互联网动态路由的基础,下一节我们将学习另一种重要的域内路由协议:链路状态路由协议。

7:路由协议(三)

在本节课中,我们将继续深入学习路由协议。我们将首先回顾距离矢量协议的改进,然后介绍链路状态路由协议的工作原理,最后初步了解学习型交换机和生成树协议的概念。

距离矢量协议回顾与改进

上一节我们介绍了距离矢量协议的基本原理和一些改进措施,如水平分割和毒性反转。本节中,我们来看看如何通过“毒性路由”来加速收敛过程。

当一条路由因超时而被删除时,路由器可以采取更积极的措施:不直接删除该路由,而是将其距离值设置为一个极大值(如无穷大),并立即通告给邻居。这被称为毒性路由

核心机制
当一个路由器发现其到某个目的地的路由超时后,它将该路由的度量值设置为无穷大(例如 cost = ∞),并立即发送更新。收到此“毒性”更新的邻居,如果正使用该路由器作为下一跳,则会接受这个无穷大的度量值(这是距离矢量协议中的特殊规则),从而立即将该路由标记为失效,而不是等待其自然超时。

公式表示
当路由 R 超时时:

R.cost = INFINITY
立即发送更新(目的地, R.cost)

这种方法比单纯等待超时能更快地从网络中清除失效路由,加快了协议的收敛速度。

链路状态路由协议

现在,我们转向另一类重要的路由协议:链路状态协议。与距离矢量协议“分布式计算,本地信息”的思路不同,链路状态协议采用“本地计算,全局信息”的方式。

核心思想

每个路由器都试图获取整个网络的完整拓扑信息(即所有链路的状态和所有目的地的位置),然后本地独立地计算到所有目的地的最短路径。

工作步骤

  1. 发现邻居:路由器通过周期性地发送“Hello”消息来发现直接相连的邻居。
  2. 泛洪链路状态:每个路由器将其自身的链路状态信息(包括邻居和链路开销)泛洪到整个网络。
  3. 构建拓扑图:每个路由器收集所有泛洪而来的链路状态信息,拼凑出完整的网络拓扑图。
  4. 计算最短路径:每个路由器以自己为源点,使用最短路径算法(如Dijkstra算法)计算到所有目的地的最短路径。
  5. 生成转发表:根据计算出的最短路径,确定下一跳,并填充本地转发表。

链路状态泛洪与防环

泛洪是链路状态协议分发信息的关键机制。其基本规则是:收到新的链路状态信息后,将其转发给所有其他邻居。

代码描述泛洪逻辑

def flood_link_state_packet(self, lsp, incoming_port):
    if self.has_seen(lsp): # 检查序列号,避免重复处理
        return
    self.record(lsp)
    for port in self.all_ports_except(incoming_port):
        self.send(port, lsp)

简单的泛洪在存在环路的网络中会导致数据包无限循环或指数级复制。解决方案是让每个路由器为其链路状态信息维护一个递增的序列号。其他路由器记录收到过的最大序列号,如果收到序列号更小或相等的旧信息,则丢弃它,从而避免重复泛洪。

收敛性与临时环路

即使使用链路状态协议,在网络拓扑变化期间也可能出现临时环路。原因在于,网络中各路由器感知拓扑变化并重新计算路径的时间点不同。当一部分路由器使用了新拓扑,而另一部分仍在使用旧拓扑时,它们的转发表可能不一致,导致形成临时环路。直到所有路由器都基于新的、一致的拓扑信息完成计算后,网络才会重新达到无环状态。

学习型交换机简介

最后,我们切换视角,看一种在数据链路层(如以太网)常用的、“机会主义”的转发方式:学习型交换机。

工作原理

学习型交换机不依赖持续的路由协议来建立转发表。它的转发表是通过观察流经的数据包动态学习建立的。

工作流程

  1. 学习源地址:当一个数据包从某个端口进入交换机时,交换机会记录该数据包的源地址和这个入端口的对应关系。这意味着,以后要发送数据给这个源地址,就可以从这个端口发出。
  2. 转发或泛洪:交换机查看数据包的目的地址
    • 如果在转发表中找到了对应的出端口,则直接从该端口转发。
    • 如果没有找到,则向除入端口外的所有其他端口泛洪该数据包(即广播)。

核心算法简述

当数据包从端口P到达:
1. 学习:转发表[数据包.源地址] = 端口P
2. 转发:
   if 转发表中存在 数据包.目的地址:
      如果对应端口 != P,则从该端口转发
   else:
      向除P外的所有端口泛洪

优势与根本问题

这种方法的优势是简单、自配置,无需手动设置路由。然而,其根本问题在于未知目的地的泛洪机制。如果网络拓扑中存在环路,泛洪的数据包会在环路中无限循环,造成广播风暴,耗尽网络资源。

为了解决学习型交换机在环路拓扑中的问题,我们需要引入新的机制。这将是下一节课的重点内容——生成树协议

总结

本节课我们一起学习了:

  1. 距离矢量协议的毒性路由:通过主动通告无穷大度量值来加速失效路由的清除。
  2. 链路状态路由协议:通过泛洪机制让每个路由器获得全局拓扑,并本地计算最短路径。我们探讨了其工作原理、泛洪防环机制(序列号)以及收敛期间可能出现的临时环路问题。
  3. 学习型交换机:一种通过数据包动态学习地址-端口映射的转发机制。我们了解了其基本操作流程,并指出了其在环路网络中泛洪会导致严重问题的根本缺陷。

下一讲,我们将深入探讨生成树协议,看它如何解决交换网络中的环路问题。

8:路由(三)与寻址

概述

在本节课中,我们将完成对学习型交换机和生成树协议的讨论,并开始探讨互联网寻址的核心概念。我们将了解分层寻址如何解决路由扩展性问题,并介绍IP地址的结构及其演变。


项目一更新

首先,助教Lloyd将为大家介绍项目一的相关信息。

项目一的内容是实现一个距离矢量路由协议的变体。所有项目相关材料,包括详细说明和演示,都已发布在Piazza上。请务必仔细阅读项目说明,并跟随每个阶段的演示进行操作,这将有助于你更好地理解项目内容和距离矢量协议的原理。

关于项目的任何问题,请在Piazza上提问或参加专门的项目答疑时间。常规的答疑时间将不处理项目相关问题。


学习型交换机与生成树协议

上一节我们介绍了距离矢量和链路状态协议,它们通过持续的路由消息交互来填充路由表。本节中,我们来看看一种截然不同的方法:学习型交换机和生成树协议。

在学习型交换机中,路由表是通过数据包“机会主义”地填充的,没有独立的路由消息,也无需静态路由作为种子。这种方法常用于第二层(L2)的路由(或称为交换)。

其工作原理是:当一个数据包从某个邻居到达交换机时,交换机可以从数据包的源地址学习到一条路由。如果数据包的目的地址不在表中,交换机会将数据包泛洪到除来源端口外的所有端口,期望目的主机能收到它。当目的主机回复时,其回复数据包又会沿途留下指向它的路由条目。

然而,这种方法在存在网络环路时会遇到严重问题。我们之前用于链路状态泛洪的解决方案(如序列号)在这里不适用,因为数据包可能由大量主机发送,且数据包本身可能没有序列号字段。

因此,生成树协议的解决方案是:通过禁用网络中的某些链路来消除所有环路,将网络拓扑变为一棵树。一旦网络成为树状结构,就可以安全地进行泛洪。

生成树协议主要做三件事:

  1. 找到从每个交换机到根交换机(通常是ID最小的交换机)的最低成本路径。
  2. 禁用所有不在通往根交换机最佳路径上的链路的数据传输。
  3. 当树结构断裂(如链路故障)时,重新开始这个过程。

协议的第一部分与距离矢量协议非常相似,但目标是构建一棵以根交换机为目标的单一树。每个交换机开始时都认为自己是根,并通过交换消息来发现真正的根和到达根的最佳路径。

在确定了最佳路径后,每个交换机执行以下操作来决定启用或禁用哪些链路:

  • 启用通往根交换机最佳路径上的那个链路。
  • 禁用所有通往比自身更接近根交换机(但不在最佳路径上)的邻居的链路。
  • 对于通往比自身更远离根交换机的邻居的链路,不做决定,由对方负责。
  • 启用所有连接主机的链路。

通过这个过程,网络最终形成一个无环的生成树,学习型交换机可以在这个树上安全地进行泛洪操作。


寻址

现在,让我们转向互联网路由如何扩展到全球规模的问题。其奥秘主要在于寻址,特别是IP寻址。

互联网是“网络的网络”。这种结构自然形成了一个两层级的层次结构,而层次结构是解决扩展性问题的关键工具。

我们可以设想一种寻址方案,其中地址由两部分组成:网络部分主机部分,格式类似于 网络.主机。例如,主机地址可以是 3.7,表示它在网络3中,是第7台主机。

这种分层寻址对路由扩展性带来了巨大好处:

  • 域间路由器(连接不同网络)只需要知道如何到达其他网络,而无需关心每个网络内的具体主机。这极大地减少了路由表条目和需要处理的路由更新。
  • 域内路由器需要知道如何到达本网络内的所有主机,以及一个指向边界路由器的“默认路由”或具体的外部网络路由,以便将数据包送出本网络。

边界路由器通过外部网关协议(EGP)学习到其他网络的路由,然后通过内部网关协议(IGP)将这些路由信息传播给域内的其他路由器。同时,域内路由器也通过IGP学习本网络内所有主机的路由。

IP地址与分类寻址

IP地址长度为32位,通常写作点分十进制的形式,例如 192.168.1.1

在互联网早期,地址被固定地划分为网络部分和主机部分。随着网络数量增长,出现了分类寻址

  • A类地址:首位为 0,前8位为网络号,后24位为主机号。约有126个网络,每个网络可容纳约1600万台主机。
  • B类地址:前两位为 10,前16位为网络号,后16位为主机号。约有1.6万个网络,每个网络可容纳约6.5万台主机。
  • C类地址:前三位为 110,前24位为网络号,后8位为主机号。约有200万个网络,每个网络可容纳约254台主机。

分类寻址存在两个主要问题:

  1. 地址块大小不灵活(A类太大,C类太小,B类数量不足)。
  2. 随着B类网络即将耗尽,域间路由表条目数量增长过快,给路由器带来压力。

无类别域间路由

为解决这些问题,引入了无类别域间路由。CIDR做了两件关键事情:

  1. 引入层次化的地址分配:地址分配不再是集中式的,而是形成了层级。ICANN将大块地址分配给区域互联网注册管理机构(RIR,如负责北美地区的ARIN),RIR再分配给大型ISP,ISP再分配给更小的组织或最终用户。每一级分配的都是一个连续的地址块。

  2. 放弃固定网络/主机边界:使用前缀长度来灵活表示网络部分。地址写作 基础地址/前缀长度 的形式,例如 192.168.0.0/16。这表示前16位是网络前缀,剩余16位可用于主机。这允许更精细地分配地址空间,减少了浪费。

例如,一个组织可能需要2000个地址,ISP可以分配一个 /21 的地址块(包含2048个地址)给它,而不是强迫它使用一个B类或几个C类地址。

这种灵活的、基于前缀的寻址方式,结合层次化的分配,是当今互联网能够持续扩展的基石。


总结

本节课我们一起学习了以下内容:

  • 完成了对学习型交换机和生成树协议的探讨,了解了如何通过构建生成树来解决L2网络中的环路泛洪问题。
  • 深入探讨了互联网寻址的核心思想:通过将地址划分为网络部分和主机部分,并利用层次化结构,极大地提高了路由的扩展性。
  • 回顾了IP地址从早期固定划分到分类寻址,再到如今无类别域间路由的演变过程,理解了CIDR如何通过灵活的前缀和层次化分配解决地址耗尽和路由表膨胀的问题。

下一讲,我们将继续深入探讨互联网的架构与协议。

9:翻转课堂第二讲 - 路由与交换协议详解

在本节课中,我们将深入探讨距离向量路由协议中的核心优化概念,并回顾学习型交换机和生成树协议的工作原理。我们将重点澄清一些容易混淆的术语,并通过实例帮助初学者理解这些网络基础概念。

项目一发布与提示

首先,我们有一些重要公告。从本周开始,线下课程恢复,无需再提前观看视频。此外,项目一已于今日发布,这是一个关于实现距离向量路由协议的项目。项目使用了一个由Murphy Mcauley和过去的研究生开发的模拟器,你可以直观地看到网络中数据包的流动和路由表的变化,非常有趣。

项目分为10个阶段,目标是逐步实现距离向量路由的各种优化,例如水平分割、毒性逆转和路由毒化。项目截止日期为10月7日午夜,所有信息都可在Ed平台上找到。

以下是开始项目时的一些实用提示:

  • 代码注释:由于后续阶段可能需要修改早期代码,强烈建议为早期代码添加详细注释。
  • 路径成本计算:当节点收到邻居关于到达某目的地成本为X的更新时,你通过该邻居到达目的地的总成本应为 X + 你到该邻居的成本
  • 深入理解:本课程作业不多,因此请务必通过项目彻底理解水平分割、毒性逆转等概念,这对考试至关重要。
  • 利用日志:在更新路由表时记录日志,有助于理清事件序列和理解协议行为。
  • 使用模拟器:模拟器是交互式Python终端,你可以实时查看节点的路由表,例如输入 print S1.table,这对调试非常有帮助。

路由协议的核心问题与优化

上一节我们介绍了项目概况,本节中我们来看看路由协议试图解决的核心问题。路由的核心任务是制定数据包转发规则,即路由器收到数据包后,根据报头信息决定将其发送给哪个邻居。理想的路由协议需要满足两个基本要求:全网可达性最小化延迟

网络功能分为数据平面和控制平面。数据平面负责实际的数据包处理和转发,而控制平面则负责制定转发决策。我们已学习链路状态和距离向量两种路由协议。由于项目一聚焦于距离向量,且其通过逐步添加优化来完善,更容易令人困惑,因此本节将重点讨论距离向量协议。

距离向量协议基于两个简单规则:

  1. 发言者:每个节点周期性地向所有邻居广播它到达各个目的地的距离。
  2. 倾听者:每个节点接收邻居的更新,并选择最优(成本最低)的路径来更新自己的路由表。

然而,在实际网络中,我们会遇到一些问题,导致路由状态失效。以下是三类主要问题:

  • 缺失表项:网络中存在可达目的地,但某些路由器没有对应的路由条目。
  • 路由环路:两个或多个路由器相互认为对方是到达目的地的最佳下一跳,导致数据包在它们之间循环。
  • 过时信息:网络发生故障(如链路中断)后,并非所有路由器都及时知晓。

解决路由环路:水平分割与毒性逆转

缺失表项问题通常可以通过定期重发广播来解决。接下来,我们重点讨论如何防止和消除路由环路,这正是水平分割和毒性逆转等优化所要解决的问题。

考虑一个简单的网络:R1直连目的地D,R2连接R1和R3,R3也连接R2。初始时,R2通过R1到达D,R3通过R2到达D。当R1-D之间的链路中断后,R2丢失了到达D的路由。此时,如果R3告诉R2“我可以通过某路径以成本2到达D”,R2会很高兴地接受,并更新自己的路由,下一跳指向R3,成本变为3(2+1)。随后,R2又会告诉R3它的新路由,成本可能变为4,如此循环,成本会不断递增至无穷大,形成环路。

水平分割 的规则是:如果我的某条路由的下一跳是你,那么我将不会向你通告这条路由。这样做的逻辑是:我不应该告诉你一个需要经过你才能到达目的地的路径,如果你需要从我这里获得这条路径,那说明我们已经陷入了环路。在上面的例子中,应用水平分割后,R3不会向R2通告其经过R2到达D的路由,从而在R1-D链路失效后,避免了环路的产生。

然而,水平分割存在边缘情况,例如当两个路由器都通过第三方路由器到达目的地,而第三方路由器失效时,在特定的计时情况下仍可能形成瞬时环路。一旦形成环路,水平分割需要等待路由条目超时(TTL到期)才能解除,这可能需要较长时间。

毒性逆转 是对水平分割的改进。它的规则是:如果我的某条路由的下一跳是你,那么我将主动告诉你,我到达该目的地的成本是无穷大(∞)。这本质上是一种“说谎”,目的是确保你永远不会把目的地为D的数据包发给我。在环路形成时,毒性逆转能通过立即发送“成本为∞”的更新,快速打破环路,而不必等待超时。

核心区别:水平分割和毒性逆转在预防直接环路方面效果相同。但当环路不幸发生时,毒性逆转能比水平分割更快地消除环路,因为它主动发送更新,而非保持沉默。

加速收敛:路由毒化

解决了环路问题,我们来看如何加速网络在故障后的收敛速度,即解决“过时信息”问题。网络状态变化中,“新增信息”(如新链路)传播很快,但“删除信息”(如链路中断)的传播速度受限于路由条目的超时时间,这通常比广播周期长得多。

路由毒化 旨在加速故障信息的传播。其规则是:当你失去某条路由时(例如直连链路中断),不要只是从路由表中删除它,而是立即向所有邻居广播,告知你到达该目的地的成本变为无穷大(∞)。这样,邻居可以立即更新自己的路由表,而不是等待旧条目超时。这个信息会像波浪一样在网络中快速传播开。

路由毒化 vs. 毒性逆转:虽然两者都使用“无穷大成本”这个机制,但它们触发条件和目标不同:

  • 毒性逆转:是一种预防性措施,针对特定邻居(我的下一跳)持续说谎,以防止环路。
  • 路由毒化:是一种反应性措施,在自己失去路由时向所有邻居广播,以快速清除网络中的过时信息。

概念梳理与总结

现在,我们可以回答一些常见的困惑:

可以使用哪些组合?
水平分割和毒性逆转互斥(一个要求沉默,一个要求说谎),因此对于环路防止,你只能选择其中一种或都不选。路由毒化是独立选项。所以可能的组合是:(水平分割 或 毒性逆转 或 无) 加上 (路由毒化 或 无)

哪种组合最好?
通常,使用毒性逆转(比水平分割消除环路更快)加上路由毒化(加速收敛)被认为是较好的组合。

生成树协议概览

上一节我们深入探讨了L3的路由协议,本节我们来看看L2(数据链路层)如何解决类似的问题。在学习型交换机网络中,交换机通过观察数据帧的源地址来学习主机位置,并通过泛洪来处理未知目的地的帧。然而,当网络拓扑存在物理环路时,泛洪会导致广播风暴。

生成树协议 就是为了解决这个问题。它在具有环路的物理拓扑上,通过分布式算法计算出一棵无环的“逻辑树”,并阻塞(禁用)不在树上的冗余链路。这样,泛洪仅在生成树上进行,避免了环路。协议通过选举根桥、计算最短路径等步骤构建这棵树,并在网络拓扑变化时重新计算。

课程总结

本节课中,我们一起学习了距离向量路由协议的关键优化机制。我们明确了水平分割毒性逆转都用于防止路由环路,后者在环路发生时收敛更快;而路由毒化用于在网络故障后快速传播信息,加速全网收敛。我们还回顾了L2网络中学习型交换机的工作方式以及生成树协议如何解决物理环路带来的广播风暴问题。理解这些概念的区别与联系,对于完成项目一和掌握网络基础至关重要。

10:域间路由

概述

在本节课中,我们将要学习域间路由。到目前为止,我们讨论的路由都发生在一个单一的自治系统内部。现在,我们将视角提升,探讨不同自治系统之间如何相互通信和路由数据。我们将了解什么是自治系统、域间拓扑结构如何受商业关系影响、以及设计域间路由协议(特别是边界网关协议)时需要考虑的关键目标,如可扩展性策略


什么是自治系统?

上一节我们介绍了域内路由,本节中我们来看看域间路由的基本单元——自治系统

一个自治系统是一个处于单一管理实体控制之下的网络。例如,加州大学伯克利分校的网络是一个AS,AT&T的网络是另一个AS。自治系统被分配一个唯一的自治系统号,例如伯克利的AS号是25。

从路由的角度看,我们将每个自治系统视为一个逻辑节点,用云朵图形表示。我们不再关心AS内部路由器和链路的细节,而是关注由这些AS节点相互连接构成的AS图,即域间拓扑。


自治系统的类型

自治系统主要分为两种类型,理解这种区别对理解路由策略至关重要。

  • 桩自治系统:这类AS的唯一目的是为其直接连接的用户或主机提供连接性。它不承载其他AS之间的流量。典型例子是企业、大学等非网络服务提供商。
    • 公式表示Stub AS 的功能是 F(host_traffic)
  • 中转自治系统:这类AS会代表其他AS承载流量。它们是互联网连接服务的提供者,如AT&T、Verizon等。其中,覆盖全球的顶级AS被称为第一层AS

商业关系塑造拓扑

域间拓扑的独特之处在于,它由AS之间的商业关系所塑造。两个AS之间不仅是物理连接,还存在法律合同定义的服务关系。

以下是三种基本的商业关系:

  1. 客户-提供商关系:客户向提供商支付费用以获得互联网连接。例如,伯克利是AT&T的客户。
  2. 对等关系:两个规模相当、流量对等的AS同意免费交换流量,以节省双方通过提供商中转的成本。
    • 核心概念Peering 的目标是 minimize(transit_cost)

这些关系决定了“钱流向哪里”,进而决定了哪些路径在商业上是可行的。


域间路由的目标

设计域间路由方案时,我们需要明确目标。除了域内路由也追求的有效性(无环路、无黑洞)外,域间路由还有两个核心目标:

  1. 可扩展性:需要在互联网规模(数万个AS,数十亿主机)上工作。
  2. 策略:必须允许每个AS的管理员基于其商业目标(赚钱或省钱)来实施路由策略,同时尊重其自治性隐私性

可扩展性的关键:层次化编址

要实现可扩展性,关键在于层次化的IP地址分配

在域间路由中,目的地不再是单个主机IP地址,而是代表一个网络内所有主机的IP前缀(例如 128.32.0.0/16)。这实现了第一次聚合。

更重要的是,地址的分配也是层次化的。例如,一个顶级ISP获得一个大地址块(如 A.0.0.0/8),然后将其细分并分配给自己的客户。这样,其他ISP只需存储指向这个大ISP的聚合路由条目即可,实现了第二次聚合。

  • 代码表示:路由表条目从 {具体主机IP -> 下一跳} 变为 {IP前缀 (如 128.32.0.0/16) -> 下一跳AS}

然而,多宿主(一个网络连接多个提供商)会打破编址层次与拓扑结构的对应关系,从而增加路由表规模,这是可扩展性面临的一个挑战。


策略的实施:导入与导出

策略是域间路由的核心。它通过控制路由信息的导入导出来实现。

  • 导入策略:决定从邻居AS收到的多条通往同一目的地的路由中,选择哪一条作为最佳路由。这决定了本AS的出口流量将发往何处。
  • 导出策略:决定将本AS已知的某条路由通告给哪些邻居AS。这决定了哪些入口流量可以进入本AS。

简单来说:导入策略控制流量如何离开,导出策略控制流量如何进入


Gao-Rexford 模型:常见的策略规则

研究人员Gao和Rexford总结了一套被广泛遵循的常见策略规则,为分析互联网路由提供了基线。

导入规则(选择路由时的偏好顺序)

  1. 优先选择从客户处学来的路由。
  2. 其次选择从对等体处学来的路由。
  3. 最后选择从提供商处学来的路由。
    这个顺序体现了“发送流量时,优先赚钱(发给客户),其次省钱(发给对等体),最后才考虑付费(发给提供商)”的商业逻辑。

导出规则(向谁通告路由)

  • 对于从客户学来的路由:通告给所有邻居(客户、对等体、提供商)。因为承载去往该客户的流量能赚钱。
  • 对于从对等体提供商学来的路由:只通告给自己的客户。因为承载这类中转流量无法从对等体或提供商处获得报酬,只有客户会为此付费。

遵循这些规则形成的路径被称为谷自由路径,即路径在AS图中不会出现先“下坡”(到客户)再“上坡”(到提供商)的“山谷”形状。


边界网关协议简介

边界网关协议是互联网上实际使用的域间路由协议。它基于距离矢量协议的思想,但为了支持策略,进行了关键扩展,成为路径矢量协议。

BGP与距离矢量的主要区别:

  1. 路径矢量:BGP在通告路由时,不仅通告距离,还通告完整的AS路径列表。这易于检测和避免环路。
  2. 基于策略的选择:最佳路由的选择基于复杂的本地策略(如Gao-Rexford规则),而非简单的全局度量。
  3. 选择性通告:AS可以根据策略决定不通告某些路由。
  4. 路由聚合:为了可扩展性,AS可以将多个具体前缀聚合成一个更短的前缀进行通告。

如果所有AS都遵循Gao-Rexford规则,并且AS图满足一定条件(如客户-提供商关系无环),那么BGP系统可以保证可达性收敛性


总结

本节课中我们一起学习了域间路由的基础知识。我们了解了自治系统是互联网的基本管理单元,域间拓扑由商业关系(客户-提供商、对等)塑造。域间路由设计追求可扩展性(通过层次化编址实现)和策略实施。BGP协议通过路径矢量导入/导出策略机制来实现这些目标。常见的Gao-Rexford规则为我们理解AS如何基于商业逻辑做出路由决策提供了清晰的模型。下一讲,我们将深入BGP协议的细节。

11:设计IP协议

概述

在本节课中,我们将完成对BGP协议的讨论,并转向设计IP协议。我们将探讨BGP在路由器层面的分布式实现,以及IP数据包头部(IP Header)的设计原理、字段含义和作用。


回顾BGP:从AS层面到路由器层面

上一节我们介绍了BGP在自治系统(AS)层面的工作原理,包括目的地前缀、路径通告和基于策略的路由选择。

本节中,我们来看看如何在实际的路由器网络中实现这些AS层面的策略。关键在于,AS并非一个物理实体,而是由路由器和链路组成的网络。因此,核心挑战在于如何让每个独立运行的路由器协同工作,分布式地实现BGP策略。

以下是实现分布式BGP需要解决的关键问题:

  • 目标:确保AS内的所有路由器都能执行AS层面的策略。
  • 角色:区分边界路由器(Border Router)和内部路由器(Interior Router)的职责。
  • 集成:如何将域内路由(Intra-domain routing)与域间路由(Inter-domain routing)结合起来。
  • 协议:路由器之间交换哪些具体的协议消息来实现上述目标。


BGP协议实现细节

BGP发言者与会话

在BGP术语中,运行BGP协议的路由器被称为 BGP发言者(BGP Speaker)。这通常指的是边界路由器。它们运行实现BGP协议规范的代码,能够发送、接收BGP消息并执行相应操作。

BGP发言者之间建立的连接称为 BGP会话(BGP Session)。主要有两种类型:

  1. 外部BGP会话(EBGP Session):位于不同自治系统的边界路由器之间的会话。用于学习外部网络的前缀,并向外部通告本AS的前缀。
  2. 内部BGP会话(IBGP Session):位于同一自治系统内部,边界路由器与所有其他路由器(包括其他边界路由器和内部路由器)之间的会话。用于在AS内部传播从外部学到的路由信息。

简单回顾

  • EBGP:用于AS间交换路由信息。
  • IBGP:用于在AS内部分发路由信息。
  • IGP:域内网关协议,如OSPF、RIP,用于AS内部的路由计算。

路由信息整合流程

所有部分如何协同工作?以下是整合流程:

  1. 域内路由(IGP):在AS内部运行IGP(如OSPF),计算到达所有内部目的地(包括边界路由器)的最短路径。
  2. 域间路由学习(EBGP):边界路由器通过EBGP会话学习外部目的地的IP前缀。
  3. 内部信息分发(IBGP):边界路由器通过IBGP会话,将学到的外部路由信息分享给AS内的所有路由器。
  4. 完整路由视图:此时,AS内的任何路由器都拥有到达互联网上任意目的地所需的信息。对于一个发往外部地址的数据包,路由器会进行两步查找:
    • 首先,根据目标IP前缀,通过IBGP信息确定应该使用哪个出口边界路由器
    • 然后,根据IGP信息,确定到达该出口边界路由器的下一跳

示例:假设AS 7拥有前缀 A.B/16。AS 1的边界路由器R1通过EBGP从AS 7学到该前缀。R1通过IBGP将此信息告知AS 1内的所有路由器,例如R2。现在R2知道,要到达 A.B/16,需要将流量发送给R1。同时,R2通过IGP知道如何到达R1。

BGP配置选项

在实际部署中,有多种配置BGP的方式。本课程假设采用以下方式(选项一):

  • 选项一:每个边界路由器与AS内所有其他路由器(共n台)建立IBGP会话。这需要 n * B 个会话(B为边界路由器数量),在大型网络中可能带来扩展性问题。
  • 其他选项:包括边界路由器之间建立IBGP全连接,并通过IGP注入前缀;或使用专用的路由反射器(Route Reflector) 服务器来集中管理和分发路由信息。

BGP属性与路由选择

BGP的核心智能体现在其属性(Attributes) 上。属性是与路由(前缀)关联的一组键值对,用于描述路由特性、实施策略和做出路由选择/导出决策。

以下是四个重要的BGP属性:

1. AS路径(AS Path)

  • 作用:记录到达目标前缀需要经过的自治系统序列。这是BGP作为路径矢量协议防止环路的基础。
  • 示例:前缀 128.112/16 位于AS 88。当AS 88通告它时,AS Path为 [88]。当AS 7018(AT&T)进一步向AS 25(伯克利)通告时,AS Path变为 [7018, 88]

2. 本地优先级(Local Preference)

  • 作用:用于在AS内部选择最佳路径,数值越高越优先。这是实现“客户>对等>提供商”等商业策略(Gao-Rexford规则)的主要机制。
  • 特点:该属性仅在IBGP消息中传播,不会通告给其他AS。
  • 示例:AS 7018从两个邻居AS 100和AS 200都学到了到达某个前缀的路由。如果AS 100是客户,AS 200是对等体,AS 7018可以配置边界路由器,为来自AS 100的路由分配本地优先级300,为来自AS 200的路由分配100。AS内所有路由器都会选择本地优先级更高的路径(即通过AS 100)。

3. 多出口鉴别器(MED)

  • 作用:用于向相邻AS提示,从哪个入口点进入本AS到达目标前缀更“近”(成本更低)。数值越低越优先。常用于存在多条并行链路(如东海岸和西海岸)的场景。
  • 特点:只是一个“建议”,接收方AS可以选择忽略。
  • 示例:AS Verizon向AS AT&T通告某个前缀。它在东海岸链路上通告时设置MED=10,在西海岸链路上设置MED=50,暗示AT&T从东海岸入口点发送流量更优。

4. IGP开销与热土豆路由(Hot Potato Routing)

  • 作用:当AS从多个边界路由器学到同一个外部前缀时,会选择IGP开销最小的那个出口路由器。这被称为“热土豆路由”,目的是尽快将流量送出本AS网络,以节省内部带宽。
  • 冲突:IGP开销可能与MED的建议冲突。例如,AT&T可能因为西海岸出口的IGP开销更低(热土豆),而选择忽略Verizon希望从东海岸入口(MED更低)的建议。这会导致往返路径不对称,在互联网中很常见。

BGP路由选择优先级总结

路由器在收到多个到达同一前缀的BGP路由更新时,按以下优先级顺序选择最佳路径:

  1. 最高本地优先级(Local Pref):实现商业策略(赚钱/省钱)。
  2. 最短AS路径(AS Path):优化性能(通常路径越短越好)。
  3. 最低MED值:尊重邻居AS的入口建议。
  4. 最低IGP开销到下一跳(Hot Potato):节省本AS带宽。
  5. (其他 tie-breaking 规则,如最早学到的路由等)。

BGP的局限性

在理解了BGP的工作原理后,我们来看看它的一些主要局限性:

  • 安全性:BGP缺乏对路由通告真实性的验证机制。恶意或配置错误的AS可以劫持前缀(Prefix Hijacking),声称拥有不属于它的IP地址块,或将流量引导到非预期路径。这是互联网的一个重大安全隐患。
  • 性能:基于策略的路由选择不一定选择性能最优(最短)的路径。AS路径长度可能掩盖内部跳数的巨大差异(例如,一个AS可能横跨全球)。
  • 配置复杂性:BGP配置涉及大量手动策略设置,复杂且容易出错。配置错误是导致互联网重大中断的常见原因。
  • 可达性与收敛:如果不遵循Gao-Rexford等良好实践,BGP可能无法保证所有目的地的可达性,甚至可能无法收敛,导致路由持续振荡。


设计IP协议头部

现在,我们将话题从网络层的控制平面(路由)转向数据平面,即数据包本身。本节重点在于设计IP数据包的头部。

IP头部的设计目标

可以将IP头部视为互联网的“接口”,它需要承载足够的信息,以便网络(路由器)和终端系统(目的主机)能够完成以下任务:

  1. 解析数据包:识别这是IP数据包,并确定头部和整体的边界。
  2. 转发数据包:提供足够信息让路由器将数据包向目的地转发。
  3. 交付给正确应用:让目的主机知道将数据包交给哪个上层协议(如TCP或UDP)处理。
  4. 返回响应:提供源地址,以便目的主机能够发送回复。
  5. 处理网络问题:应对传输过程中可能出现的错误,如比特损坏、环路、数据包过大等。
  6. 支持特殊处理:允许对数据包进行优先级标记或特殊操作(如记录路径)。

IP头部关键字段及其作用

基于上述目标,IP头部(以IPv4为例)包含以下关键字段:

基础字段

  • 版本(Version):标识IP协议版本(如4或6),允许协议演进。
  • 头部长度(IHL) & 总长度(Total Length):定义IP头部和数据包的总大小。
  • 源IP地址(Source IP Address) & 目的IP地址(Destination IP Address):用于路由转发和响应返回。
  • 协议(Protocol):指示IP载荷中的下一个头部是哪种协议(例如,6代表TCP,17代表UDP),以便目的主机进行多路分解(Demultiplexing)。

处理问题的字段

  • 生存时间(TTL, Time To Live):每经过一个路由器减1,减至0时数据包被丢弃。用于防止因路由环路导致数据包无限循环。
  • 首部校验和(Header Checksum):针对IP头部计算的校验值,用于检测头部在传输过程中是否发生比特错误。路由器在转发前会验证并重新计算(因为TTL变了)。
  • 分片相关字段(Identification, Flags, Fragment Offset):当数据包大小超过某条链路的MTU时,路由器需要对其进行分片。这些字段用于标识同一原始数据包的所有分片,并在目的主机进行重组。

特殊处理字段

  • 服务类型(ToS, Type of Service):最初用于指示数据包所需的QoS(如低延迟、高吞吐量),但未广泛成功应用。现在其部分比特被用作差分服务代码点(DSCP) 来标记数据包优先级,以及显式拥塞通知(ECN) 比特。
  • 选项(Options):可变长字段,用于支持一些高级功能,如记录路由、时间戳、源路由指定路径等。但由于处理开销大、对硬件不友好且可能被用于攻击,现已很少使用。

IPv4头部格式一览

一个标准的IPv4头部固定部分为20字节(若不包含选项),按32位(4字节)对齐排列,包含上述所有字段。


总结

本节课中,我们一起学习了BGP协议在路由器层面的分布式实现机制,包括BGP会话类型、路由信息整合流程以及通过AS Path、Local Pref、MED和IGP开销等属性实现复杂路由策略的方法。我们还探讨了BGP在安全、配置和收敛方面的局限性。

随后,我们转向IP协议的数据平面,深入探讨了设计IP头部的目标,并逐一解释了IPv4头部中各字段的语义和作用,包括用于寻址、多路分解、防环、检错、分片以及服务质量指示等关键功能。理解IP头部的设计是理解互联网数据包如何被处理和转发的基石。

下一讲,我们将探讨从IPv4到IPv6的演进,以及IP协议相关的一些安全问题。

12:IP 转发 (Rob Shakir - Google)

📖 概述

在本节课中,我们将要学习 IP 转发的核心概念。我们将了解路由器的基本构成、工作原理,以及它是如何在互联网中高效地转发数据包的。课程将涵盖路由器的数据平面、控制平面和管理平面,并深入探讨最长前缀匹配这一关键算法。


🏗️ 路由器是什么?

路由器是一种专门设计用于转发数据包的计算机。它的核心功能是接收数据包,根据其目的 IP 地址决定将其发送到哪个链路,并沿着正确的链路转发出去。

上一节我们介绍了路由器的基本定义,本节中我们来看看路由器在现实世界中的物理形态和部署环境。

路由器的物理形态

互联网本质上是由许多相互连接的网络组成的,而连接这些网络的正是路由器。这些路由器被部署在专门的数据中心设施中。

这些设施拥有专门的电力、冷却系统,以便放置路由器并实现互连。例如,当谷歌与康卡斯特或 Horizon 互连时,通常就在这样的建筑中进行。建筑内部是成排的机架,里面装满了用于构建互联网、连接万物的路由器。

以下是几张相对历史但具有代表性的路由器照片:

它们本质上就是为快速转发数据包而专门设计的计算机,并且有不同的大小规格。

路由器的规格参数

我们通常从几个维度来衡量路由器:

  • 端口数量:路由器外部有多少个可以与其他系统互连的端口。
  • 端口速率:每个端口能够运行的最大速度,通常称为线速。
  • 路由器容量:这是路由器整体转发能力的总和,计算公式为:
    路由器容量 = 端口数量 × 端口速率
    
    例如,一个家用路由器可能有 4 个 100 Mbps 端口和 1 个 1 Gbps 端口,其容量约为 1.4 Gbps。

在像谷歌这样的网络中,我们部署的是大型系统。例如,一个 8 槽系统,每个线卡有 36 个端口,每个端口运行在 400 Gbps,其总容量为:

288 端口 × 400 Gbps = 115.2 Tbps

随着技术发展,端口速率提升到 800 Gbps,容量也随之翻倍。

路由器容量的增长受到物理空间的限制。一个机架大约 7-8 英尺高,能容纳的设备有限。同时,功耗和散热也是关键约束。因此,容量的提升主要依赖于端口速率的增加,而非简单地增加端口数量。


🔧 路由器内部架构

了解了路由器的外部形态和规格后,本节我们来剖析其内部结构。路由器内部主要由几个关键部分组成。

核心组件

一个路由器机箱内包含两种主要类型的板卡:

  1. 控制卡:通常包含一个 x86 处理器(可能运行 Linux),负责运行控制和管理平面软件,与外部交换路由信息,并编程线卡。
  2. 线卡:专门用于数据包转发的板卡。端口(输入/输出)位于线卡上。线卡本身可能也有自己的控制处理器,用于管理本地链路状态或协议。

数据包进入输入线卡,经过处理后,通过一个内部互联网络(称为交换矩阵)发送到输出线卡,最后从输出端口发出。

三个功能平面

路由器功能通常被划分为三个“平面”:

  • 数据平面:这是我们最关心的部分,负责快速转发数据包。它主要由线卡、专用的转发芯片和交换矩阵芯片组成。
  • 控制平面:负责运行路由协议(如 BGP、OSPF),与其他路由器通信以确定网络路径,并将计算出的转发表下发到各个线卡。
  • 管理平面:负责与外部系统或人员交互,用于配置路由器(如设置 IP 地址、启用协议)和监控路由器状态(如流量统计、故障告警)。这对于实际运营网络至关重要。

所有这三个平面对于运行一个真正的路由器都是必不可少的。缺少任何一个,路由器都无法正常工作。

下图展示了一个真实路由器(Cisco 7606)的内部视图,可以清晰地看到控制卡和线卡:


📦 数据包处理流程

我们已经了解了路由器的架构,现在聚焦于数据平面,看看它是如何处理不同类型的数据包的。

路由器需要处理几种不同类型的数据包:

  1. 用户数据包:这是需要被转发的普通数据包。它们在线卡上根据已安装的路由进行查找,然后通过交换矩阵转发到输出端口。
  2. 控制平面数据包:例如,用于 BGP 会话的数据包。这些数据包是发给路由器本身的,因此会被从线卡“上送”到控制平面进行处理。
  3. 异常数据包:例如,IP TTL 过期的数据包。对于这些不知道如何处理或需要特殊响应的数据包,线卡会将其“上送”给控制平面处理。

为什么需要专用硬件?

以最小的以太网数据包(64 字节)和 400 Gbps 端口为例,每个端口每秒需要处理约 7.81 亿个数据包。一个有 36 个端口的线卡,每秒需要处理的数据包数量是天文数字。

通用 CPU 每秒只能处理数百万个数据包,这被称为“慢路径”。为了达到所需的带宽和查找速度,我们必须使用专用的转发硬件,即“快路径”。这些专用芯片牺牲了灵活性,换来了极高的速度和能效,对于控制大规模网络的成本至关重要。


🔍 核心:IP 查找与转发

数据平面最核心的任务是 IP 查找。本节我们将深入探讨这一过程,特别是“最长前缀匹配”算法。

输入线卡的处理步骤

当用户数据包到达输入线卡时,会经历以下步骤:

  1. 接收与解码:从线路上(电信号或光信号)接收并解码数据包。
  2. 链路层处理:处理以太网帧等链路层协议,检查有效性。
  3. IP 查找与转发:这是最关键的一步,决定数据包的去向。
  4. 交换矩阵交互:通过专用芯片将数据包发送到系统内的其他线卡。

转发过程本身又可分为几个阶段:

  • 解析:解析数据包,识别其是 IPv4、IPv6 还是其他封装。
  • 查找:执行实际的查找操作。
  • 动作:根据查找结果对数据包进行操作,如减少 TTL、更新校验和。

硬件中的动作

有些动作在硬件中很容易实现:

  • 减少 TTL:操作固定,易于实现。
  • 更新校验和:规则明确,易于实现。

有些则较难或我们希望避免:

  • IP 选项:灵活多变,处理耗时,通常被过滤或避免使用。
  • 分片:可以实现,但有开销。网络通常通过协商 MTU(最大传输单元)来避免分片。互联网上通用的 MTU 是 1500 字节。


🧮 最长前缀匹配

查找的核心是“最长前缀匹配”。为了理解它,我们首先需要明白为什么不能进行精确匹配。

从精确匹配到前缀匹配

理想情况下,我们希望进行精确的 IP 地址匹配。但 IPv4 有 2^32 个地址,IPv6 有 2^128 个地址,存储和更新如此庞大的精确匹配表是不现实的。

更重要的是,IP 地址具有层次结构。例如,谷歌从注册机构获得一个大地址块,然后在内部将其划分给搜索、YouTube 等部门。对于外部网络来说,它们只需要知道如何将数据包发送到谷歌,而不关心谷歌内部的具体划分。这种层次结构使得我们可以使用更紧凑的前缀路由表。

前缀 代表一组共享相同高位比特的 IP 地址。例如,前缀 192.0.2.0/24 表示前 24 位固定的所有地址。

什么是 LPM?

在互联网中,网络之间是多点互联的网状结构,而不是简单的树状结构。一个目的地可能通过多个不同长度的前缀可达。

最长前缀匹配 规则是:当数据包的目的 IP 地址匹配多个路由前缀时,选择其中 前缀长度最长(即最具体)的那条路由。如果没有匹配的前缀,则使用默认路由(如果存在),否则丢弃数据包。

LPM 查找示例

假设我们有一个简单的路由表:

端口 前缀
P1 192.0.0.0/22
P2 192.0.4.0/24
P3 192.0.5.0/24
P4 192.0.6.0/23

现在有一个目的地址为 192.0.5.10 的数据包。

  1. 将其转换为二进制。
  2. 与表中每个前缀的二进制形式进行比较。
  3. 发现它与 P2 (192.0.4.0/24) 不匹配,但与 P3 (192.0.5.0/24) 和 P4 (192.0.6.0/23) 的部分比特匹配。
  4. 应用 LPM 规则:192.0.5.0/24(长度 24)比 192.0.6.0/23(长度 23)更长、更具体。
  5. 因此,数据包应该从 P3 端口转发出去。

高效实现:前缀树

对于只有几个前缀的表,我们可以逐个比较。但互联网 IPv4 路由表有约 100 万个前缀,线性查找效率太低。

我们可以利用 IP 地址的二进制树状结构。将路由前缀组织成一棵二叉树(前缀树)

  • 树的每一层对应 IP 地址的一个比特位(0 向左,1 向右)。
  • 在每个代表有效前缀结尾的树节点上,关联一个转发动作(输出端口)。

查找时,从根节点开始,根据数据包目的 IP 的每个比特位遍历这棵树。我们需要记录沿途遇到的最后一个有效转发动作。当遍历到无法继续匹配的叶子节点或空节点时,就使用最后记录的那个动作。这保证了我们找到的是最长匹配前缀。

优化与挑战

实际路由表中,多个前缀可能指向同一个端口,我们可以利用这一点来压缩树结构,减少需要存储和查找的节点数量。

设计 LPM 算法时,我们不仅关心查找速度,还关心更新速度。因为 BGP 等协议会频繁更新路由,我们需要快速更新前缀树以反映网络状态的变化。此外,还可以利用实际部署中的特征(如前缀长度分布、热门目的地)进行启发式优化。


⏳ 超越基本转发:队列管理

除了基本的查找和转发,现代路由器还实现了更复杂的功能,如队列管理,以满足不同的业务目标。

为什么需要队列管理?

并非所有数据包都同等重要。例如:

  • 语音或视频流量对延迟敏感。
  • 不同客户可能支付了不同等级的费用。
  • 需要确保关键业务流量不被淹没。

当多个输入端口的数据流需要从同一个输出端口发出,且总速率超过端口带宽时,就会发生拥塞。此时,路由器需要将数据包暂存在内存(缓冲区)中排队等待发送。队列管理就是决定如何分类缓冲调度这些数据包。

队列管理的三个阶段

  1. 分类:根据数据包的某些特征(如入端口、IP 头中的 DSCP 字段、协议类型)将其划分到不同的队列中。
  2. 缓冲区管理:当缓冲区快满时,决定丢弃哪些数据包。简单的策略是“队尾丢弃”,但更复杂的策略如“随机早期检测”可以在缓冲区满之前就主动丢弃部分数据包,以通知发送端(如 TCP)降低速率。
  3. 调度:决定从各个队列中取出数据包发送的顺序。例如,优先级调度让语音包先出队,轮询调度公平地服务多个流。

互联网的实际情况

在公共互联网上,由于网络互不信任,通常不会使用基于数据包内容的分类和优先级。进入网络的数据包的 DSCP 等字段通常会被重置为 0,所有数据包被同等对待(“尽力而为”服务)。队列管理主要用于实现网络运营者自身的业务策略(如保证企业客户质量)或在私有网络、高级服务中。

为了简化分析,我们通常假设最简单的模型:

  • 无分类:所有数据包同等重要。
  • 队尾丢弃:缓冲区满时,新到的数据包直接被丢弃。
  • 先进先出调度:数据包按到达顺序发送。

💎 总结

本节课中我们一起学习了 IP 转发的核心知识:

  1. 路由器的本质:路由器是专门用于转发 IP 数据包的计算机,具有数据、控制和管理三个功能平面。
  2. 核心任务:其核心任务是在数据平面上对数据包目的 IP 地址执行最长前缀匹配查找,以决定转发端口。
  3. LPM 算法:LPM 利用 IP 地址的层次结构,通过查找最具体的匹配前缀来实现高效路由。前缀树是实现 LPM 的一种基础数据结构。
  4. 硬件与软件的权衡:为了实现极高的转发速度(每秒数百亿个数据包),路由器使用专用的转发硬件(快路径)。而灵活性要求高的控制和管理功能则由通用 CPU 软件(慢路径)处理。
  5. 高级功能:除了基本转发,路由器还通过队列管理实现流量分类、缓冲和调度,以支持服务质量等高级特性,但这在公共互联网上应用有限。
  6. 设计挑战:路由器设计始终在速度、灵活性、成本和功耗之间进行权衡。更多的功能通常意味着更低的转发容量或更高的成本。

理解 IP 转发是理解互联网如何工作的基石。从你家中的小路由器到谷歌全球骨干网中的庞然大物,它们都遵循着这些基本原理,将数据包准确地送达目的地。

13:可靠传输

概述

在本节课中,我们将要学习可靠传输的基本概念和机制。首先,我们会完成对IP报头的讨论,简要了解IPv6相对于IPv4的主要变化,并分析IP报头的安全影响。然后,我们将进入传输层,开始探讨如何在不可靠的网络上构建可靠的数据传输服务。这是理解TCP协议的基础,也是你第二个项目的核心内容。


IPv4到IPv6的演进 🚀

上一节我们介绍了IPv4报头的各个字段及其功能。本节中,我们来看看它的继任者——IPv6。

IPv6的设计主要源于对IPv4地址耗尽(32位地址空间)的担忧。因此,IPv6采用了128位的超长地址,极大地扩展了地址空间。目前,全球约有30%到40%的互联网在使用IPv6,各大云服务商和中国等地区都在积极部署,它终将成为互联网的主流协议。

除了地址扩展,IPv6的设计者也借此机会对报头进行了一次“清理”,优化了一些在IPv4中被认为复杂或很少使用的功能。然而,它并非一个雄心勃勃的协议,其核心设计理念与IPv4保持一致,我们所学的关于IPv4的大部分知识仍然适用。

IPv6报头的主要变化

以下是IPv6相对于IPv4报头的主要改进:

  • 地址扩展:源地址和目的地址长度从32位扩展到128位。
  • 移除校验和:因为校验工作可以完全由端主机完成,让数据包穿越网络到达接收方再检查校验和,即使出错也只是浪费一点带宽,从而减轻了路由器的处理负担。
  • 移除分片支持:IPv6将分片工作推给了发送方。如果路由器收到一个超过链路MTU的数据包,它会丢弃该包并向发送方返回一个ICMPv6错误消息,告知其路径MTU。发送方随后会发送尺寸合适的数据包。
  • 改进选项处理:IPv6报头中不再有“选项”字段。它引入了“下一个报头”字段,其作用类似于IPv4的“协议”字段,用于指示紧接在IP报头之后的是哪种报头(如TCP、UDP)。如果需要添加像源路由这样的选项,可以定义一个新的“扩展报头”(如源路由报头),并为其分配一个“下一个报头”值。这样,选项就变成了位于IP报头和传输层报头(如TCP)之间的一种扩展报头,可以通过链式结构实现多个选项。
  • 移除报头长度字段:由于选项已从主报头中移除,报头长度固定为40字节,因此不再需要该字段。
  • 新增流标签:这是一个20位的字段,用于标识属于同一“流”或相关会话的一系列数据包(例如,一次文件下载或一个网页浏览会话中的所有内容)。这为网络中需要识别相关数据包的中间设备(如防火墙)提供了明确的提示,而无需依赖临时规则(如五元组)进行猜测。

设计哲学

IPv6的设计体现了清晰的端到端原则:任何可以推给端主机完成的工作,都推给端主机(如校验和、分片)。同时,它也注重简化(移除选项和变长报头)和可扩展性(通过“下一个报头”机制)。流标签的引入则是一种巧妙的折中,允许端主机向网络传递应用语义的提示,而无需将应用逻辑嵌入网络。


IP报头的安全分析 🛡️

在了解了IPv6的改进后,我们回过头来审视一下IP报头本身存在的安全问题。互联网的原始设计并未充分考虑安全,因此网络层存在多种安全威胁。这里我们分析攻击者通过篡改IP报头字段可能发起的几种攻击:

  • IP地址欺骗:发送方可以在源IP地址字段填入任意地址,而网络不会验证其真实性。这可能导致:
    • 拒绝服务攻击:攻击者伪造大量不同源IP的数据包攻击目标,使防御者难以通过源IP过滤攻击流量。
    • 反射攻击:攻击者向许多服务器发送请求,并伪造源IP为受害者地址,导致这些服务器的响应涌向受害者,耗尽受害者的资源。
  • 服务类型字段滥用:攻击者可以为自己恶意流量标记高优先级,如果高优先级服务需要额外付费,结合IP欺骗,还可以将费用转嫁给被冒充的受害者。
  • 利用分片和选项发起资源消耗攻击:发送大量需要分片或携带IP选项的数据包,会迫使路由器进行额外的处理(分片重组、选项解析),消耗其CPU资源,可能影响其正常功能。因此,许多运营商的路由器会直接忽略或丢弃带选项的数据包。
  • 利用TTL进行网络探测:通过设置较小的TTL并分析返回的“超时”消息,攻击者可以探测到目标网络的内部拓扑结构,这正是traceroute工具的原理。有些运营商因此会配置路由器不响应TTL超时消息。

由此可见,IP报头的设计需要在效率、安全性和可演进性之间取得平衡。IPv4在这些方面做得尚可,而IPv6在某些方面(如简化设计)有所改进,但安全问题(如地址欺骗)依然存在。


可靠传输基础 🧱

现在,我们离开网络层,向上进入传输层,开始讨论可靠性。本节课我们将涵盖基本概念和机制,下节课再深入TCP协议的细节。

为什么需要可靠传输?

当你在互联网上传输文件时,你希望文件能够完整、无误地到达目的地。然而,我们构建可靠传输的基础——IP网络——只提供“尽力而为”的服务。数据包可能会丢失、损坏、乱序、延迟甚至被意外复制。因此,可靠传输的核心挑战就是在这样一个不可靠的网络上,为上层应用提供可靠的交付抽象。

根据端到端原则,可靠性是端主机(而非网络)的职责,通常由操作系统内核中的传输层实现,这样应用开发者就无需自己处理复杂的可靠性问题。

可靠传输的目标

我们的设计目标有三个:

  1. 正确性:接收方至少收到每个数据包一次(at-least-once),且内容正确(通过校验和保证)。
  2. 及时性:传输不应无休止地等待。
  3. 效率:避免不必要的重传,充分利用网络带宽。

值得注意的是,可靠性协议允许在尝试足够次数后宣告失败(例如,网络永久分区),但必须向上层应用明确报告失败,绝不能谎报成功。

单数据包传输的基石

我们先从最简单的场景开始:如何可靠地传输一个数据包?这揭示了可靠传输所需的基本构件。

以下是处理各种网络问题的核心机制:

  • 应对丢失:超时与重传

    • 发送方发送数据包后启动一个定时器
    • 如果在定时器到期前收到接收方返回的确认,则取消定时器,传输成功。
    • 如果定时器到期仍未收到确认,发送方重传该数据包。
    • 问题:如果确认包丢失,发送方会超时重传,导致接收方收到重复的数据包。这是“至少一次”交付语义的必然结果,因为发送方无法区分是数据包丢失还是确认包丢失。
  • 应对损坏:校验和

    • 接收方计算数据包的校验和,并与发送方提供的校验和比对。
    • 如果校验失败,数据包被损坏,接收方可以:
      1. 直接丢弃(什么也不做),依赖发送方的超时重传机制。
      2. 发送一个否定确认(NACK),明确告知发送方数据包损坏,促使其立即重传。TCP协议没有采用NACK。
  • 应对延迟和重复

    • 延迟:如果数据包或确认在超时前到达,则没有问题。如果在超时后到达,会导致不必要的重传和重复确认,但仍在“至少一次”的语义范围内。
    • 重复:网络设备故障可能导致数据包被意外复制。接收方会收到重复包并发送重复确认,发送方处理重复确认即可,机制上可以兼容。

小结:通过校验和、确认(ACK/NACK)、超时重传这些构件,我们能够可靠地传输单个数据包。其语义是接收方可能收到重复包。


多数据包传输与滑动窗口 🪟

单数据包传输效率极低。为了提升效率,我们需要同时传输多个数据包。

从“停等”到“滑动窗口”

最简单的多包协议是停等协议:发送方必须等待前一个数据包的确认到达后,才能发送下一个。这导致链路利用率极低,每轮往返时间只能发送一个包。

改进方法是允许发送方有多个数据包在传输中,即采用滑动窗口协议。

  • 窗口大小:用一个参数 W 表示允许的未确认数据包最大数量。
  • 滑动机制:发送方维护一个窗口,初始包含序列号1到W的数据包。每收到一个确认,窗口就向前“滑动”一格,允许发送下一个新的数据包。
  • 目标:通过让发送方持续发送数据,来“填满管道”,充分利用网络带宽。

如何设置窗口大小?

理想情况下,我们希望发送方在整个往返时间内都保持发送状态。这引出了带宽时延积的概念:

理想窗口大小 (W) = 往返时间 (RTT) × 瓶颈带宽 (B)

窗口大小(以字节计)应至少等于带宽时延积,才能充分利用网络容量。设置过小会浪费带宽,设置过大会导致拥塞(后续课程讨论)。

确认信息的类型与权衡

当有多个数据包在传输中时,确认信息的设计变得重要。主要有三种类型:

  1. 独立确认:收到数据包i,就回复ACK i

    • 优点:简单、紧凑。
    • 缺点:对ACK丢失不健壮,任何ACK丢失都会导致超时重传。
  2. 完全信息确认:确认中携带接收方已收到的所有数据包序列号信息(例如,“已收到1, 2, 4, 5”)。

    • 优点:信息完整,对ACK丢失非常健壮。
    • 缺点:确认包可能变得非常大,开销大。
  3. 累积确认:确认中只携带一个序列号N,表示接收方已收到所有序列号小于等于N的数据包。

    • 优点:紧凑,对单个ACK丢失有一定健壮性(后续的累积ACK能弥补前面ACK的信息)。
    • 缺点:信息不完整。当发生多个数据包丢失时,发送方无法从重复的累积ACK中精确判断具体丢失了哪些包,只能知道有包丢失。

TCP协议主要使用累积确认,因为它平衡了开销和健壮性。后来通过选择性确认扩展来弥补其信息不完整的缺点。

基于确认的丢包检测

有了持续的确认流,我们可以比单纯依赖超时更早地检测丢包。一个常见的启发式规则是:如果发送方连续收到K个(例如3个)对更高序列号数据包的确认,而仍未收到对某个预期数据包X的确认,则可以推断数据包X很可能丢失了。

  • 对于独立确认完全信息确认,这个规则很清晰,能明确知道哪个包丢了。
  • 对于累积确认,这个规则表现为收到K个重复的累积ACK(即ACK号不变)。这能告诉发送方有数据包丢失,但无法精确指出是哪个(当有多个包丢失时)。发送方需要进行一定的猜测。

响应丢包

检测到丢包后,自然要重传。对于独立或完全信息确认,重传哪个包是明确的。对于累积确认,在多个包丢失的场景下,需要结合其他机制(如TCP的“快速重传”和“选择性确认”)来决定重传策略,避免不必要的重传。


其他可靠传输思路 💡

除了这种基于确认、重传的“自动重传请求”模型,还有其他设计可靠传输的思路:

  • 无确认的重复发送:简单地反复发送所有数据,直到接收方通知已收全。非常低效。
  • 前向纠错编码:利用编码理论,发送方发送的并不是原始数据包的副本,而是包含冗余信息的编码数据包。只要接收方收到足够数量(可能少于发送总数)的编码包,就能通过解码恢复出原始数据。这种方法无需确认和重传,能容忍一定丢包率,常用于流媒体等场景,但尚未成为互联网传输层的主流。

总结

本节课我们一起学习了可靠传输的基础构建模块。我们首先回顾了IPv6对IPv4报头的简化和改进,并分析了IP报头存在的安全漏洞。然后,我们深入探讨了在不可靠网络上实现可靠传输的核心机制:校验和、序列号、确认、定时器、重传以及滑动窗口。我们比较了不同类型的确认信息(独立、完全信息、累积确认)的优缺点,并了解了如何利用确认流来更高效地检测丢包。这些概念是理解下一讲内容——传输控制协议(TCP)——的基石。TCP正是综合运用了这些构件,并加入了拥塞控制、流量控制等复杂机制,成为了互联网可靠的传输层支柱。

14:期中复习

在本节课中,我们将回顾前半学期所学的核心概念,涵盖从底层链路到高层协议设计的全部内容。我们将快速梳理关键知识点,帮助你为考试做好准备。

考试概览与指南

首先,我们明确考试的范围和形式。期中考试将涵盖目前为止所有讲座中涉及的内容,但不包括最近一次关于可靠性的讲座(即超时重传机制)。

考试将基于讲座内容,教材可作为辅助理解的参考资料。如果教材与讲座内容有出入,请以讲座为准。

考试中不会有复杂的数学计算或冗长的运算。如果你发现自己陷入了复杂的计算,很可能思路有误。

考试旨在测试你对概念的理解和应用能力,而非死记硬背。你可能会遇到将课堂上学到的算法(如距离向量)应用到新场景或陌生拓扑结构中的问题。

考试格式通常包括约25道快速问答题和几道针对特定主题的深入分析题。题目难度可能递增,请注意合理分配时间。

请保持自信,你已经掌握了解决问题所需的所有知识。如果遇到难题,不要慌张,冷静分析。

网络基础:从链路到路由器

我们从最基础的链路开始。你需要了解链路的关键属性:

  • 带宽:链路传输数据的能力。
  • 传播时延:信号在链路上传播所需的时间。
  • 带宽时延积:链路的容量,表示链路上能容纳的“在途”数据量。

基于这些属性,你应该能够计算数据包从一点传送到另一点所需的总时间,并理解其组成部分:

  • 传输时延:将数据包所有比特推送到链路上所需的时间。公式为:数据包大小 / 带宽
  • 传播时延:比特在物理链路上传播所需的时间。
  • 排队时延:数据包在路由器缓冲区中等待处理的时间。

路由器将多条链路互连,形成网络拓扑。在罗伯的讲座中,我们深入探讨了路由器的内部结构。

理解路由器内部运作的关键是区分控制平面数据平面

  • 数据平面(快速路径):负责数据包的快速转发。这通常由硬件实现,处理固定功能,如检查IP头、更新TTL、计算校验和、执行IP查找并转发数据包。
  • 控制平面(慢速路径):运行在控制处理器上,负责计算路由表(通过运行BGP、距离向量、链路状态等路由协议),并处理需要复杂操作的数据包(如带有选项的数据包)。

数据平面追求极致的性能,因此像分片、选项处理这类复杂操作对其压力很大。

路由基础与域内路由

网络的核心挑战之一是如何进行路由。我们首先讨论了有效路由状态的含义:即对于任何目的地,都没有死胡同和路由环路。

接着,我们引入了链路成本的概念,并探讨如何计算最小成本路径。成本度量通常是链路延迟,如果未指定,则默认成本为1。

除了动态路由,还有静态路由(由网络管理员手动配置)和默认路由(用于处理所有未明确路由的目的地,通常用于只有单一出口的网络)。

在域内路由中,我们学习了三种主要方法:

距离向量路由

这是一种分布式异步协议。每个路由器向其邻居通告它到所有目的地的最小成本距离,并监听邻居的通告,以此计算自己的路由表。

你需要掌握其详细组件:

  • 通告规则:当听到一个新目的地、或听到一个成本更低的路由、或从当前下一跳听到距离变更时,更新路由表。
  • 周期性与触发更新
  • 收敛:网络变化后,所有路由器重新计算路由直至达到稳定状态的过程。
  • 计数到无穷大问题:以及如何通过设置最大度量值(如16跳)来避免。
  • 优化收敛的技术
    • 水平分割:不向下一跳路由器通告通过它到达目的地的路由。
    • 毒性逆转:主动向下一跳通告到目的地的成本为无穷大。
    • 路由毒化:当路由失效时,立即向邻居通告其成本为无穷大。

链路状态路由

这种方法与距离向量相反。每个路由器通过泛洪机制收集整个网络中所有链路的状态信息,从而构建出完整的网络拓扑图,并独立计算到所有目的地的最短路径。

关键点包括:

  • 泛洪的可扩展性:通过序列号机制避免重复泛洪同一链路状态通告。
  • 收敛期间的问题:尽管链路状态收敛较快,但在收敛期间仍可能存在问题。

生成树协议与学习型交换机

这种方法用于实现“即插即用”的网络,无需手动配置主机位置。交换机通过观察数据包的源地址来学习主机位置(学习型交换机)。对于未知目的地的数据包,则进行泛洪。

为了避免泛洪引起环路,需要先使用生成树协议构建一个无环的树状拓扑,数据包只在这个生成树上泛洪。

STP构建生成树的过程类似于距离向量,但同时确定树的根节点和到根的路径。

域间路由与BGP

单个网络域被称为自治系统。AS内部使用内部网关协议(如距离向量、链路状态),而AS之间则需要外部网关协议,即边界网关协议

AS分为不同类型:

  • 传输AS:为不属于本AS的源和目的地承载流量。
  • 桩AS:只包含本AS内部的目的地。
  • 一级传输提供商:位于互联网顶层,彼此对等互联,没有上游提供商。

域间路由的核心挑战是可扩展性策略

可扩展性主要通过层次化IP地址地址聚合来实现。IP地址按前缀进行路由,这导致了最长前缀匹配的查找需求(例如,目的地址192.168.1.1匹配前缀192.168.1.0/24,而非192.168.0.0/16,因为前者前缀更长)。

策略源于AS之间的商业关系(客户-提供商、对等)。核心原则是“路由跟随金钱”。策略通过BGP的路由导入路由导出规则实现:

  • 导入策略(选择路由):通常优先选择客户发来的路由,其次是对等方,最后是提供商。
  • 导出策略(通告路由)
    • 从客户学来的路由:通告给所有邻居(客户、对等、提供商)。
    • 从对等或提供商学来的路由:只通告给自己的客户(不会通告给其他对等或提供商,因为不想免费为它们传输流量)。

BGP通过路径属性来实现策略和路径选择,重要的属性包括:

  • AS路径:记录路由经过的AS序列,通常优先选择路径最短的。
  • 本地优先级:由管理员手动设置,用于影响本AS内的路由选择偏好。
  • MEDIGP成本:用于在多个出口中选择,以优化AS内部带宽使用。

在AS内部,边界路由器通过eBGP与其他AS交换路由,并通过iBGP将这些路由传播给内部路由器。内部路由器同时运行IGP(如OSPF)来学习AS内部拓扑,以知道如何将数据包送达边界路由器。

互联网架构设计原则

在理解了“如何做”之后,我们回顾一下互联网“为何如此设计”的根本原则。

尽力而为服务与统计复用

互联网采用尽力而为的服务模型:发送数据包,但不保证一定送达。这建立在统计复用的基础上,即所有用户共享网络资源,这比为每个用户预留固定带宽(电路交换)效率高得多,成本也更低。

分层与协议

通过分层将复杂的端到端通信任务分解。我们采用五层模型:

  1. 应用层
  2. 传输层(如TCP/UDP)
  3. 网络层(IP)
  4. 链路层(如以太网)
  5. 物理层

每一层通过协议进行通信。关键特性是严格分层网络层唯一性(全球统一的IP协议)。这种模块化设计促进了并行创新。

端到端原则

该原则指导功能应在何处实现。核心思想是:如果某个功能(如可靠性)无法在网络上完全正确地实现(因为最终仍需端系统确认),那么就应该在端系统实现,网络至多提供优化。

因此,在互联网中,端系统实现了全部五层,而网络内部(路由器)只实现下三层(网络层及以下)。

数据包封装与IP头

数据在发送时,从上到下每层添加各自的协议头(封装);接收时,从下到上每层移除并处理对应的头(解封装)。

IP头的设计集中体现了网络层的功能与权衡。你需要熟悉IPV4头各字段的作用(如TTL防环、校验和检错、源/目的地址等)。通过对比IPV6(取消了头部校验和、分片等字段),可以理解哪些设计是成功的,哪些带来了问题。


本节课中,我们一起回顾了互联网架构的核心内容:从底层的链路、路由器,到域内(距离向量、链路状态、STP)和域间(BGP、策略、地址聚合)路由协议,最后回归到互联网根本的设计原则(尽力而为、分层、端到端原则)。希望这次复习能帮助你梳理知识体系,祝你在期中考试中取得好成绩!

15:传输控制协议 (TCP) 🚀

在本节课中,我们将要学习传输层的作用,并深入探讨传输控制协议(TCP)的具体设计。我们将从传输层的通用功能开始,然后聚焦于TCP如何实现这些功能,包括其可靠字节流抽象、连接管理、流量控制等核心机制。


传输层概述

上一节我们介绍了网络层,本节中我们来看看位于网络层和应用层之间的传输层。传输层在端主机(通常在操作系统中)实现,其核心作用是弥合应用层期望的通信抽象与网络层提供的基础服务之间的差距。

与网络层不同,传输层可以同时运行多种协议(如TCP、UDP),不同的应用可以同时使用不同的传输协议,但它们都共享底层的IP层。

将传输层构建在操作系统中,使其能为所有应用提供公共服务。如果没有像TCP这样的传输层,每个应用都需要自行处理网络层带来的所有复杂性和不确定性。

传输层的核心功能

以下是传输层需要解决的几个核心问题:

  1. 多路分解:当数据从网络到达时,如何确定将其传递给哪个应用进程。
  2. 可靠性:确保数据在不可靠的网络上可靠地传输。
  3. 抽象转换:将网络层的“数据包”抽象转换为应用层更易理解的抽象(如字节流)。
  4. 避免接收方过载:控制发送速率,防止接收方缓冲区溢出。
  5. 避免网络过载:控制发送速率,公平地与其他数据流共享网络资源。

接下来,我们将逐一探讨这些功能,并了解TCP如何实现它们。

多路分解与端口

多路分解的核心问题是:当数据包到达主机后,如何确定将其交给哪个应用进程。

解决方案是使用端口。端口是一个16位的逻辑标识符,用于标识应用进程与操作系统通信的端点。端口号携带在传输层(如TCP或UDP)的头部中。

当数据包到达时,操作系统根据IP头中的协议字段(如值为6代表TCP)将其交给相应的传输层协议处理。传输层协议再根据头部中的目的端口号,将数据传递给监听该端口的应用进程。

可靠性

关于可靠性的基本概念和构建模块(如校验和、确认、超时重传、序列号、窗口等),我们在上一讲中已经详细讨论过。TCP将利用这些组件来实现可靠的数据传输。

应用层抽象

应用开发者不希望处理数据包丢失、重复、乱序或损坏等底层网络问题。因此,传输层需要提供一个更高级的抽象。

最常用且直观的抽象是可靠的、有序的字节流。发送方将字节按顺序放入一个“管道”,接收方从管道另一端按相同顺序取出所有字节。TCP就提供了这样的抽象。

另一个常见的抽象是不可靠的单条消息传递,由UDP协议提供。它只是简单地将一个数据报发送出去,不保证可靠性或顺序性。这对于那些可以容忍少量丢失、数据量小或对延迟极其敏感的应用(如IoT传感器、实时音视频)非常有用。

流量控制:避免接收方过载

在可靠性讨论中,我们引入了“窗口”的概念,它决定了发送方在未收到确认前可以发送多少数据。窗口大小的设定需要平衡三个目标:充分利用网络容量、不使接收方过载、不使网络过载。

本节我们先看第二个目标:流量控制

为什么接收方可能过载?因为TCP提供有序交付。如果数据包乱序到达,接收方必须将后续到达的数据包缓存在缓冲区中,直到缺失的包到达后才能按序提交给应用。接收方缓冲区是有限的。

解决方案是:接收方动态地告知发送方自己还有多少可用的缓冲区空间。在TCP中,这个值称为通告窗口,它被放在每个确认包的头部中发送给发送方。发送方则确保自己已发送但未确认的数据量不超过接收方通告的窗口大小。

拥塞控制:避免网络过载

第一个目标(填满管道)给出的窗口大小上限是:带宽延迟积(BDP),即 窗口 ≤ 往返时间(RTT) × 瓶颈链路带宽。但这假设了该数据流独占整个瓶颈链路。

现实中,网络由许多流共享。因此,每个流应该只占用其“公平份额”,而不是全部容量。确定这个公平份额是一个复杂的问题,这就是拥塞控制算法的任务。

我们可以将拥塞控制算法视为一个“黑盒”,它运行在发送方,根据网络状况动态计算出一个值,称为拥塞窗口。发送方最终的可用窗口大小,是接收方通告窗口和自身拥塞窗口中的较小值。

公式发送窗口 = min(通告窗口, 拥塞窗口)

拥塞控制将是后续多节课的重点。

窗口大小决策总结

回顾一下,发送方窗口大小受限于三个因素:

  1. 网络容量:RTT × 瓶颈带宽(理论最大值)。
  2. 接收方能力:通告窗口
  3. 网络公平性:拥塞窗口

实践中,由于难以精确获知瓶颈带宽,且拥塞窗口通常小于带宽延迟积,因此发送窗口由后两者决定。

公式实际发送窗口 = min(通告窗口, 拥塞窗口)


传输层协议:UDP简介

在深入TCP之前,简单了解下UDP。用户数据报协议(UDP)是一个非常轻量级的传输层协议。它只提供最基本的多路分解(通过端口)和可选的校验和,不提供可靠性、顺序性、流量控制或拥塞控制。

UDP适用于那些需要低开销、能容忍丢包或自身实现了简单重传机制的应用。


深入TCP 🧩

TCP由Vinton Cerf和Bob Kahn等人设计,是互联网的基石。它之所以重要,不仅因为绝大多数互联网应用都运行在TCP之上,还因为它体现了优秀的工程设计和细节关注。

TCP抽象:可靠的、有序的字节流

TCP向应用层提供“可靠的、有序的字节流”抽象。这意味着应用看到的是一个连续的字节流,所有字节都会按发送顺序可靠地送达。

实现上,发送方将应用数据分割成一个个TCP段进行发送。每个TCP段都包含一部分字节流数据和一个TCP头部。

TCP段被封装在IP数据包中。TCP段的最大长度受限于链路的最大传输单元(MTU),计算公式为:

公式最大段大小(MSS) = MTU - IP头长度 - TCP头长度

序列号与确认号

TCP的关键在于使用字节偏移量进行标识,而不是数据包编号。

  • 序列号:标识该TCP段中第一个数据字节在整个字节流中的偏移量。
  • 确认号:接收方用它来确认数据。其含义是“期望收到的下一个字节的序列号”,即接收方已正确接收该序号之前的所有字节。

初始序列号:每个TCP连接开始时,会随机选择一个初始序列号(ISN)。连接的第一个数据字节的序列号是 ISN + 1。使用随机ISN主要是出于安全考虑,防止旧连接的残留数据包被误认为是新连接的数据,或防止序列号预测攻击。

TCP是面向连接的

为了实现可靠字节流,TCP需要在通信两端维护连接状态(如序列号、缓冲区、定时器等)。因此,TCP是面向连接的协议,需要在数据传输前显式地建立连接,在传输后拆除连接。

此外,TCP连接是全双工的,意味着双方可以同时向对方发送数据。一个方向的数据包可以“捎带”另一个方向的确认信息。


TCP头部格式与功能实现

现在,我们结合TCP头部格式,来看TCP如何具体实现传输层的各项功能。

TCP头部结构

一个TCP头部包含以下关键字段(对应下图):

  1. 源端口 & 目的端口:各16位,用于多路分解。
  2. 序列号:32位,该段数据的第一个字节的序列号。
  3. 确认号:32位,期望收到的下一个字节的序列号。
  4. 头部长度:4位,以4字节为单位,指示TCP头部的长度。
  5. 标志位:6个控制位,包括URG、ACK、PSH、RST、SYN、FIN。
  6. 窗口大小:16位,即接收方的通告窗口,用于流量控制。
  7. 校验和:16位,用于差错检测。
  8. 紧急指针:与URG标志位配合使用,指示紧急数据的位置(较少使用)。
  9. 选项:可变长度,用于扩展功能(如MSS、窗口缩放因子、SACK等)。

多路分解

IP层根据协议字段(值6)将数据包交给TCP模块。TCP模块再根据目的端口号,将数据传递给相应的应用套接字。

可靠性实现

TCP使用我们讨论过的所有可靠性组件,但有其特定设计:

  • 序列号与确认:基于字节偏移量,使用累积确认
  • 滑动窗口:发送方维护一个窗口,窗口左侧是已确认的字节,右侧是当前允许发送的最大字节序号+1。窗口大小受限于min(通告窗口, 拥塞窗口)
  • 超时重传:为窗口中最老的已发送未确认字节设置一个定时器。超时则重传。
  • 快速重传:如果收到3个重复的ACK,TCP会认为数据丢失,在超时前立即重传。
  • 超时估计:通过测量样本RTT,动态估算往返时间,并据此设置超时值。

流量控制实现

接收方通过头部中的窗口大小字段,向发送方通告其当前可用的缓冲区空间(即通告窗口)。发送方据此调整其发送窗口的右边界,确保不会淹没接收方。

连接管理:三次握手与四次挥手

连接建立:三次握手 👐

TCP使用三次握手来建立连接,同步初始序列号,并交换其他参数(如MSS)。

  1. SYN:客户端发送一个SYN包(SYN=1)到服务器,并选择一个初始序列号 seq = ISN_c
  2. SYN-ACK:服务器收到后,回复一个SYN-ACK包(SYN=1, ACK=1)。该包包含:
    • 对客户端SYN的确认:ack = ISN_c + 1
    • 服务器自己的初始序列号:seq = ISN_s
  3. ACK:客户端收到SYN-ACK后,发送一个ACK包(ACK=1)进行确认:ack = ISN_s + 1

至此,连接建立完成,双方可以开始传输数据。

连接终止:四次挥手 👋

TCP使用四次挥手来优雅地关闭一个全双工连接。

  1. FIN:当一方(如客户端)完成数据发送后,发送一个FIN包(FIN=1)。
  2. ACK:另一方(服务器)收到FIN后,发送一个ACK包进行确认。此时,从客户端到服务器的连接方向关闭,但服务器到客户端的连接仍然可用。
  3. FIN:当服务器也完成数据发送后,它发送自己的FIN包。
  4. ACK:客户端收到服务器的FIN后,发送ACK确认。

客户端在发送最后一个ACK后,会等待一个时间周期(2MSL),然后才完全关闭连接,以确保网络中所有残留的旧数据包都已消失。

连接重置:RST

除了优雅关闭,TCP还支持使用RST标志位进行强制重置。任何一方都可以发送一个RST包来立即终止连接。这通常用于处理异常情况,例如收到一个不属于任何现有连接的数据包,或者需要立即中止连接时。

TCP状态机

TCP连接在其生命周期中会经历一系列状态。下图展示了一个简化的TCP状态机(以主动发起连接的一方为例):

  1. CLOSED -> SYN_SENT:应用发起连接,发送SYN。
  2. SYN_SENT -> ESTABLISHED:收到SYN-ACK,发送ACK。
  3. ESTABLISHED:在此状态下进行数据传输。
  4. ESTABLISHED -> FIN_WAIT_1:应用关闭连接,发送FIN。
  5. FIN_WAIT_1 -> FIN_WAIT_2:收到对FIN的ACK。
  6. FIN_WAIT_2 -> TIME_WAIT:收到对方的FIN,发送ACK。
  7. TIME_WAIT -> CLOSED:等待2MSL超时后关闭。


总结

本节课中我们一起学习了传输层,特别是TCP协议。我们了解到:

  • 传输层是应用层和网络层之间的桥梁,提供多路分解、可靠性、抽象转换、流量控制和拥塞控制等服务。
  • TCP提供可靠的、有序的字节流抽象,是面向连接全双工的协议。
  • TCP使用端口进行多路分解,使用基于字节序列号的累积确认、滑动窗口、超时与快速重传来实现可靠性。
  • 流量控制通过接收方的通告窗口实现,防止接收方缓冲区溢出。
  • 拥塞控制通过发送方维护的拥塞窗口实现,旨在公平共享网络资源(后续课程详解)。
  • TCP连接的建立和终止分别通过三次握手四次挥手完成,并定义了相关的状态机。

TCP是一个经过精心设计、经受住时间考验的协议。尽管面临新的应用场景(如物联网、实时媒体)的挑战,其核心架构至今仍是互联网可靠数据传输的基石。

16:拥塞控制的目标、问题与方法 🚦

概述

在本节课中,我们将要学习网络中的一个核心主题:拥塞控制。我们将探讨为什么网络会发生拥塞,拥塞带来的危害,以及设计拥塞控制方案的基本目标、面临的挑战和可能的解决思路。本节课将重点介绍拥塞控制的基本概念和设计空间,为后续深入理解TCP的拥塞控制机制打下基础。


拥塞问题回顾

上一节我们讨论了可靠传输,本节中我们来看看网络中的另一个基本问题:拥塞。

当路由器同时收到多个数据包时,它会缓冲一个并转发另一个。如果短时间内有太多数据包连续到达,队列就会堆积,导致数据包延迟增加,最终可能不得不丢弃数据包。这种状态就是网络或路由器经历了拥塞。避免进入这种状态正是拥塞控制的目标。

为什么需要担心拥塞?

拥塞会显著影响网络性能。下图展示了一个理论模型:将网络视为一个具有特定容量和队列的链路。当网络负载(X轴)增加时,平均数据包延迟(Y轴)会急剧上升。虚线表示链路的容量。

从这个图中我们可以得到两个关键结论:

  1. 性能权衡:提高网络利用率(接近链路容量)是以增加延迟为代价的。拥塞控制的目标就是找到既能高效利用网络,又不至于导致应用无法接受延迟的“最佳操作点”。
  2. 丢包是延迟的后果:当你看到丢包时,网络已经处于高延迟的糟糕状态。因此,如果关心延迟,丢包就是一个非常糟糕的信号。


历史背景:拥塞崩溃

有趣的是,TCP在早期(70年代末到80年代中期)并没有任何形式的拥塞控制。发送速率完全由上一节讨论的流量控制(接收方的通告窗口)决定。旧的TCP版本会在超时时,将整个窗口的数据全部注入网络并持续重传。

在80年代中期,互联网用户和开发者观察到一系列被称为 “拥塞崩溃” 的事件。例如,Michael Karels和Van Jacobson观察到,从劳伦斯伯克利实验室到伯克利的吞吐量从32 KB/s骤降至40 B/s,下降了近一千倍。

他们发现并修复了几个问题,包括超时估计算法(最初的TCP没有自适应超时估计)。更重要的是,他们意识到随着用户增多,数据源并没有降低向网络发送数据的速率。因此,他们在伯克利Unix的TCP/IP协议栈中引入了一系列算法,即我们现在所说的拥塞控制。自那以后,互联网上再未发生过拥塞崩溃。

Van Jacobson的贡献尤为突出,他后来还领导开发了BBR等新的拥塞控制协议,该协议现已用于YouTube、谷歌搜索等大量互联网流量。


拥塞控制的基本思路

他们的方法巧妙而简单,是对TCP已有机制的增量扩展:

  • TCP已有检测丢包的机制(超时或重复ACK)。
  • TCP已有控制发送量的窗口机制。

核心思想是:让发送方根据检测到的丢包来调整其窗口大小

这个方案的优点在于:

  • 有效:该补丁应用后,拥塞崩溃立即消失。
  • 实用:无需升级路由器或应用程序,甚至只需升级发送方一侧。它只是伯克利Unix代码中的几行修改,因此在几个月内就被广泛采用。

这体现了系统设计的智慧:最好的解决方案往往源于对实际问题的具体理解。Jacobson从复杂的网络反馈控制模型中提取出核心直觉,并用简单的启发式方法(几行代码)实现。


拥塞控制的复杂性

为什么拥塞控制是一个难题?让我们通过一个例子来建立直觉。

假设有如下网络拓扑和链路容量:

问:主机A应以多大速率发送数据?
答案取决于:

  1. 目的地:如果发送给C,瓶颈是10 Gbps链路;如果发送给F,则不应超过2 Gbps。
  2. 路由动态性:如果A发给E,初始路径是10 Gbps。但如果路由发生变化(主机不参与路由,故不知情),新路径的瓶颈可能是1 Gbps,A的速率也应变为此值。
  3. 竞争流:如果A发往F(速率2 Gbps),此时B开始发往E。两条流共享1 Gbps链路,若公平共享,则A应降至1 Gbps。
  4. 间接竞争:如果此时G开始发往D。蓝流和紫流在1 Gbps链路上竞争,各得0.5 Gbps。这意味着紫流不能超过0.5 Gbps,从而释放了红流(A到F)的瓶颈链路容量,红流的速率反而可以增加到1.5 Gbps

这个例子说明,一个新流的加入,即使没有直接与我的流竞争资源,也可能改变我应得的带宽分配。

本质上,拥塞控制是一个资源分配问题,即我的流在其路径的每条链路上应分配多少带宽份额。但这比传统的资源分配(如CPU调度)复杂得多:

  • 高度相互依赖:更改一个流或一条链路的分配,会对网络中所有流产生连锁反应(全局影响)。
  • 动态变化:每当有流加入或离开系统时,分配就需要改变,且发生频繁。
  • 无集中控制:没有一个实体能掌握完整的拓扑或流信息,也没有实体能控制网络中的所有流。

拥塞控制的目标

在开始设计之前,我们需要明确一个好的拥塞控制方案应达到什么目标。

资源分配结果的角度看,我们希望:

  • 高链路利用率
  • 低数据包延迟和丢包率
  • 流之间的公平共享

我们知道无法同时实现完美利用率和零延迟,因此需要在目标之间寻求良好的权衡。

系统解决方案的角度看,我们希望方案是:

  • 可扩展的
  • 分布式的
  • 自适应的

这些都是互联网环境所要求的。


设计空间:可能的方案

以下是几种可能的拥塞控制思路:

1. 无控制

发送方随心所欲地发送。对于单个发送方,TCP的可靠性可以保证数据最终送达,但这会浪费大量带宽(非瓶颈链路),不是可行的方案。

2. 预留资源

在流开始时,通过网络预留带宽。这可以避免拥塞,但正如之前课程讨论的,这不是互联网采用的方式(尽管某些场景如ISP会进行资源预留)。

3. 基于价格或优先级

经济学家认为这是最优方案:将带宽视为稀缺商品,通过定价调节需求。例如:

  • 优先级:高优先级用户(付费更多)的流量在网络过载时优先通过。
  • 拥塞定价:根据网络拥塞程度向用户收费(例如,观看Netflix时按需付费提升体验)。

虽然随着城市交通拥堵收费和数字微支付的兴起,这个想法不再显得荒谬,但它需要相应的商业模式和支付模型,在互联网早期不存在,至今也未广泛部署。

4. 动态调整(胜出方案)

主机动态学习网络当前的拥塞程度,并相应地调整发送速率。这是我们之前直觉判断A应该发送多少速率时所隐含的方法。

动态调整方案在实践中胜出,也是所有现代拥塞控制方案的工作原理。它通用性强,不预设商业模式或应用需求。但它假设了 “良好公民”行为,即发送方会“做正确的事”并降低速率以共享带宽。经济学家认为这有违常理,但互联网至今主要依靠非恶意参与者来维持这一状态。


动态调整方案详解

我们将深入探讨动态调整方案,这也是TCP采用的路径。

核心思想

每个源独立运行以下算法:

  1. 以某个速率 R 向网络发送数据一段时间。
  2. 在此期间结束后,询问:我是否经历了拥塞?
  3. 如果经历了拥塞,则在下一周期降低速率;如果未经历,则在下一周期提高速率。
  4. 重复此过程。

这引出了解决方案的三个核心组件:

  1. 如何选择初始速率?(发现阶段)
  2. 如何检测拥塞?(检测阶段)
  3. 如何对拥塞做出反应?(即提高和降低的幅度是多少?)(调整阶段)

组件一:检测拥塞

在基于主机的方案中,我们依赖来自网络的隐式反馈。主要有两种信号:

A. 基于丢包(TCP主要采用)

  • 优点
    • 信号明确:丢包通常意味着路径上有路由器拥塞。
    • 工程便利:TCP中检测丢包的机制(超时、重复ACK)已存在。
  • 缺点
    • 延迟代价:丢包发生在高延迟之后,在检测到拥塞前已经历了性能下降。
    • 二进制信号:只告知“拥塞了”,不告知“应发送多快”。
    • 非拥塞性丢包:无线链路中,干扰可能导致丢包,但这并非拥塞信号。
    • 与重排序混淆:TCP使用累积确认,将多个重复ACK视为丢包。如果网络数据包重排序,会导致TCP误判为丢包并错误降速。这限制了网络设计(如数据中心中利用多路径负载均衡)。
  • 丢包类型:TCP中,丢包检测有两种方式,暗示了拥塞严重程度不同:
    • 重复ACK:暗示轻度、孤立的丢包。
    • 超时:暗示严重拥塞(大量数据包或ACK未能通过)。

B. 基于延迟

  • 思路:发送方观察估算的往返时间(RTT),如果RTT增加,则怀疑发生拥塞。
  • 历史:长期被认为难以准确使用,因为延迟信号噪声大,且需要知道“正常”延迟基准。
  • 现状:谷歌的BBR协议基于延迟,并证明在大规模数据样本下是可行的。它仍存在争议,但是一个重要的新方向。

组件二:发现初始速率

目标是估算路径上可用的带宽。

  • 要求
    • 安全性:从低速开始,避免立即造成拥塞。
    • 快速收敛:需要迅速攀升到可用带宽附近,否则会浪费时间和带宽。
  • 线性增长的不足:如果可用带宽范围很大(如从1 Mbps到1 Gbps),线性增长太慢。
  • TCP的解决方案:慢启动
    • 名称误导:它启动慢,但增长快。
    • 过程:从一个很小的值开始,然后指数级增长(例如,每轮加倍),直到检测到丢包。此时,发送方得知上一个未引起丢包的速率是安全的。

示例:从100 Mbps开始 -> 200 Mbps -> 400 Mbps -> 800 Mbps -> 1.6 Gbps(此时检测到丢包)。则安全速率被认为是800 Mbps。


组件三:调整速率(增/减窗算法)

这是拥塞控制最关键的部分。调整的速度和幅度决定了:

  • 效率:多快能充分利用可用带宽。
  • 公平性:新流加入后,多快能达到公平共享。

调整有两种基本方式:

  • 加性增/减:增加或减少一个常量(如 +1 Mbps, -1 Mbps)。
  • 乘性增/减:按比例增加或减少(如 翻倍,减半)。

由此产生四种设计选项:

  1. AIMD (Additive Increase, Multiplicative Decrease):加性增,乘性减。
  2. AIAD (Additive Increase, Additive Decrease):加性增,加性减。
  3. MIMD (Multiplicative Increase, Multiplicative Decrease):乘性增,乘性减。
  4. MIAD (Multiplicative Increase, Additive Decrease):乘性增,加性减。

直觉上,我们倾向于“温和探索,遇拥塞快速撤退”的策略,即 AIMD


为什么是AIMD?—— 效率与公平性的收敛

我们通过一个简单模型来分析。假设一条容量为 C 的链路上有两个流,其速率分别为 x1 和 x2。

  • 目标
    • 效率:x1 + x2 = C(充分利用链路)。
    • 公平:x1 = x2(公平共享)。
  • 图形化分析(设C=1):
    • 效率线:x1 + x2 = 1(蓝色线)。线上点效率最优。
    • 公平线:x1 = x2(绿色线)。线上点绝对公平。
    • 理想点:效率线与公平线的交点。

分析四种算法在图形上的移动轨迹:

  • AIAD:增减都沿斜率为1的直线移动。在效率线上下振荡,但从不向公平线移动,无法收敛到公平。
  • MIMD:增减都沿从原点出发的射线移动。同样在效率线附近振荡,但从不改变与公平线的角度,无法收敛到公平。
  • MIAD:增沿射线,减沿水平/垂直线移动。结果会极度不公平,导致一个流获得零带宽,另一个流独占所有带宽。
  • AIMD:增沿斜率为1的直线(加性增),减沿射线(乘性减)。每次“减”操作后,“增”操作会沿着不同的线(更靠近公平线)进行。经过数次振荡,操作点会收敛到效率与公平的交点

因此,AIMD是唯一能同时实现效率与公平收敛的算法


TCP拥塞控制方案概览

综合以上组件,我们得到了TCP拥塞控制的基本轮廓:

  1. 初始速率:采用慢启动。从很小窗口开始,指数增长(每RTT窗口加倍)直至首次丢包。
  2. 拥塞检测:主要依据丢包信号(超时或重复ACK)。
  3. 速率调整:采用 AIMD 策略。
    • 无拥塞时(加性增):每RTT将拥塞窗口增加1个MSS(最大报文段长度)。
    • 检测到拥塞时(乘性减):将拥塞窗口减半。

TCP的“锯齿”行为

一个TCP连接的吞吐量随时间变化会呈现典型的“锯齿”状:

  • 慢启动阶段:指数增长。
  • 首次丢包:窗口乘性减半。
  • 拥塞避免阶段:进入线性增长(AIMD的加性增),直到再次丢包,窗口再次减半,如此循环。


总结

本节课中我们一起学习了拥塞控制的基础知识。我们了解到:

  • 拥塞是由于网络资源需求超过供给导致的,会引起延迟增加和丢包。
  • 拥塞控制本质上是一个分布式、动态、相互依赖的资源分配难题。
  • 一个好的拥塞控制方案需要在效率(高利用率)、公平性(流间公平共享)和低延迟之间取得平衡。
  • TCP采用的动态调整方案是主流,其核心是AIMD算法,并结合慢启动基于丢包的拥塞检测
  • AIMD被证明是能同时收敛到效率和公平的唯一一种基础调整策略。

下一讲我们将深入TCP拥塞控制的实现细节,包括如何用窗口而非速率来表达这些概念,以及针对不同丢包类型(超时 vs. 重复ACK)的不同反应机制。

17:TCP拥塞控制设计 🚦

在本节课中,我们将深入学习TCP拥塞控制的具体实现。我们将回顾TCP的滑动窗口可靠性机制,并详细探讨如何在此基础上扩展,通过调整拥塞窗口(cwnd) 来实现拥塞控制。核心在于理解TCP如何通过确认(ACK)时钟来调整发送速率,并遵循慢启动(Slow Start)拥塞避免(Congestion Avoidance)快速恢复(Fast Recovery) 等状态来动态适应网络状况。


TCP可靠性回顾:滑动窗口

上一节我们介绍了拥塞控制的基本问题与设计空间,本节中我们来看看TCP如何具体实现。首先,我们需要回顾TCP实现可靠数据传输的基础:滑动窗口协议。

在滑动窗口协议中,发送方维护一个大小为 w 的连续字节窗口。窗口左侧的字节(已发送且被确认)可以释放,右侧的字节尚未发送。发送方只能发送窗口内的字节。

  • 发送方行为:发送窗口内的字节。它只维护一个与窗口左侧(即最早未确认字节)关联的计时器。若该计时器超时,则重传从该偏移量开始的所有字节。
  • 接收方行为:发送累积确认(Cumulative ACK),指明期望收到的下一个字节序号。
  • 快速重传(Fast Retransmit):当发送方收到三个重复的ACK(Duplicate ACK) 时,它会认为数据包已丢失,并立即重传第一个丢失的包,而无需等待计时器超时。
  • 窗口滑动:当发送方收到对新数据的确认时,窗口向右滑动,允许发送新的数据。

引入拥塞控制:拥塞窗口(cwnd)

现在,我们在TCP的滑动窗口设计中加入拥塞控制。关键是为发送方增加一个新的状态变量:拥塞窗口(cwnd)

发送方的实际发送窗口大小由以下公式决定:

实际窗口大小 = min(cwnd, rwnd)

其中 rwnd 是接收方通告的窗口大小(用于流量控制)。在本讲座中,我们假设 rwnd 足够大,因此发送速率主要由 cwnd 决定。

发送方的发送速率(Rate)与 cwnd 和往返时间(RTT)直接相关:

发送速率 ≈ cwnd / RTT

因此,通过调整 cwnd,TCP就在调整其发送速率。

ACK时钟(ACK Clocking)

TCP拥塞控制的所有更新(除了超时)都由确认(ACK)的到达驱动,这种机制称为ACK时钟

其核心思想是:每当发送方收到一个新的ACK(意味着一个数据包已成功离开网络),它就被允许向网络注入一个新的数据包。这样,发送方的数据包传输就被ACK的到达速率所“计时”。

ACK时钟的好处在于:

  1. 自我调节:发送方能自动将发送速率调整到网络可用带宽的水平。
  2. 平稳发送:避免了突发流量,减少了网络中瞬时队列和丢包的可能性。

ACK时钟优雅地实现了我们在上节课提到的“守恒原则”:在稳定状态下,从网络中取走一个数据包,才允许放入一个新的数据包。

TCP拥塞控制的核心组件

TCP的拥塞控制实现基于我们之前确定的三个设计组件,以下是其具体实现方式:

1. 丢包检测

TCP通过两种方式检测拥塞(丢包):

  • 收到三个重复ACK:通常表示发生了单个数据包丢失
  • 重传计时器超时:通常表示发生了严重拥塞,可能丢失了多个数据包。

2. 初始速率发现:慢启动(Slow Start)

慢启动的目标是快速找到一个合适的初始发送速率。

  • 启动:连接开始时,cwnd 设置为 1个MSS(最大报文段长度)。
  • 增长规则:在慢启动阶段,每收到一个ACK,cwnd 增加1个MSS
  • 效果:由于每个RTT内大约会收到 cwnd 个ACK,因此 cwnd 在每个RTT内会翻倍,实现指数级增长。
  • 慢启动阈值(ssthresh):TCP维护一个 ssthresh 变量,记录“安全速率”的估计值。当发生丢包时,ssthresh 被设置为当前 cwnd 的一半。下次进入慢启动时,cwnd 会指数增长直到达到 ssthresh,然后转入更谨慎的拥塞避免阶段。

3. 速率适应:AIMD与状态转换

在探测到安全速率后,TCP使用加性增、乘性减(AIMD) 来调整速率。

  • 加性增(Additive Increase) - 拥塞避免状态
    • cwnd >= ssthresh 时,TCP进入拥塞避免状态。
    • 在此状态下,每收到一个ACK,cwnd 增加 1/cwnd 个MSS
    • 这样,每个RTT内,cwnd 总共增加1个MSS,实现线性增长。
    • 代码/公式表示(以字节为单位):
      cwnd = cwnd + (MSS * MSS / cwnd)
      

  • 乘性减(Multiplicative Decrease)
    • 当通过三个重复ACK检测到丢包时,TCP执行:
      ssthresh = cwnd / 2
      cwnd = ssthresh  (不低于1个MSS)
      
    • 然后进入快速恢复状态。

  • 超时处理
    • 当发生超时时,TCP认为网络状况不明,采取最保守策略:
      ssthresh = cwnd / 2
      cwnd = 1 (重置为1个MSS)
      
    • 然后重新进入慢启动状态。

快速恢复(Fast Recovery)优化

在拥塞避免阶段,如果仅丢失一个数据包,标准的AIMD行为(将 cwnd 减半并等待重传包被确认)会导致发送方在较长时间内停滞,且丢失ACK时钟的节奏。快速恢复 就是为了优化这种情况。

快速恢复的核心思想是:在收到三个重复ACK后,不立即将 cwnd 减半到 ssthresh 并等待,而是临时“赊账”,允许发送方在收到重复ACK期间继续发送新数据包,以保持管道充盈和ACK时钟。

以下是快速恢复的规则:

  1. 当收到第3个重复ACK时:
    • 设置 ssthresh = cwnd / 2
    • 设置 cwnd = ssthresh + 3(为已收到的3个重复ACK给予信用)。
    • 重传丢失的包,并进入快速恢复状态。
  2. 在快速恢复状态中,每收到一个额外的重复ACK
    • cwnd = cwnd + 1(再给予1个信用)。
    • 如果允许(有可用的窗口),则发送一个新数据包。
  3. 当收到一个新的ACK(表明重传的包已被接收,缺口被填补)时:
    • 设置 cwnd = ssthresh(正式执行乘性减)。
    • 退出快速恢复状态,进入拥塞避免状态。

TCP状态机总结

TCP拥塞控制的行为可以通过一个状态机清晰地概括,包含三个主要状态:

  1. 慢启动(Slow Start)cwnd 指数增长。
  2. 拥塞避免(Congestion Avoidance)cwnd 线性增长(AIMD中的AI)。
  3. 快速恢复(Fast Recovery):优化单个丢包后的恢复过程。

状态转换由以下事件触发:

  • 任何状态下的超时 -> 进入慢启动
  • 慢启动中,cwnd >= ssthresh -> 进入拥塞避免
  • 慢启动或拥塞避免中,收到3个重复ACK -> 进入快速恢复
  • 快速恢复中,收到新ACK -> 进入拥塞避免

理解这个状态机以及每个状态下 cwnd 的更新规则,就掌握了TCP拥塞控制的核心。

TCP变体与互操作性

存在多种TCP拥塞控制变体(如Tahoe、Reno、NewReno、SACK)。我们今天讨论的机制基本对应 TCP NewReno

  • 互操作性:只要变体不改变TCP头部格式或ACK的语义(如Tahoe、Reno、NewReno),仅改变发送方内部的 cwnd 更新逻辑,它们就可以互操作。因为接收方行为不变。
  • 需要协商的变体:如果变体改变了协议字段(如SACK使用选择性确认选项),则需要连接双方都支持才能正常工作。


本节课中我们一起学习了TCP拥塞控制的详细设计。我们从滑动窗口可靠性基础出发,引入了拥塞窗口(cwnd) 的概念,并深入探讨了TCP如何通过ACK时钟机制,在慢启动拥塞避免快速恢复三个状态间转换,动态地执行加性增、乘性减(AIMD) 算法来应对网络拥塞。我们还了解了快速恢复优化如何改善单个丢包后的性能。掌握这些状态和转换规则是理解TCP行为的关键。下一讲,我们将探讨更高级的拥塞控制主题及其性能分析。

18:高级拥塞控制

在本节课中,我们将学习TCP拥塞控制的高级主题。我们将首先学习一个用于估算TCP吞吐量的模型,然后探讨TCP拥塞控制存在的一些局限性,最后了解几种由路由器辅助的拥塞控制方案。

项目与课程安排

大家好,我们开始上课。今天我们将发布项目二。

Tenzan将为大家介绍这个项目。项目二将围绕传输层展开,你们将使用Murphy开发的网络模拟器来实现一个TCP套接字的核心部分。这个网络模拟器与项目一的不同,它是一个名为POX的模拟器,将帮助你们测试、验证并与你们的套接字实现进行交互。这个项目分为九个阶段,类似于上一个项目的十个阶段。

项目截止日期是11月11日午夜。如果需要任何帮助,请参加项目答疑时间,而不是课程内容答疑时间。

请大家尽量往前坐,这样能让课堂更紧凑一些。

在课程开始前,有一个快速通知。接下来的四节课,我们将恢复之前尝试过的模式,发布四节录播课。这些是Murphy在2020年的精彩录播,主题涵盖DNS、HTTP、以太网以及各种发现和引导协议,如DHCP和ARP。这样你们就可以从头到尾地理解从设置笔记本电脑到下载网页的所有步骤。这些录播课非常棒。根据我们收到的调查反馈,大家普遍欢迎录播课。

对于每隔一节课就进行一次翻转课堂,大家的感受有些复杂,因为这增加了时间投入。因此,我们将在四节录播课结束后进行一次翻转课堂。我们可能会对这几节课中涉及的协议(如DHCP、DNS等)进行一次更偏向动手实践的讲解。

重申一下,从本周四开始,接下来的一周都没有现场授课。请务必观看录播课。和往常一样,我们会有测验。在接下来的周二,我们将进行一次翻转课堂。请务必在来上课前观看录播,翻转课堂将假设你们已经看过了。

所有关于此事的提醒和细节都会发布在网站上。在后勤方面有任何问题或想法吗?

课程概述

好的,那么今天是关于拥塞控制主题的最后一讲。这是关于拥塞控制的第三讲。

上次我们详细介绍了TCP拥塞控制的工作原理,逐步讲解了如何实现窗口、重传时如何滑动窗口等规则。

今天的课程将有很大不同。我们将从更高的层面退一步,审视几个主题。我们将尝试对TCP的性能进行建模,从而摆脱那些详细的、逐秒的行为描述。

我们还将对TCP进行一些批判性分析,看看它做得好和不好的地方,这引出了TCP的一些演进和变化。

最后,我们将讨论路由器辅助的拥塞控制,其中一些已在实践中应用,一些还处于研究开发阶段,我们会探讨所有这些想法。

为了让你们思路清晰,我们将涵盖相当广泛的设计理念。其中一些甚至仍处于研究阶段,我们不期望你们在本课程中完全理解这些理念,但你们应该知道这些设计试图解决什么问题,以及它们试图如何解决这些问题的高层思路。

与之前详细讲解的TCP拥塞控制不同,我们不要求你们了解这些想法如何工作的细节。

TCP吞吐量方程

那么,今天的第一个主题是TCP吞吐量方程,它试图回答的问题是:我们已经详细讨论了TCP和拥塞控制的工作原理,包括滑动窗口、AIMD(加性增乘性减)等。但如果我问你,给定一条路径,作为应用开发者或用户,你能期望获得多大的TCP吞吐量?你能回答吗?

如果我告诉你,你的路径有特定的往返时间(RTT)和丢包率(P),这很难回答,因为我们研究的是TCP如何工作的非常底层的机制。你可能能告诉我,如果窗口大小是17,你收到了三个重复确认,你会将窗口大小减少多少,然后在某个时间点能在网络中发送多少数据包。但这并不令人满意。这不是我们在更高层面思考性能的方式。我们想要一种更简洁、更通用的方式来推理性能。这就是TCP吞吐量方程试图给我们的东西。它是一个简单的模型,做了一些简化假设,因为可以想象,没有精确的闭式方程能准确预测TCP吞吐量。

但这个方程要回答的问题是:如果我告诉你源和目的地之间有一条路径,并且我告诉你这条路径的大致往返时间(RTT)和丢包率(P),那么这个方程将告诉你,作为应用开发者或用户,可以期望获得多大的吞吐量。

让我们逐步推导这个方程是如何工作的。简单模型从一个假设开始:假设每当发送方的拥塞窗口达到一个参数 W_max(即经历丢包时的最大窗口大小)时,就会发生一次丢包。

我们进一步假设丢包是通过重复确认(Duplicate ACK)机制检测到的。这意味着我们没有超时,只有一次孤立的丢包。这是另一个假设。

那么,思考一下发送方窗口大小将如何演变。第一步,你收到三个重复确认,检测到丢包,第一件事就是将窗口大小减半。此后,我们进入AIMD阶段(加性增乘性减)。因此,在检测到丢包后的一个RTT,我们期望窗口大小为 W_max / 2 + 1。再过一个RTT,我们增加2。再过另一个RTT,我们增加3。依此类推,直到在我们的模型中达到 W_max,即最大窗口大小,此时我们再次经历丢包。

然后,在这一点上,再次将窗口大小减半,并重复此过程。因此,我们窗口大小的演变过程是:从 W_max 减半到 W_max / 2,然后每个RTT增加1,直到再次达到 W_max,然后再减半。

我们每个RTT增加1,持续了 W_max / 2 个RTT。因为从 W_max / 2 开始,每个RTT增加1,直到达到 W_max,这意味着我们经过了 W_max / 2 个RTT。然后,我们一遍又一遍地重复。

基于这个非常简单的假设,这告诉我们,假设我们始终处于这种在减半和全满之间上下波动的稳定行为中,那么每个RTT的平均窗口大小是 3/4 * W_max

那么吞吐量,窗口大小就是你发送的数据包数量。因此,吞吐量是 3/4 * W_max 乘以 MSS(每个数据包的字节数),再除以往返时间(RTT)。因为我们平均每个RTT发送 3/4 个窗口的数据。字节除以时间,我们就得到了吞吐量。

到目前为止都清楚吗?有任何问题吗?

这样,我们已经用往返时间表达了吞吐量(但还包含 W_max 项)。W_max 本身并不是路径属性。所以下一步是用其他东西替换 W_max,这将是丢包率的表达式。

到目前为止有任何问题吗?

好的,这将是模型的第二步。现在我向你们展示的是拥塞窗口大小(以数据包数为单位)随时间(X轴)变化的图。

我们从慢启动阶段开始,达到 W_max,一次丢包后减半,然后在这两点之间上下波动,进行AIMD。我们将关注点放在这里。我们假设我们永远停留在这个AIMD阶段,忽略初始的慢启动。

现在,让我们关注这里用蓝色高亮显示的其中一个周期,标记为 A

如果我们现在考虑这个图中的丢包率,我们在那个阴影区域恰好丢了一个数据包。平均而言,我们在整个阴影区域每发送 A 个数据包就丢失一个。因此,我们的丢包率 P1 / A,其中 A 是那个阴影区域发送的数据包总数。

剩下的就是计算阴影区域实际有多少个数据包 A。我们刚刚说过,这个时间段是 W_max / 2 个RTT。因为每个RTT我们增加1,我们从 W_max / 2 增加到 W_max,所以有 W_max / 2 个RTT。

我们还说过,平均每个RTT我们有 3/4 * W_max 个数据包。因此,阴影区域的数据包总数就是这两者的乘积。即 (3/4 * W_max) * (W_max / 2) = (3/8) * W_max^2

现在,如果我将项移来移去,我可以用丢包率 P 来表示 W_max,然后将其代入我们之前得到的用 W_max 表示吞吐量的公式中。

这样我们就得到了这个函数,即吞吐量用路径的往返时间(RTT)和丢包率(P)表示。

我暂停一下。有任何问题吗?或者有人希望我重复某个部分吗?

MSS是最大报文段大小,即一个数据包中的字节数。在我们乘以MSS之前的所有计算都是以数据包为单位的。当我们乘以MSS时,就得到了比特每秒。

还有其他问题吗?或者有哪个点希望我重复吗?我们将在接下来的幻灯片中使用这个方程,所以理解它很重要。

因为我们在线性增加,从一半到全满,所以平均值是四分之三。

好的,给定一条路径,我们可以期望的TCP吞吐量是多少?我们现在知道,吞吐量与 1 / RTT 成正比,与路径丢包率的平方根成反比。

你们可以离线查看推导过程,那里没有什么复杂的东西。但从直觉上思考,这应该完全合理。为什么它与往返时间成反比?因为请记住,我们在TCP中所做的一切都是基于确认(ACK)来计时的。你等待一个往返时间来接收确认,然后做出反应。所以,如果我的往返时间更短,我就能更快地收到确认,反应也更快,增加得也更快。因此,吞吐量与往返时间成反比是合理的,短往返时间意味着反应更快。

丢包率也是如此。如果路径丢包率高,那么我会更频繁地减少窗口。因此,我的吞吐量更低是合理的。如果我丢包率非常低,那么我会持续增加并保持在那里,不会减少。同样,吞吐量与丢包率成反比也是合理的。

这个模型做了一些简化假设,特别是忽略了慢启动。如果你有一个长连接,通常只慢启动一次,然后大部分时间进行AIMD,那么这个假设是合理的。它还假设固定的往返时间和非常清晰的丢包模式,即每次达到 W_max 时恰好丢一个包。

尽管有这些假设,但这个模型实际上很有用,因为它让我们能够退一步,摆脱对单个确认的思考,转而考虑TCP的整体行为。

正如我们将在接下来的几张幻灯片中看到的,仅仅看这个方程就能给你很多关于TCP做得好和不好的地方的见解。我们马上会讲到。

TCP拥塞控制的局限性

总结一下,我们已经完成了TCP拥塞控制的讲解,现在将转向探讨TCP做得不好的地方。

让我们回顾一下TCP进行拥塞控制的主要特点:

  1. 它是基于主机的,无需路由器支持。实际上,它是基于发送方的,所有智能都在发送方。
  2. 它是基于丢包的,我们使用丢包作为拥塞的指示。
  3. 我们适应的时标是往返时间。
  4. 我们从慢启动开始,拥塞窗口(cwnd)从1开始,每个RTT翻倍,直到发生丢包。
  5. 在稳定状态下,我们通过加性增乘性减(AIMD)来适应并找到正确的速率。这是一种非常温和的增加和非常保守的减少。
  6. 最后,吞吐量取决于往返时间和丢包率,根据我们推导的方程。

现在,如果你审视这个设置,并稍微转变角度,进行一些批判性思考。对于这个设置或这套设计选择,有哪些是我们不喜欢、可能有问题、可能显得怪异或值得评论的地方?

我们不断改变速率,总是在增加或减少,从未处于一个稳定的点。是的,这是一个问题。还有它是基于丢包的。丢包可能并非由拥塞引起,这绝对是个问题。关于这个设置还有其他问题吗?关于发送方呢?它纯粹是基于主机的,这既是优点也是缺点。我们稍后会讲到。还有其他吗?

好的,让我们看看其中的一些问题。我想我列出了大约八个。第一个影响实际上直接来自于观察TCP吞吐量方程。

1. 面对异构RTT的不公平性

我们已经说过,流的吞吐量与往返时间成反比。这在某种程度上是设计使然。我们决定等待确认返回,并基于此决定如何调整拥塞窗口。但这带来了一些影响。

如果我给你看一个非常简单的例子,比如这个:A1与B1通信,往返时间为100毫秒;A2与B2通信,往返时间为200毫秒。TCP吞吐量方程告诉我们什么?你期望A1和A2的吞吐量关系如何?在其他条件相同的情况下,A1的吞吐量是A2的两倍。

因此,面对异构的往返时间,TCP实际上是不公平的。我们讨论过AIMD如何收敛到效率和公平,但如果你回顾那个例子,你会意识到我们假设它们有相同的往返时间,它们在同一时间做出反应,因此我们收敛到完全公平。但在实践中,如果你的往返时间短,你增加得更快,因此你更贪婪地获取带宽份额。所以TCP并不完全公平。

例如,如果你坐在伯克利,从旧金山的服务器下载文件,假设往返时间与地理距离成正比,那么你的吞吐量将比从东海岸下载要高,因为你的往返时间更短。实际上,当我们讨论Web和HTTP时,你会听到内容分发网络(CDN),比如Akamai或Cloudflare,它们将内容缓存到离用户非常近的地方。这有几个原因,一个是传播延迟更低,但你还能获得额外的提升,因为TCP增加得更快,给你更好的吞吐量。因此,在实践中,这也是推动内容边缘计算、将内容和计算移近用户的原因之一。

对此有任何问题吗?所以,这可以被称为一个问题,也可以不称为问题,但这确实是TCP的实际行为,作为互联网用户,我们就是这样体验它的。

2. 高速TCP问题

第二个影响来自于观察TCP吞吐量方程,即高速TCP问题。这里的设置如下:假设你在互联网上有一条路径,其瓶颈容量或作为发送方可以发送的容量是100 Gbps。假设你的往返时间是100毫秒,MSS是1500字节。

如果我将这些值代入TCP吞吐量方程,它会告诉我,为了达到100 Gbps的吞吐量,我需要的丢包率大约是 2 * 10^{-12},这是一个低得离谱的丢包率。这意味着每500亿个数据包才丢一个。

或者,如果你计算一下需要多少个RTT才能增加到那个速率,可能需要数小时。这里发生了什么?我说的是,我希望一个流获得100 Gbps。它必须能够增加。如果你在这里代入数字,并计算这种带宽延迟积对应的拥塞窗口数据包数,可能是成千上万,甚至十万个数据包。

那么,思考一下,如果我的拥塞窗口有10000个数据包,我丢了一个包,TCP会将拥塞窗口减半到5000个数据包。现在,它每100毫秒增加一个数据包。但要重新增加到10000个数据包,需要5000个RTT。5000个RTT乘以100毫秒,你将花费数小时才能重新增加到100 Gbps。

所以,基本上,这些数字是不切实际的。即使是 10^{-12} 的丢包率,也接近大多数通信介质的比特误码率。因此,在你试图增加到那个速率的过程中,几乎肯定会遇到丢包。

因此,TCP面临的根本问题是,为了将单个流扩展到非常高的吞吐量或非常高速的链路,如果你采用每个RTT增加一个数据包的加性增加方式,速度会非常慢。

通常有人会问,这种情况有多常见?一个流需要占用100 Gbps路径的情况多吗?答案是不太常见。所以这不是每个互联网用户都面临的问题,尽管我们有100G或10G的链路,但它们被数百万用户共享。然而,在某些场景下,这个问题确实会出现。实际上,提出这个问题并在标准机构中推动解决的团队是ESnet(能源科学网络)。这是一个连接美国国家实验室并连接到能源部的网络。

这些物理学家和天体物理学家有粒子模拟器,以及从外太空收集的巨大数据集需要传输。因此,他们确实经常需要在站点之间移动这些非常大的数据传输,并且他们建立了一个专为他们(也不仅仅是他们)的网络,虽然通过互联网,但他们已经在所有实验室之间部署了100 Gbps的链路,他们希望能够使用它们。所以,这是一个非常糟糕的情况:你部署了高速网络,但由于TCP的阻碍,你实际上无法达到那种高吞吐量。

因此,他们提出的解决方案,也是他们一直在努力标准化的方案之一,叫做高速TCP。它对TCP做了一个非常简单的改变。如果你处于这种场景,你会怎么做?

如果你被要求修改TCP的拥塞控制来解决这个问题,你会怎么做?增加超过一个数据包。这绝对是一个方法。但有一个附加条件:你必须小心不要影响其他流。

非常接近你的想法:流(发送方)监控其拥塞窗口。一旦超过某个阈值,它就说,好吧,我处于高速状态,我不是在竞争100 Mbps,而是在竞争大量带宽。我可以开始更快地增加,因为有很多可用带宽。因此,高速TCP通常的做法是:在达到阈值之前进行低加性增加,之后,它会根据拥塞窗口的函数来增加,即在那之后超线性地增加。即使在减少时,它也会比减半更温和一些。

再次强调,我们不要求你们知道高速TCP如何工作的细节,但要理解它解决的问题:如果你试图占用这些高速链路,TCP增加得太慢。其基本思路是增加得更快,比线性更快。

你能想到其他解决这个问题的方法吗?不改变TCP?这在互联网上经常发生。发送更多流,打开多个同时连接。人们提出的另一种方法是使用路由器辅助的拥塞控制,我们稍后会讲到。

对于打开多个连接,人们怎么看?作为解决方案有什么缺点吗?你可以打开100个连接,获得100倍的吞吐量。但有一个小原因使得这样做很痛苦。从应用开发者的角度来看,TCP的一个优点是它提供了一个很好的字节流抽象。你只需要说,我有这些数据要发送,打开一个连接,发送过去,就完成了。如果你要打开多个连接,应用现在必须考虑如何在连接之间划分应用数据,必须跟踪这个特定连接是否被重置、需要重启并重新发送数据,或者一个连接慢而另一个快,可能需要重新在连接之间分配数据。所以作为应用开发者,需要做更多工作,但这当然是可行的。这可能比一开始就让你的程序多线程化更容易,是的。这是个很好的类比。

3. 基于速率的拥塞控制

第三个影响是我们称之为基于速率的拥塞控制。这正是之前有人提到的,即TCP吞吐量是波动的,我们不断重复那种上升、下降、再上升的模式。对于某些应用来说,这不是自然的行为方式。特别是,如果你考虑音频或视频流应用,它们以特定的速率采样音频或视频,应用的自然行为是选择一个速率并以该速率传输数据。

因此,支持此类应用的一个解决方案直接来自于吞吐量方程,我们称之为基于方程的拥塞控制。

想象一下,你是一个应用开发者,由于这种波动行为,你实际上不想使用TCP。你可以做的是发送几个数据包,测量往返时间,估计丢包率 P,将这些值代入TCP吞吐量方程,然后以该速率发送。

在讲座早些时候,有人问过,如果你有一个UDP应用,而UDP不做任何拥塞控制,我们不应该担心UDP如何与TCP竞争吗?构建UDP应用的正确方法就是这样做,即计算TCP会占用多少带宽份额,我将占用相同的份额,然后我就以那个速率发送。

对此有任何问题吗?我们这样做的原因是,这个方程告诉我们,我们允许一种不同的方式来实现TCP友好性,而无需实际遵循TCP的拥塞控制行为。

这通常有一个标准,RFC编号在那里,叫做基于方程的拥塞控制。它不一定是TCP。你可以使用TCP头部,如果你想保持TCP兼容性,但让速率由方程决定,而不是滑动窗口;或者它也可以是基于UDP的实现。所以它不一定是TCP。

4. 其他局限性

TCP拥塞控制的其他局限性。一个是之前提到的,我们过去讨论过,TCP会将其他原因(如链路损坏)造成的丢包误认为是拥塞造成的丢包。对于TCP来说,任何丢包都意味着拥塞,它会降低速率。

所以,如果链路上发生了损坏之类的事情,TCP不会知道。它仍然会表现得好像发生了拥塞,并降低速率。因此,你获得的性能比实际需要的要低。同样,我们不一定有解决方案。

另一个问题:你认为短流在TCP下表现如何?这个问题尤其相关,因为大多数测量显示,实际上大多数流都非常短。例如,这是几年前谷歌研究的一个数据点:50%的流只发送一个或少于一个数据包的数据。

想想为什么会这样。例如,很多数据中心应用是键值对查找,你说“给我这个键对应的值”。这不是一个长流,而是一个请求-响应,非常短的流。大约80%的流要发送的数据少于100 KB,这大约是少于10个数据包。

如果我们有绝大多数流都只有少量数据包,你认为TCP拥塞控制会怎么做?你认为这样做有什么缺点?让我们从TCP会怎么做开始。TCP启动连接时处于慢启动。想象一下,如果有四个数据包要发送。我会发送一个数据包,等待确认;发送两个数据包,等待确认;发送第四个数据包。所以我花了三个RTT发送了四个数据包。吞吐量低得离谱,大约每个RTT一个数据包。而我们刚刚说过,大约50%或80%的流将看到这种吞吐量。

这可能是个问题,因为许多流永远不会离开慢启动。我们在TCP中做了所有这些工作来找到你的公平份额并保持它,但事实上,大多数短流只会处于慢启动,它们花费的时间比应有的要长。这只是短流将经历的情况。

另一个出现的问题,这涉及到TCP拥塞控制的具体细节:请记住,我们严重依赖重复确认来避免超时。但是,假设我有一个五六个数据包的流,第四个或第五个数据包丢失了。我管道中没有足够的数据包来触发重复确认。因此,短流如果发生丢包,通常会导致超时,而超时时间与典型的RTT相比非常长。所以,如果你有一个短流,你又会得到很差的性能。

如何解决这个问题?首先,这有多大问题?你可能会想,哇,如果50%的流都是一个数据包,为什么我们一开始要在TCP中做所有这些工作?因为当你从另一个角度看测量数据时,网络上99%或90%的字节来自长流。我们称之为大象流和老鼠流,大多数字节在这些长流中,尽管大多数流是这些短的老鼠流。这种大象与老鼠的现象,我们将在讨论数据中心时再次提到。

回到这个问题。我们有很多短流,它们从未离开慢启动,性能很差,并且遭受超时。你能想到一个快速、有点取巧的修复方法吗?从一个更大的初始窗口开始。这实际上是谷歌几年前游说标准机构提出的,叫做初始窗口10,即从初始窗口10开始。所以现在,如果你要发送的数据少于10个包,你只需将它们全部丢到网络中,就完成了。

这是一个非常务实的快速修复方案。它并没有完全解决问题,因为它没有解决没有足够重复确认来触发重传的问题,所以你仍然会有超时。但除此之外,它解决了慢启动导致多个RTT的问题。

为什么不为短流使用不同的协议?事实上,在数据中心领域,有很多工作致力于能够快速发送短流。在实践中,这已经足够好了,对吧?初始窗口为10,这在一定程度上解决了问题。你不需要去重写网络栈和你的应用。所以从务实的角度来看,这解决了问题。没有特别的理由必须使用不同的协议。

5. TCP填满队列(Bufferbloat)

另一个问题:TCP填满队列。这再次与使用丢包作为延迟指示有关。所以,如果你记得TCP的工作方式,它在设计上故意试图找到最大可持续速率,而它这样做的方式就是导致丢包发生。因此,TCP设计上会填满瓶颈链路的队列。

如果你记得,我们说过丢包跟随延迟。这意味着在你经历丢包之前,你肯定已经经历了延迟,因为你填满了队列,你的数据包在队列中等待,增加了延迟,然后你才看到丢包。

这意味着延迟很大,而且对每个人都很大。想象一下,你有一个瓶颈链路,并且有很多这些短流,它们只有一两个数据包要发送。如果网络中没有长流,它们将看到零排队延迟,它们只是通过。现在,你有一个大流启动,传输一个10 GB的文件。这一个长流填满了所有队列。它不仅给自己造成延迟,也给网络中的所有流造成延迟,因为那些短流现在也被卡在长流后面,坐在队列中并产生延迟。

特别是,这确实是TCP设计的行为方式。所以并不是设计者不知道这种行为。但真正导致这个问题的是,很多供应商开始在路由器上添加越来越多的内存,特别是我们家里的家用路由器,它们有大量的缓冲空间。这里的想法是,家用路由器供应商只是不想在家里发生数据包丢弃,所以他们开始放入大缓冲区。于是我们开始看到这些非常大的延迟。

现在,这种由排队引起的缓冲区延迟实际上可能非常显著。在互联网上,这个问题被称为缓冲区膨胀(Bufferbloat)。这对于提供内容或关心延迟的互联网服务尤其成问题。当谷歌开始运行YouTube时,这个问题的典型例子真正惹恼了人们。

他们发现,当家中的用户同时观看YouTube时,搜索响应时间会下降。所以,如果你正在观看YouTube视频,而你家里有人在搜索,他们会看到更差的搜索延迟或搜索响应时间,因为有时家庭路由器上有数秒的缓冲。

这个问题清楚了吗?好的。那么,路由器在做什么?通过链路发送数据包。而TCP在设计上会增加其速率,直到导致丢包。这意味着什么?发送方(比如这里的YouTube)会继续尝试以更高的速率发送流量,这意味着在你家路由器朝向用户的方向上,家庭路由器的队列开始填满。但TCP会继续增加该速率,直到看到丢包。这意味着你路由器上的队列是满的。所以现在,当一个数据包到达该路由器时,它会坐在队列顶部,等待队列被清空。但是,如果我有,比如说,10000个数据包在队列中,那个数据包可能会被延迟很长时间。

还有其他问题吗?

这正是导致谷歌开始研究这种新的拥塞控制算法BBR的原因。这里的想法是,如果你是谷歌,试图解决这个问题,你会怎么做?

你想要低延迟。因为如果你想一想,坐在路由器缓冲区里有什么好处?从性能或吞吐量的角度来看,没有任何好处。我们设置缓冲区是因为我们说流量是突发的,我们想吸收暂时的突发。但现在,如果我们说我们总是让这个队列满着,它实际上并没有以任何方式帮助我们。

你会尝试做什么来修复这个问题?缩小队列。有一个关于这个的幻灯片。你可以告诉路由器供应商,特别是家用路由器供应商,不要在家庭路由器上放那么多缓冲。但这样做的担忧是,如果你有突发流量,会导致丢包。是的,没错。

实际上,这是一个巧妙的想法。虽然不是BBR采用的方法,但你绝对正确。这个想法叫做随机早期检测(RED),意思是当你的队列开始填满时,让我们主动丢弃一个数据包来告诉发送方减速。很好的想法,但不是BBR采用的那个。让我重新表述这个问题:如果不改变路由器,你会怎么做?如果你只是YouTube或谷歌,这是你唯一的选择,是的。这是一个有趣的思路。如果你没有突发流,但这能解决问题吗?因为这里的问题是TCP故意试图增加其速率,它唯一知道如何停止的方式就是先填满队列。

是的,所以这里的想法是基于延迟的拥塞控制。这是对TCP所做工作的一个非常简化的版本。这里你应该理解的是BBR试图做什么。我希望你理解BBR试图做什么的思路。

BBR试图做的是说,我们仍然希望以链路瓶颈或可用容量的最大可能吞吐量运行,但我们希望在不建立队列的情况下做到这一点。

因此,BBR的见解是,随着队列的建立,我作为发送方得到的信号表现为延迟。所以,如果我测量一个数据包从我发送到确认返回所花费的时间,如果我开始建立队列,数据包开始坐在队列中,我应该能够通过测量数据包的延迟来看到它。

因此,他们的主要思路是:与其等待丢包,不如测量延迟。让我们很好地了解路径的最小延迟是多少。所以,如果我能够足够频繁地测量延迟,我得到的最低测量值,即最小延迟,是在没有排队的情况下我可以预期的延迟的合理近似值。如果我开始看到我的延迟超过这个值,那么我就可以降低速率。

所以,它在检测到延迟增加时提前降低速率,而不是等到丢包。但除此之外,TCP适应和搜索、上升和下降以找到正确发送速率的基本思路在BBR中仍然存在。主要思路是测量延迟,观察其是否增加,并对延迟做出反应而不是丢包。

有任何问题吗?再次强调,我们不要求你们知道BBR如何工作的细节,但要理解它解决的问题:TCP会建立队列。并理解其核心思想:我们将测量延迟并对延迟的增加做出反应。

关于延迟,我们能作弊吗?为什么我们测量最小RTT?BBR中的想法是,我们不想让数据包在队列中堆积。当队列堆积时,延迟增加。但我需要知道一个基线,我需要比较什么。我需要知道一个好的延迟是什么样的。那个好的延迟将是我曾经看到的最小RTT。我将猜测或希望那个最小RTT对应于我没有排队的时候。

还有其他问题吗?

6. 作弊方式

作为TCP发送方,你可以作弊的方式。如果你只控制TCP连接的发送方,你可以做些什么来获得更高的吞吐量?不将窗口减半,这当然是一种。另一种是每个RTT增加超过一个MSS。事实上,如果你回顾我们上次做的效率-公平曲线,如果你说X每个RTT增加2,而Y每个RTT增加1,你会看到X获得Y两倍的速率。这是一种非常容易获得超过你公平份额的方法。

其他方式:直接打开多个连接。

再次回到这里,如果A向B打开10个连接,而D打开一个连接,那么A将获得D吞吐量的10倍。这可能是获得超过你公平份额的最简单方法。

使用大的初始窗口。没有什么能阻止你从1000个数据包的窗口开始。基本上,TCP中没有什么能阻止你不遵守规则,只是发送比你应有的更多的数据。

这对大家来说有道理吗?那么,每个人通常会问的自然问题是:为什么互联网没有遭受拥塞崩溃?我们开始讲拥塞控制时谈到,人们引入拥塞控制的原因是因为他们经历了拥塞崩溃。现在我们有了这个非常复杂的拥塞控制算法。但我们也知道,我们生活在一个互联网上有很多恶意行为者的世界。而我们刚刚说过,你不必遵循那个拥塞控制算法。你认为为什么我们还好好的?至少据我所知,自80年代以来,互联网上从未有过拥塞崩溃的报告。

当然,如果你有一组发送过多流量的源,它将被视为拒绝服务攻击,ISP可能会进行速率限制。另一个原因是,即使是那些在拥塞控制上作弊的人,他们也会退避。所以,回顾一下拥塞控制的早期原因,当时的TCP实现有一个固定的超时,它们从不调整超时,也从不降低速率。所以每次超时(可能是一个很短的值),它就会将整个窗口的数据丢到网络中。而今天,即使你打开10个连接,当这些连接遇到拥塞时也会退避;或者即使你从一个更大的初始拥塞窗口开始,当你丢包时,你也会进行乘性减少。所以,原则上,你可以向网络狂发数据包,但这通常是一种攻击,并且会受到严格限制。如果你只是想稍微修改TCP以获得不公平的优势,这会给你不公平的优势,但不会导致拥塞崩溃。

所以,这是对为什么我们尚未遭受拥塞崩溃的推测。那么你应该问的第二个问题是:好吧,我们没有拥塞崩溃,但不公平在互联网上常见吗?答案是,我们实际上不知道。有一些轶事报告偶尔出现在媒体上。例如,几年前,有一项研究显示BBR实际上并不公平,在某些BBR与某些传统TCP连接竞争的场景中,BBR会占用超过其份额的带宽。这导致了一些新闻报道,谷歌与这些研究人员合作修复BBR等等。但这确实会发生。我无法告诉你这在互联网上有多普遍,不公平的程度有多大。你可以想象,这实际上是一个很难回答的问题。在谷歌和BBR的案例中,他们开源了系统,所以人们可以获取并进行实验。但我们真的不知道Netflix做了什么(我并不是特意针对Netflix),但许多拥有闭源实现的内容提供商,我们就是不知道。

问题:BBR实际上是,如果你与YouTube服务器通信,该服务器在向你发送数据包时会使用BBR。BBR是TCP,它只是TCP内部一种不同的拥塞控制算法。所以这里的技巧是,拥塞控制只是在发送方运行的一种算法。所以它仍然是TCP。很好的观点。谷歌的原始实现是对Linux中拥塞控制算法的修改,并且是开源的。所以现在,你可以去获取源代码并在你的笔记本电脑上运行BBR。

7. 与可靠性的紧密耦合

最后一点,这与作弊关系不大,更多是软件工程实践:拥塞控制与可靠性深度交织在一起。我们详细讨论过,如果你看TCP中如何实现拥塞控制,其机制与TCP如何实现可靠性密切相关。我们使用重复确认、确认、超时,我们有窗口的概念,我们有围绕快速重传和快速恢复的规则,这些都与TCP如何做可靠性有关。

当我第一次介绍它时,我谈到这是TCP进行拥塞控制的一个有吸引力的方面。在当时,这只是TCP实现中的几行代码更改。这是积极的方面。缺点是,它使演进变得复杂。

当你考虑实现中的模块化时,我们希望有一定的关注点分离。例如,如果我想从累积确认切换到选择性确认,这应该是我们如何实现可靠性的问题。但在TCP中,它也影响我们如何进行拥塞控制。我们必须回过头来重新审视所有关于拥塞控制的快速恢复、快速重传规则。

所以这不一定是性能问题,更多的是代码可维护性和软件模块化的问题。为什么这很重要?有时我们想要拥塞控制,但不想要可靠性,比如实时音频或视频。但在TCP中,没有简单的方法可以关闭可靠性而获得拥塞控制,你两者都得到。反之亦然。原则上,我们可能想要可靠性而不想要拥塞控制,比如一个简单的请求-响应,一个键值查找。我其实不需要拥塞控制,但我想要可靠性。同样,在TCP中,没有简单的方法可以关闭拥塞控制而保留可靠性。

所以,仅仅从软件模块化的角度来看,这可能不是最好的决定。

路由器辅助的拥塞控制

快速回顾一下TCP的问题:被非拥塞性丢包误导、填满队列导致高延迟、短流并未从拥塞控制中受益太多、AIMD对于高速链路不切实际、锯齿状模式对某些应用不理想、在异构RTT下不公平(我们对此没有真正的解决方案)、与可靠性紧密耦合、以及端主机可以作弊。

这可能看起来是一个很长的列表。你可能会觉得,哇,TCP有这么多问题。实际上,我认为除了端主机可能作弊这一点,其他大多数问题都不是致命问题。我们有处理它们的方法或合理的变通方案,这可能就是TCP能持续这么久的原因。大家觉得这合理吗?

另一个观察是,事实上,我们接下来要讲的最后一部分内容是:大多数这些问题,如果我们能从路由器那里得到一些帮助,就可以非常干净地解决它们,你们中的几个人在整个过程中已经提到了这一点。

例如,我们被丢包误导,或者你必须等待填满队列才能看到丢包。路由器知道它们正在经历的拥塞程度,它们可以直接告诉端主机是否拥塞。同样,如果我们不确定端主机(发送方)应该以什么速率发送,也许路由器可以直接告诉你它可以容纳的速率。路由器可以直接向发送方发送反馈。最后,如果端主机可以作弊,一种策略是让路由器在流之间强制执行隔离,以确保作弊不是一种选择。

这大致上是三种我们称之为路由器辅助拥塞控制的方式。这是一个很大的设计空间,但三种最有帮助的方式是:一是强制执行公平性,这样我们就不依赖发送方做正确的事;二是进行更精确的速率适应,这样发送方就不需要不断搜索正确的速率;最后是提供一种非常直接、明确的拥塞检测方式。

1. 公平队列(Fair Queuing)

我们将从第一个开始,探讨路由器如何确保每个流获得其公平份额。我们一直在诉诸你们对公平的直觉理解,现在我们将最终更严格地定义公平性。

这里的基本思路是,公平性背后的设置是什么?在继续之前,你们

19:DNS I

概述

在本节课中,我们将学习域名系统(DNS)的基础知识。DNS是互联网的一项核心服务,它负责将人类易于记忆的域名(如 www.google.com)转换为计算机用于路由的IP地址(如 172.217.14.206)。我们将从DNS的历史背景和设计目标开始,逐步深入到其工作原理、协议细节以及实际应用。


历史背景与设计目标

上一节我们回顾了互联网的基础架构。本节中,我们来看看DNS出现之前,互联网是如何管理主机名与地址映射的。

早期互联网依赖于一个名为 hosts.txt 的集中式文件。这个文件由网络信息中心(NIC)维护,包含了所有联网主机名及其对应的地址。每个站点需要定期通过FTP下载此文件以更新本地副本。

然而,随着网络规模扩大,这种集中式管理方式暴露出诸多问题:

  • 管理负担重:NIC团队需要手动处理所有新增主机的请求。
  • 分发效率低:文件体积和下载频率随主机数量增长而急剧增加,导致网络流量和更新延迟问题。
  • 存在单点故障:如果NIC的FTP服务器宕机,整个网络将无法获取最新的地址映射。
  • 与互联网去中心化理念不符:集中式管理与互联网开放、分布式的设计原则相悖。

为了解决这些问题,保罗·莫卡派乔斯在1983年设计了域名系统(DNS)。DNS的设计目标包括:

  • 核心功能:将人类友好的名称映射到IP地址。
  • 可扩展性:能够处理海量的域名(如今约3.6亿个)和查询请求。
  • 分布式管理:避免更新瓶颈,允许不同组织管理自己的域名空间。
  • 高可用性:消除单点故障。
  • 高性能:由于大多数网络通信都始于名称查询,因此查询速度至关重要。


DNS的层次化结构

DNS通过三个相互交织的层次结构来解决上述问题。

1. 名称层次
域名本身是层次化的,例如 eecs.berkeley.edu。你可以从任何节点向上追溯到根节点,中间节点(如 berkeley.edu)本身也是有效的名称。

2. 管理权限层次
管理权限是分层委派的。例如,.edu 域由Educause管理,而 berkeley.edu 子域的管理权限则被委派给加州大学伯克利分校,eecs.berkeley.edu 又可以进一步委派给电气工程与计算机科学系。

3. 基础设施层次
DNS服务器本身也构成一个层次结构。根域名服务器知道所有顶级域(TLD)服务器的地址;TLD服务器(如负责 .edu 的服务器)知道其下权威域名服务器的地址;权威域名服务器则存储其负责区域内的具体映射记录。

一个区域对应一个管理权限所负责的连续域名层次部分。例如,伯克利分校的权威服务器负责 berkeley.edu 区域,但它可以将 eecs.berkeley.edu 子区域的权限委派给另一个独立的服务器。


域名解析过程

了解了DNS的层次结构后,我们来看看一次具体的域名查询是如何完成的。这个过程称为迭代解析

以下是解析 repo.eecs.berkeley.edu 的步骤:

  1. 查询者向根域名服务器询问 repo.eecs.berkeley.edu 的地址。
  2. 根服务器不知道答案,但它知道负责 .edu 域的TLD服务器,于是返回一个NS记录,指引查询者去问 .edu 服务器。
  3. 查询者向指定的 .edu TLD服务器询问同样的问题。
  4. .edu 服务器也不知道答案,但它知道负责 berkeley.edu 的权威服务器,于是返回另一个NS记录。
  5. 查询者向 berkeley.edu 的权威服务器询问。
  6. 该服务器知道 eecs.berkeley.edu 子区域已被委派,于是返回负责 eecs.berkeley.edu 的权威服务器的NS记录。
  7. 查询者最终向 eecs.berkeley.edu 的权威服务器询问。
  8. 这台服务器存有 repo.eecs.berkeley.eduA记录,于是返回对应的IP地址。

在实际应用中,用户的设备(主机)通常不会自己完成所有这些迭代查询。相反,它会将查询任务委托给一个递归解析服务器(通常由ISP提供)。主机向递归解析器发送一个递归查询请求,递归解析器则代表主机执行上述迭代查询步骤,并将最终结果返回给主机。根服务器和大多数权威服务器通常只接受非递归/迭代查询


关键问题与解答

上述解析过程引出了三个关键问题:

1. 谁执行查询?
可以是主机自身,但更常见的是主机将递归查询请求发送给递归解析服务器(如ISP提供的DNS服务器),由后者执行迭代查询。

2. 主机如何知道递归解析服务器的地址?
通常通过动态主机配置协议(DHCP)自动获取。也可以手动配置。主机可以配置多个DNS服务器地址以提供冗余。

3. 递归解析服务器如何知道根服务器的地址?
这里存在一个“先有鸡还是先有蛋”的问题。解决方案是:DNS解析软件在发布时,其内部会预配置一组根服务器的IP地址(称为“根提示”)。解析器启动时,先用这些预配置地址查询根服务器,获取最新的根服务器列表,这个过程称为“ priming ”。只要至少有一个预配置地址有效,这个机制就能工作。

另外,为了提高可靠性,DNS规定每个区域必须至少有两台权威服务器作为副本。根服务器也有多个(目前有13个集群,通过任播技术实现全球分布)。


DNS协议与记录类型

现在,让我们深入到协议层面,看看DNS查询和响应具体是如何进行的。

协议基础
DNS主要使用UDP协议,端口53。选择UDP是因为:

  • 节省了TCP三次握手的往返时间。
  • 无需为大量短期查询维护连接状态。
  • 查询和响应通常很小,一个数据包即可容纳。
  • 可靠性通过简单的超时重传机制处理。

DNS也使用TCP端口53,主要用于区域传输(在权威服务器间同步整个区域的数据),因为TCP为大数据量的可靠传输提供了保障。

资源记录
DNS存储和传输的数据单元称为资源记录。它是一个包含多个字段的元组,核心字段包括:

  • 类型:记录的类型,如 ANSCNAME 等。
  • 名称:该记录关联的域名。
  • :记录的具体内容,如IP地址或别名。
  • TTL:生存时间,指示该记录可被缓存多久(秒)。

核心记录类型

  • A记录:将主机名映射到IPv4地址。这是DNS最核心的功能。
    公式主机名 -> IPv4地址
  • NS记录:指定负责某个域或子域的权威域名服务器。
    公式域名 -> 权威DNS服务器主机名

查询示例
以查询 ischool.berkeley.edu 的A记录为例:

  1. 向根服务器查询,根服务器返回 .edu TLD服务器的NS记录及其A记录(附加记录)。
  2. .edu TLD服务器查询,它返回 berkeley.edu 权威服务器的NS记录及其A记录。
  3. berkeley.edu 权威服务器查询,它返回 ischool.berkeley.edu 的A记录及TTL值。


域名的创建

最后,我们了解一下一个域名(如 example.com)是如何诞生的。

以下是创建流程:

  1. 获取IP地址:从ISP处获得一个IP地址块(如 192.0.2.0/25)。
  2. 注册域名:通过ICANN授权的注册商(如GoDaddy)注册 example.com,每年支付约15美元费用。
  3. 设置权威服务器:运行至少两台权威DNS服务器(或使用注册商/托管商提供的服务)。
  4. 告知注册商:将你的权威DNS服务器的主机名和IP地址提供给注册商。
  5. 注册商更新TLD:注册商在 .com TLD服务器的区域文件中,为 example.com 插入指向你的权威服务器的NS记录和A记录。
  6. 配置资源记录:在你自己的权威DNS服务器上,添加所需的资源记录,如 www.example.com 的A记录。

如果想要自己的顶级域(如 .cs168),则需要直接向ICANN申请,费用高昂(约18.5万美元)。


总结

本节课中,我们一起学习了域名系统(DNS)的基础知识。我们从DNS产生的历史原因和设计目标出发,理解了它通过名称、权限和基础设施三个层次结构来解决可扩展性与分布式管理问题。我们详细剖析了域名迭代解析的全过程,以及递归解析服务器在其中扮演的角色。我们还探讨了DNS基于UDP的协议设计、核心的资源记录格式,并了解了A记录和NS记录如何协同工作完成地址解析。最后,我们梳理了一个新域名从注册到生效的完整流程。DNS作为互联网的“电话簿”,其高效、可靠的运行是互联网得以顺畅使用的基础。在接下来的课程中,我们将探索DNS更多样化的用途和记录类型。

20:DNS II

概述

在本节课中,我们将继续深入探讨域名系统(DNS)。我们将重点分析DNS系统需要满足的核心要求,并详细阐述通过增加服务器数量来提升其可用性、可扩展性和性能的四种主要方法。最后,我们将以批判性的视角审视DNS,探讨其设计是否依然适用于当今的互联网。


DNS系统的核心要求

上一节我们介绍了DNS的基本工作原理,本节中我们来看看一个成功的DNS系统需要满足哪些关键要求。

首先,系统必须具备高可用性。我们期望互联网在没有DNS的情况下将难以甚至无法使用,因此DNS服务绝不能宕机。

其次,DNS必须是高度可扩展的。因为它将被频繁使用,需要能够处理海量的域名、请求和组织机构。

最后,DNS必须高性能。鉴于上述期望,它的响应速度必须非常快。

这三个挑战在很大程度上是相互关联的,而我们解决它们的基本思路是一致的:增加更多的服务器。具体而言,我们将通过四种不同的方式来实现这一点。


提升DNS的四种方法

以下是四种通过增加服务器来提升DNS系统表现的主要策略。

1. 域名服务器的独立性

第一种方式我之前已经提到过:不同域的名称服务器是相互独立的。这意味着,例如,berkeley.edu的名称服务器故障不会影响mtholyoke.edu。同样,.edu顶级域名服务器中断也不会波及所有.com域名。

核心概念:DNS层级结构中不同部分的服务器是独立的。

  • 对可用性的影响:层级结构中不同部分的命运互不绑定。
  • 对可扩展性的影响:没有单个服务器需要存储所有域的信息。
  • 对性能的影响:即使某些域名的请求激增导致其性能下降,其他地方的性能也不会受到特别影响。

2. 多台权威名称服务器

第二种方式同样之前提到过:所有域都至少有两台名称服务器。例如,Mount Holyoke学院就有四台权威名称服务器。

核心概念:每个域名配置多台(至少两台)权威服务器。

  • 对可用性的影响:如果一台服务器崩溃,还有其他服务器可以备用。
  • 对可扩展性和性能的影响:查询请求被分散到多台服务器上,有助于均衡负载。

3. 根服务器的任播部署

我们在根名称服务器的背景下看到第三种增加服务器的方式。我之前提到有13个根服务器(字母A到M),但你可能会有疑问:面对45亿互联网用户,13台根服务器真的够用吗?

答案是不够的。例如,仅J根服务器在2018年4月平均每秒就处理66,000次查询。计算下来,每查询只有约15微秒的处理时间。那么,秘诀是什么呢?

核心概念:通过任播技术,每个字母标识的根服务器实际上在全球有数百个副本。例如,J根服务器在2020年有162个副本。所有副本共享同一个IP地址。

# 任播示例:多个地理分布的服务器共享同一IP地址
根服务器 J 的 IP: 2001:503:c27::2:30
副本位置: 北美、南美、欧洲、亚洲、澳大利亚等...

BGP路由协议会自动将数据包路由到“距离”客户端最近的那个副本(这里的“距离”由BGP策略定义)。这种方式称为任播,它可以将数据包交付给多个目的地中的任何一个。

任播的优缺点:

  • 优点:无需特殊的路由变更即可工作;提升可用性(网络分区时自动切换);提升可扩展性(分散负载);提升性能(减少到根服务器的往返时间)。
  • 缺点:类似多宿主,其前缀难以聚合;可能与TCP配合不佳(如果路由变化或客户端移动,TCP连接可能中断)。幸运的是,DNS主要使用简单的UDP交换,因此这对DNS不是问题。

4. 广泛使用缓存

第四种提升性能的方法使用了我们最喜爱的计算机科学技巧之一:缓存。虽然这不完全是“增加服务器”,但与增加解析服务器密切相关。

回顾周二的例子,一次完整的DNS查询可能涉及多次往返,如下图所示:

如何加速?答案是缓存。解析服务器可以缓存它看到的所有回复,并为它服务的所有主机共享这些结果。这样,像google.com这样的常见域名几乎总是能从缓存中获取,无需进行完整的迭代查询。

核心概念:DNS资源记录中的TTL字段指定了该记录可以被缓存的时间。

# DNS资源记录示例
www.example.com.    300    IN    A    93.184.216.34
# TTL为300秒,表示此记录可缓存5分钟

缓存服务器可以部署在层级结构的任何地方,甚至主机本身也可以缓存结果。

  • 对可用性的影响:即使在网络暂时中断时,常见查询仍可由缓存提供服务。
  • 对可扩展性的影响:减少对其他服务器的负载。
  • 对性能的影响:消除了常见查询的多轮往返延迟;如果缓存靠近客户端(如本地解析服务器),还能减少到服务器的往返时间。

总结

本节课中我们一起学习了如何通过增加服务器来提升DNS的可用性、可扩展性和性能。我们探讨了四种主要方法:

  1. 保持域名服务器的独立性。
  2. 为每个域部署多台权威服务器。
  3. 对根服务器使用任播技术进行全球复制。
  4. 在各级广泛使用缓存。

总体而言,更多的服务器意味着可能有一台离你更近(降低延迟),单台服务器因高负载而变慢的可能性更小,数据在多个地方有副本(高可用性),并且单台服务器需要存储的数据量减少。


对DNS的批判性思考

现在,让我们以一些批判性的问题来结束关于DNS的讨论。DNS通常被视为互联网不可或缺的基石,但我们不妨退一步思考:我们命名的对象是否正确?DNS工作得好吗?它是否危及隐私?DNS真的那么重要吗?

我们命名了正确的东西吗?

回顾早期互联网的三大“杀手级应用”:

  1. Telnet:用于远程登录主机。此时命名主机是合理的。
  2. FTP:用于文件传输。它结合了主机名和文件名。但如果我们不关心文件具体存放在哪台主机上,或许命名内容本身(数据)比命名主机更好。这正是“命名数据网络”等研究项目探索的方向。
  3. 电子邮件:基于“用户@主机”的模式。但如果用户更换了主机呢?今天,gmail.com背后是无数主机。或许为(而非邮箱提供商)设计独立的命名系统会更好。

对于后来的万维网,URL本质上是“主机名+文件名”。现代Web服务更是动态的机器集群。我们键入facebook.com,但访问的并非某一台特定主机。这让人思考,我们是否应该命名服务,而非主机?

小结:对于Telnet,命名主机完全合理。但对于文件、人和服务,我们不得不将它们与主机名人为绑定。如果重新设计,我们或许能为这些用例设计出更好的方案。

DNS工作得怎么样?

这是一个宽泛的问题。一项2019年的研究分析了J根服务器一天内近60亿次查询,发现:

  • 61%的查询是针对不存在的顶级域。
  • 38%的查询是来自同一源的重复查询(本应被缓存)。
  • 只有约0.6%的查询是“有效”查询。

这揭示了两个问题:缓存对于错误查询效果不佳;我们构建的这套强大、可扩展的系统,其大部分精力都在处理“垃圾”请求。不过,从用户角度看,DNS大多时候工作正常。

DNS与隐私

当浏览加密网站时,通信内容受到保护,但DNS查询本身通常不加密。这使得ISP等第三方可以轻易记录用户访问的站点。近年来,出现了如DNS over HTTPS等加密DNS方案以增强隐私。然而,这又将信任问题转移给了提供加密DNS服务的机构(如Cloudflare)。如何在隐私和信任之间取得平衡,仍是正在发展的议题。

DNS真的重要吗?

请思考:你上次手动输入一个域名是什么时候?对于许多人,可能不是今天或昨天。我们使用互联网的方式已经改变:

  • 获取天气?打开App,而非telnet到某个主机。
  • 浏览网页?起点通常是搜索引擎,浏览器地址栏更像搜索栏。

可记忆、可键入的地址似乎不再那么重要。可读性甚至可能带来错误的安全感,攻击者常利用相似的域名进行钓鱼(例如wellsfargo.com vs wellsfargo.com)。通过搜索访问可能比直接输入或点击链接更安全。

此外,DNS的许多现代用途(如通过多个A记录实现负载均衡、作为间接层动态将用户导向最近的数据副本)与其最初“人类可读的主机名”的驱动因素关系不大。

最终思考:有观点认为DNS的原始目的未能完全经受住时间考验。然而,它发展出的这些间接层功能对当今互联网至关重要。这引出一个问题:如果我们抛开人类可读性的要求,重新设计一个系统来解决这些问题,能否做得更好?


本节课中我们一起学习了DNS系统为满足高要求而采用的多种架构策略,并以发展的眼光对其设计初衷和现代角色进行了反思。

21:Web

概述

在本节课中,我们将学习万维网(Web)的基础知识。我们将从Web的诞生背景和核心思想开始,逐步深入到其关键技术组件,包括HTML、URL、HTTP协议以及浏览器。随后,我们将探讨如何通过缓存、内容分发网络(CDN)和TCP优化来提升Web的可用性、可扩展性和性能。最后,我们将对所学内容进行总结。


Web的诞生与核心思想

上一节我们介绍了DNS,它为用户提供了基于名称而非地址的网络交互方式。本节中,我们来看看万维网,它真正成为了互联网的“面孔”,并彻底改变了世界。

万维网的成功源于其设计理念。它并非试图强制规定数据的结构或存储方式,也不依赖于特定的计算机或数据库系统。相反,它提供了一个非常抽象的模型:由链接(超链接)连接的节点(内容)。这种抽象性使得Web能够构建图书馆、银行、商店等各种应用。

Web是去中心化的,这意味着任何人都可以通过购买或租用服务器和互联网连接,将自己的内容或服务添加到Web上。它让我们能够快速浏览来自不同来源的所有内容和服务,并可以即时组织和重组它们。

Web的基础组件

了解了Web的核心理念后,我们来看看构成它的具体技术组件。一个完整的Web系统需要满足几个基本要求。

以下是构成Web的核心技术:

  1. HTML(超文本标记语言):用于表示包含链接的内容。
    <html>
      <body>
        <h1>欢迎</h1>
        <p>这是一个<a href="https://example.com">链接</a>。</p>
      </body>
    </html>
    
  2. Web浏览器:用于访问、导航和显示内容的客户端程序。
  3. URL(统一资源定位符):用于引用内容,是链接或嵌入其他内容的方式。
  4. Web服务器:用于托管内容。
  5. HTTP(超文本传输协议):用于从服务器获取内容到客户端的协议,本质上是将URL转换为TCP连接的方式。

URL的通用格式为:<协议>://<主机名>[:端口]/<路径>[?查询][#片段]。例如,https://www.example.com:443/path/to/page.html?search=term#section

HTTP协议详解

我们已经了解了Web的各个组成部分是如何协同工作的。现在,我们将重点深入探讨其中的关键协议:HTTP。

HTTP采用客户端-服务器架构。客户端使用TCP连接到服务器的知名端口(通常是80端口)。客户端发出请求,服务器返回响应。该协议本身是无状态的。

一个基本的HTTP交换流程如下:

  1. 客户端在端口80上发起TCP连接。
  2. 客户端发送一个HTTP请求。
  3. 服务器发送一个HTTP响应。
  4. 客户端确认收到响应。
  5. 服务器关闭连接。

HTTP请求与响应

HTTP请求和响应都是纯文本、人类可读的,行之间由CRLF(回车换行)分隔。

一个典型的HTTP GET 请求如下:

GET /about.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html
  • 请求行:包含方法(如GET)、资源路径和协议版本。
  • 请求头:提供额外信息或修改请求。
  • 空行:分隔头部和可选的正文。
  • 正文:用于提交数据(如POST请求的表单内容)。

对应的HTTP响应如下:

HTTP/1.1 200 OK
Date: Mon, 23 May 2022 22:38:34 GMT
Server: Apache
Content-Type: text/html; charset=UTF-8
Content-Length: 138

<html>
<body>
<h1>关于我们</h1>
</body>
</html>
  • 状态行:包含协议版本、状态码(如200表示成功)和原因短语。
  • 响应头:提供关于响应的额外信息。
  • 空行:分隔头部和正文。
  • 正文:包含请求的资源内容(如HTML文档)。

常见的HTTP方法包括:

  • GET:获取资源。
  • POST:向服务器提交数据。
  • HEAD:类似于GET,但只返回头部,不返回正文。

常见的状态码类别包括:

  • 2xx:成功(如200 OK)。
  • 3xx:重定向(如304 Not Modified,用于缓存验证)。
  • 4xx:客户端错误(如404 Not Found)。
  • 5xx:服务器错误(如500 Internal Server Error)。

提升Web性能:缓存、CDN与TCP优化

掌握了HTTP的基础后,我们从用户和内容提供者的角度来思考:我们需要Web快速且高度可用。这与我们在DNS课程中讨论的目标非常相似。我们将使用类似的思想——复制和缓存——来解决这些问题,并同时解决一些TCP相关的问题。

Web缓存

缓存之所以有效,主要是基于时间局部性原理:如果访问了一个资源,很可能很快会再次访问它。

HTTP通过请求和响应中的头部字段来实现缓存控制。关键的头部包括:

  • Cache-Control: max-age=<秒数> (HTTP/1.1):指定资源可以被缓存多长时间(生存时间,TTL)。
  • Expires: <日期> (HTTP/1.0):指定资源的绝对过期时间。
  • Cache-Control: no-cache:客户端使用此头部表示希望跳过缓存,直接向源服务器请求。
  • If-Modified-Since: <日期>:客户端在缓存资源过期后,使用此头部向服务器询问资源是否已修改。如果未修改,服务器返回 304 Not Modified 状态码,从而节省带宽。

缓存可以位于多个位置:

  1. 浏览器缓存:每个浏览器都有自己的本地缓存。
  2. 代理服务器缓存
    • 正向代理:位于客户端附近(如企业或ISP网络),为多个客户端服务,减少网络流量和延迟。
    • 反向代理:位于服务器附近,减轻源服务器的负载。

内容分发网络

如果内容提供者希望确保其内容始终被复制并靠近客户端,可以使用内容分发网络。CDN是一个大型分布式存储基础设施。

CDN的工作原理通常涉及DNS技巧:

  1. 内容提供者(如CNN)购买CDN服务。
  2. CDN为客户的资源创建新的域名(如 e12596.dscj.akamaiedge.net)。
  3. 内容提供者将其网页中的资源URL“重写”为CDN的域名。
  4. 当客户端请求这些资源时,DNS查询会将客户端引导至CDN网络中离它最近、负载最轻的服务器。

内容进入CDN的方式有两种:

  • 拉取:CDN服务器像缓存一样工作,在未命中时从源服务器拉取内容。
  • 推送:内容提供者主动将内容上传到CDN,由CDN分发到其所有服务器。

HTTP与TCP的交互优化

许多网页由多个小对象(如图片、CSS、JS)组成。如果为每个小对象单独建立TCP连接,由于往返时间(RTT)占主导,性能会非常差。

以下是几种优化策略:

  1. 并发连接:浏览器同时打开多个TCP连接(例如6个)来并行下载对象。
  2. 持久连接:在同一个TCP连接上发送多个HTTP请求/响应,避免为每个对象重新建立连接。这可以将下载N个小对象的时间从 2N 个RTT减少到 N+1 个RTT。
  3. 管道化:在持久连接的基础上,不等待响应就连续发送多个请求。理论上这可以将时间减少到2个RTT(建立连接+所有请求/响应)。但由于实现复杂和队头阻塞问题(一个慢请求会阻塞同一连接上的后续请求),实践中已被弃用。
  4. 多路复用:HTTP/2引入的技术,在单个连接内创建多个“流”,有效解决了队头阻塞问题,是当前的主流优化方式。

公式:估算下载时间
假设下载 N 个小对象,每个对象的传输时间可忽略,主要耗时是RTT。

  • 非持久连接:时间 ≈ 2N × RTT
  • 持久连接:时间 ≈ (N + 1) × RTT
  • K 个并发持久连接:时间 ≈ (N/K + 1) × RTT

总结

本节课中,我们一起学习了万维网的核心架构。我们从Tim Berners-Lee解决CERN信息管理问题的初衷开始,理解了Web去中心化、抽象链接模型的设计哲学。我们剖析了Web的基础组件:用HTML表示内容,用URL定位资源,用HTTP协议在客户端和服务器之间传输数据,并由浏览器呈现给用户。

随后,我们深入探讨了如何保证Web的可用性、可扩展性和性能。我们学习了缓存机制如何利用时间局部性提升效率,包括浏览器缓存和代理缓存。我们介绍了内容分发网络(CDN)如何通过全球分布的服务器和智能DNS将内容推送到用户附近。最后,我们分析了HTTP与TCP的交互,以及如何通过并发连接、持久连接和多路复用来克服由小对象和RTT导致的性能瓶颈。

万维网的成功在于其简单的核心思想与强大的可扩展性相结合,使其成为了塑造现代世界的基石应用。

22:以太网与端到端操作

在本节课中,我们将学习以太网(Ethernet)的核心概念,包括其地址结构、服务类型,以及它与网络层(L3)协议(如IP)如何协同工作。我们还将探讨ARP和DHCP这两个关键协议,并通过一个详细的端到端示例,展示从主机启动到网页下载的完整数据包流程。

以太网简介与历史背景

以太网是目前应用最广泛的第二层(L2)网络技术。在深入其技术细节之前,我们先了解一下它的历史渊源。

以太网的设计灵感部分来源于一个名为ALOHAnet的早期无线网络系统。1972年,罗伯特·梅特卡夫(Robert Metcalfe)在施乐公司(Xerox)工作时,负责为Alto个人计算机和激光打印机设计一个廉价的连接网络。他借鉴了ALOHAnet的共享介质和随机接入思想,创造了最初的以太网。

共享介质与多路访问协议

在像ALOHAnet这样的无线电网络或早期以太网中,所有节点都使用一个共享的传输介质。这意味着不同节点的传输可能会相互干扰或碰撞,导致通信混乱。因此,需要一个多路访问协议来协调介质的使用。

以下是几种主要的多路访问方法:

  • 固定划分:将介质按频率或时间固定划分给每个节点。例如,每个节点获得一个专用频率或一个固定的时间槽。这种方法在流量突发时可能效率低下,造成资源浪费。
  • 轮流访问:节点轮流使用介质。
    • 轮询:由一个协调器决定谁在特定时间发送。
    • 令牌传递:一个虚拟令牌在网络中传递,只有持有令牌的节点才能发送。
  • 随机访问:这是ALOHAnet开创并被以太网采用的方法。节点在需要发送时直接尝试发送,如果发生碰撞,则等待一段随机时间后重试。

从ALOHA到经典以太网

ALOHAnet使用了一种简单的随机接入方案:远程站点有数据包就直接发送;中心站成功接收后回复确认;如果发生碰撞(无确认),发送方等待一段随机时间后重传。

罗伯特·梅特卡夫将以太网设计为使用单一同轴电缆作为共享介质,这比为每台计算机单独拉线要便宜得多。以太网在ALOHA的基础上进行了改进,引入了CSMA/CD协议。

CSMA/CD代表载波侦听多路访问/碰撞检测,其核心思想是:

  1. 载波侦听:发送前先“听”线路上是否有其他节点在发送(即感知载波信号)。只有在安静时才开始发送,这比ALOHA的“直接发送”更礼貌。
  2. 碰撞检测:由于信号传播延迟,两个节点可能同时侦听到“安静”并开始发送,导致碰撞。因此,节点在发送时也持续“听”。如果检测到碰撞,立即停止发送,并等待一段随机时间后重试。

为了在高竞争环境下减少连续碰撞,以太网使用了二进制指数退避算法:每次重传碰撞后,等待的随机时间上限会加倍(例如,第一次碰撞后等待0或1个时间单位,第二次碰撞后等待0、1、2或3个时间单位,以此类推)。这使得网络在可能时快速传输,在必要时减缓速度。

以太网地址与服务类型

在共享介质以太网中,发送的数据所有节点都能收到。因此,第二层地址的作用不是定位,而是标识接收者,就像一个在黑暗房间里喊出的名字。

  • 地址格式:以太网地址(MAC地址)为48位(6字节),通常表示为用冒号或连字符分隔的六组两位十六进制数(例如:00:50:B6:01:23:45)。
  • 地址结构:前24位通常标识制造商,后24位由制造商分配,用于标识具体设备。理论上,这保证了全球唯一性。
  • 地址性质:地址通常“烧录”在网络接口硬件中,难以更改,因此常被印在设备上。

以太网支持多种服务类型:

  • 单播:发送给一个特定目标。这是默认模式。
  • 广播:发送给同一以太网段上的所有节点。通过使用全为1的地址(FF:FF:FF:FF:FF:FF)实现。
  • 组播:发送给一个特定组的所有成员。通过使用特殊的组播地址实现(地址的第一个字节的最低有效位为1)。广播地址是组播地址的一个特例。

组播的一个常见应用是mDNS,例如苹果的AirPlay或网络打印机发现,设备通过向特定组播地址发送查询来发现本地网络上的服务。

现代交换式以太网

经典的共享介质以太网已很少使用,现代以太网主要是交换式以太网

  • 关键变化:链路上只有两个节点(如主机-交换机、交换机-交换机),使用全双工链路,因此没有碰撞,也不再需要CSMA/CD。
  • 语义等价:虽然物理实现不同,但交换式以太网通过泛洪实现了与共享介质相同的语义——当一个帧的目的地未知或是广播/组播帧时,交换机会将其转发到所有其他端口(除了接收端口)。因此,发送的帧仍然能被网络中的所有节点“听到”。
  • 优化学习型交换机通过记录源地址和端口的对应关系来优化单播流量,后续发往已知地址的帧可以直接转发到特定端口,而无需泛洪。

广播、未知单播和组播帧在交换式以太网中被统称为BUM流量,它们都通过泛洪处理。

第二层与第三层的交互

IP协议的目的是将许多独立的网络(如以太网)组合成一个互联网。这些独立网络在IP语境下常被称为子网

数据包从源主机到目的主机的旅程,是第二层和第三层转发交替进行的过程:

  1. 子网内通信:如果源主机和目的主机在同一子网,数据包完全在第二层转发。源主机使用ARP协议获取目的主机的MAC地址,然后将数据帧直接发送到本地网络。
  2. 跨子网通信:如果目的主机在不同子网,数据包必须通过路由器(第三层设备)转发。
    • 源主机将数据帧发送给其默认网关(即本地路由器),使用路由器的MAC地址作为第二层目的地址。
    • 路由器检查第三层IP头部,根据路由表决定下一跳,然后将数据包重新封装进一个新的第二层帧中(源MAC地址变为路由器出口的MAC地址,目的MAC地址变为下一跳设备或最终目的主机的MAC地址),发送出去。
    • 这个过程在路径上的每个路由器重复,直到数据包到达目的主机所在的子网。

关键点:在传输过程中,第二层地址(MAC地址)在每一跳都会改变,而第三层地址(IP地址)从源到目的地保持不变(除非经过NAT)。

地址解析协议与动态主机配置协议

要使上述过程工作,主机需要知道一些关键信息:自己的IP地址、子网掩码、默认网关的IP地址等。这通过两个协议实现:

  • ARP:用于在同一子网内,根据已知的IP地址查询对应的MAC地址。主机广播一个“谁有这个IP?”的ARP请求,拥有该IP的主机则单播回复其MAC地址。结果会被缓存以提高效率。
  • DHCP:用于主机自动获取网络配置信息。过程如下:
    1. 发现:主机广播DHCP Discover消息。
    2. 提供:DHCP服务器广播DHCP Offer消息,提供一个可用的IP地址租约及其他配置。
    3. 请求:主机广播DHCP Request消息,选择其中一个提供。
    4. 确认:服务器广播DHCP Ack消息,确认租约生效。

DHCP提供的信息包括:IP地址、子网掩码、默认网关、DNS服务器地址等。

端到端操作示例

让我们通过一个综合示例,将所学知识串联起来。假设主机H1刚启动,需要完成以下操作:

  1. 通过DHCP获取自身配置。
  2. 从远程服务器 h5.com 下载一个小文件。
  3. 从本地服务器 h2.com 下载两个小文件。

以下是H1发送和接收的主要数据包序列:

阶段一:启动与配置 (DHCP)

  1. H1 -> 广播: DHCP Discover (UDP)
  2. 广播 -> H1: DHCP Offer (UDP, 来自DHCP服务器)
  3. H1 -> 广播: DHCP Request (UDP)
  4. 广播 -> H1: DHCP Ack (UDP, 来自DHCP服务器)
    (H1现在获得了IP地址、子网掩码、默认网关R1的IP地址、DNS服务器H3的IP地址)

阶段二:获取 h5.com 文件
5. H1 -> 广播: ARP Request (询问“谁是DNS服务器H3的MAC地址?”)
6. H3 -> H1: ARP Reply (单播回复其MAC地址)
7. H1 -> H3: DNS Query for h5.com (UDP)
8. H3 -> H1: DNS Reply with IP of H5 (UDP)
(H1得知H5的IP地址,并判断其不在本地子网,需通过网关R1)
9. H1 -> 广播: ARP Request (询问“谁是网关R1的MAC地址?”)
10. R1 -> H1: ARP Reply (单播回复其MAC地址)
11. H1 -> R1 (目的MAC), 最终到H5 (目的IP): TCP SYN (建立连接)
12. H5 -> H1: TCP SYN-ACK
13. H1 -> H5: TCP ACK
14. H1 -> H5: HTTP GET (请求文件)
15. H5 -> H1: TCP ACK (确认收到GET请求)
16. H5 -> H1: HTTP Response (携带文件数据)
17. H1 -> H5: TCP ACK (确认收到HTTP响应)
(连接空闲一分钟后关闭)
18. H1 -> H5: TCP FIN
19. H5 -> H1: TCP ACK
20. H5 -> H1: TCP FIN
21. H1 -> H5: TCP ACK

阶段三:获取 h2.com 文件
(注意:DNS服务器H3的MAC地址已在ARP缓存中)
22. H1 -> H3: DNS Query for h2.com (UDP)
23. H3 -> H1: DNS Reply with IP of H2 (UDP)
(H2在本地子网)
24. H1 -> 广播: ARP Request (询问“谁是H2的MAC地址?”)
25. H2 -> H1: ARP Reply
26. H1 -> H2: TCP SYN
27. H2 -> H1: TCP SYN-ACK
28. H1 -> H2: TCP ACK
29. H1 -> H2: HTTP GET (第一个文件)
30. H2 -> H1: TCP ACK
31. H2 -> H1: HTTP Response
32. H1 -> H2: TCP ACK
33. H1 -> H2: HTTP GET (第二个文件,复用同一TCP连接)
34. H2 -> H1: TCP ACK
35. H2 -> H1: HTTP Response
36. H1 -> H2: TCP ACK
(连接空闲一分钟后关闭)
37. H1 -> H2: TCP FIN
38. H2 -> H1: TCP ACK
39. H2 -> H1: TCP FIN
40. H1 -> H2: TCP ACK

总结

本节课中,我们一起学习了以太网的核心原理及其在互联网架构中的角色。我们从其共享介质的历史和CSMA/CD协议讲起,探讨了现代交换式以太网如何通过泛洪和学习机制保持语义一致性。我们深入分析了第二层MAC地址与第三层IP地址的区别与联系,并详细介绍了ARP和DHCP这两个实现网络自动配置和地址解析的关键协议。最后,通过一个从主机启动到完成网页下载的端到端示例,我们清晰地看到了数据包如何在不同网络层间协作,穿越子网,最终完成通信任务。理解这些基础协议的交互,是掌握互联网实际工作原理的重要一步。

23:端到端操作回顾

在本节课中,我们将学习互联网端到端通信是如何工作的,特别是第二层(L2,如以太网)和第三层(L3,如IP)如何协同工作。我们将通过回顾核心概念和实际演示来理解数据包从源到目的地的完整旅程。


地址:L2与L3

上一节我们介绍了网络分层,本节中我们来看看L2和L3各自使用的地址。

L2和L3有各自独立的地址结构和转发方式,但它们必须协同工作。每个数据包在网络中传输时都同时携带L2和L3的头部信息。

以下是L2(以太网MAC地址)和L3(IPv4地址)的关键区别:

  • 来源:MAC地址通常硬编码在网络接口硬件中。IP地址则由网络运营商根据设备在网络拓扑中的位置动态分配(如通过DHCP)或手动配置。
  • 结构:MAC地址是扁平的,不利于聚合。IP地址是分层的,可以进行聚合(例如,将多个IP地址合并为一个前缀)。
  • 可移植性:MAC地址随设备移动。IP地址则取决于你连接的网络,移动后会改变。
  • 作用:MAC地址用于在同一本地网络(如一个以太网)内的设备间通信。IP地址是互联网的端到端寻址方案,用于跨网络通信。

这些特性是相互关联的。例如,MAC地址硬编码在硬件中,意味着它随设备移动,这导致无法对其进行有效的聚合分配。

为什么需要两种地址?

  • 为什么不能只用MAC地址作为全局标识? 因为MAC地址无法聚合,互联网上有数十亿设备,如果路由表需要为每个MAC地址都维护一条记录,这将导致无法扩展
  • 为什么不能只用IP地址,跳过L2? 虽然理论上可能,但L2(如以太网)提供了非常便利的“即插即用”引导机制。当一台主机启动时,它只知道自己的MAC地址。L2的广播发现机制(如ARP、DHCP)使得主机能够自动发现其IP地址、第一跳路由器等信息,从而顺利引导L3。如果没有L2,IP协议本身需要增加复杂的广播和发现机制,这实际上会重新发明L2的许多功能。此外,历史原因也起了作用,在互联网发展初期,已经存在许多以太网网络。

引导与发现协议

主机启动时,只知道自己硬编码的MAC地址。它需要通过发现协议来获取所有其他必要信息才能与远程主机通信。

以下是两个关键的引导协议,它们都运行在主机所在的本地L2网络内,并严重依赖广播能力(这在本地网络内是可行且方便的)。

地址解析协议

ARP用于将同一本地网络内的IP地址解析为对应的MAC地址

其操作非常简单:

  1. 发起主机广播一个ARP请求包,询问“谁拥有这个IP地址?”。
  2. 网络上的所有主机都会听到这个广播。
  3. 拥有该IP地址的主机(或路由器接口)会单播回复一个ARP响应,告知其MAC地址。

动态主机配置协议

DHCP用于主机启动时,动态获取其L3(IP层)的配置信息。

以下是DHCP的工作流程:

  1. DHCP发现:客户端广播“我需要配置”。
  2. DHCP提供:DHCP服务器(可能有多台)响应一个“提供”,包含建议的IP地址、子网掩码、默认网关、DNS服务器地址等。
  3. DHCP请求:客户端选择一个“提供”,并广播“我请求使用这个配置”。
  4. DHCP确认:服务器最终确认,客户端正式获得该IP地址的租约。

关键点

  • DHCP基于UDP/IP。在客户端获得IP地址之前,它使用特殊的广播IP地址(如255.255.255.255),该地址会转换为广播以太网地址,从而实现L2层的广播。
  • 为了效率和状态管理,ARP和DHCP都使用缓存。ARP结果会被缓存一段时间;DHCP获得的IP地址有租约期限,客户端需要定期续租,否则服务器会回收地址。

数据包转发:L2与L3的协同

现在我们已经了解了地址和引导过程,本节中我们来看看数据包在实际转发过程中,L2和L3是如何协同工作的。请记住,每个数据包都同时包含L2和L3的头部

从主机发送数据包

假设主机A想发送一个IP数据包给主机B。以下是发生在主机A上的步骤:

  1. L3决策:主机A的IP层(L3)查看目标IP地址(B的IP)。
  2. 判断是否在同一网络:主机A将自己的IP地址和子网掩码与目标IP地址进行比较。
    • 如果在同一网络,则“下一跳”就是目标主机B本身
    • 如果不在同一网络,则“下一跳”是默认网关(第一跳路由器)
  3. 确定下一跳的IP地址:此时,主机A知道了下一跳的IP地址(要么是B,要么是路由器)。
  4. L2封装:主机A将数据包交给L2(以太网)。L2需要知道下一跳IP地址对应的MAC地址
    • 如果不知道,则使用ARP进行查询。
  5. 发送:L2将数据包封装成以太网帧,源MAC地址是A的MAC目标MAC地址是下一跳的MAC,然后发送出去。

关键规则:在数据包的端到端路径中,IP地址(源和目的)始终保持不变,而MAC地址在每一跳(每一个L2网络段)都会改变

数据包到达路由器

现在,假设数据包到达了一个路由器(例如主机A的默认网关R1)。

  1. L2接收:数据包到达路由器R1的某个接口。路由器检查以太网帧头中的目标MAC地址
  2. 判断是否给自己
    • 如果目标MAC地址匹配该接口的MAC地址,说明这个L2帧就是发给路由器本身的。路由器剥离L2头部,将IP数据包向上传递给L3处理。
    • 如果目标MAC地址不匹配,则路由器是在作为一台L2交换机进行工作(这种情况在连接多台设备的路由器接口上可能发生),它会根据MAC地址表进行L2转发。在简单的点对点链路或主机-路由器链路上,这通常不会发生。
  3. L3路由决策:路由器查看IP数据包头的目标IP地址
  4. 查找路由表:路由器在其IP路由表中进行最长前缀匹配,决定从哪个出站接口发送数据包,以及下一跳的IP地址是什么。
  5. 重新进行L2封装:路由器确定了出站接口和下一跳IP地址后,过程回到“从主机发送数据包”的步骤4和5。路由器会为这个数据包构建一个新的L2帧
    • 源MAC地址:路由器出站接口的MAC地址。
    • 目标MAC地址:下一跳IP地址对应的MAC地址(通过ARP或配置获得)。
  6. 转发:路由器将新的L2帧从出站接口发送出去。

这个过程在路径上的每一台路由器上重复,直到数据包到达目标主机所在的本地网络。最终,目标主机会收到一个目标MAC地址是自己的数据包,剥离L2头后,将IP数据包向上传递给传输层和应用层。


实例分析

让我们通过一个具体例子来巩固理解。考虑一个简单网络:Soda Hall有一个共享以太网,Cory Hall有一个交换式以太网,它们通过路由器R1连接。

场景1:Soda Hall内主机A发给主机B(同一网络)

  • 路径:A -> B(直接通过共享以太网)。
  • 数据包信息
    • L3:源IP = A的IP, 目的IP = B的IP。
    • L2:源MAC = A的MAC, 目的MAC = B的MAC。

场景2:Soda Hall的主机A发给Cory Hall的主机C(跨网络)

  • 路径:A -> R1 -> ... -> C。
  • 在A到R1的链路上
    • L3:源IP = A的IP, 目的IP = C的IP。
    • L2:源MAC = A的MAC, 目的MAC = R1接口(连接Soda Hall侧)的MAC。
  • 在R1到C的链路上(例如,在Cory Hall的交换网络内):
    • L3:源IP = A的IP, 目的IP = C的IP。(不变
    • L2:源MAC = R1接口(连接Cory Hall侧)的MAC, 目的MAC = C的MAC。

这个例子清晰地展示了IP地址的端到端不变性,以及MAC地址的逐跳变化。


实践演示:使用Wireshark观察网络流量

理论需要结合实际观察。本节中我们将使用网络分析工具Wireshark来观察真实的网络操作。

演示1:观察DHCP和ARP

  1. 断开并重连Wi-Fi:在Wireshark中开始捕获无线网卡流量,然后打开电脑Wi-Fi。
  2. 观察流量:你会看到大量数据包。通过过滤可以找到关键流程:
    • DHCP交互:可以看到DHCP Discover, Offer, Request, Ack四个报文,其中包含了分配的IP地址、网关、DNS服务器和租约时间。
    • ARP请求:在获得IP后,电脑可能会发送ARP请求来解析网关的MAC地址。
  3. 查看结果:在终端使用命令如 ifconfig(查看IP和MAC)、netstat -rn(查看路由表)、arp -a(查看ARP缓存)来验证获取到的配置信息。

演示2:观察HTTP/TCP流量

  1. 访问一个简单网页:在浏览器中打开一个自己搭建的或已知的简单HTTP(非HTTPS)网站。
  2. 使用浏览器开发者工具:在“网络”标签页中,可以清晰地看到浏览器发起的每一个HTTP请求(如HTML文件、图片),以及请求/响应的头部信息、状态码(如200成功、304未修改)、时间线等。
  3. 使用Wireshark进行包级观察
    • 在Wireshark中捕获流量,并设置过滤器(例如 tcp.port == 80 && ip.addr == <服务器IP>)来聚焦于与该网站的HTTP通信。
    • 你可以观察到:
      • TCP三次握手SYN, SYN-ACK, ACK
      • HTTP请求/响应:在TCP连接建立后的数据包中,可以看到明文的HTTP GET请求和服务器返回的HTTP/1.1 200 OK响应及HTML内容。
      • TCP流控制:可以查看序列号、确认号、窗口大小等TCP字段。
      • 连接终止FIN包等。

注意:对于HTTPS网站,应用层数据是加密的,因此在Wireshark中看不到HTTP明文,但依然可以观察TCP和TLS握手过程。


总结

本节课中我们一起学习了互联网端到端操作的核心原理。我们回顾并厘清了L2(MAC地址,以太网)和L3(IP地址)在寻址、转发中的不同角色与协同工作方式。我们了解了主机如何通过ARP和DHCP协议完成初始引导。更重要的是,我们一步步分析了数据包从源主机发出,经过路由器逐跳转发,最终到达目的主机的完整过程,明确了IP地址端到端不变MAC地址逐跳变化的关键原则。最后,通过Wireshark的实际演示,我们将理论知识与真实的网络流量联系起来,加深了对HTTP、TCP等协议在实际中如何运作的理解。掌握这些基础概念是理解更复杂网络协议和应用的基础。

24:软件定义网络 (SDN)

概述

在本节课中,我们将要学习软件定义网络(SDN)。SDN 是一种管理网络的方法,它并非革命性的技术,而是一种组织网络功能的方式。我们将探讨 SDN 为何出现、它解决了什么问题,以及其背后的核心设计理念。课程将从背景介绍开始,逐步深入到 SDN 的具体架构和抽象概念。


课程背景与动机

首先,我们需要理解 SDN 诞生的背景。在 2004 年左右,多个研究团队(如普林斯顿、CMU、斯坦福、伯克利)以及谷歌、微软等工业界公司,都在探索类似 SDN 理念的网络管理新方法。这些想法在 2008 年左右汇聚,并被《麻省理工科技评论》命名为“软件定义网络”。

SDN 的快速采纳(例如,2011年成立了拥有众多成员的开放网络基金会)表明它必定解决了某个强烈的痛点。这个痛点主要有两方面:一是思科(Cisco)在当时网络设备市场的绝对主导地位,其他厂商渴望能削弱其优势的新技术;二是更根本的原因——传统网络管理变得异常复杂和困难。

上一节我们介绍了 SDN 出现的时代背景,本节中我们来看看网络管理本身面临的挑战。

网络管理的困境

什么是网络管理?在网络中,我们通常区分两个平面:

  • 数据平面:负责根据本地转发表状态处理每个数据包。例如,一个数据包到达,路由器查看其目的地址,查询路由表,然后从相应端口转发出去。这个过程基于本地信息,功能明确。
  • 控制平面:负责计算和设置数据平面所使用的转发状态。这通常需要了解整个网络的拓扑等信息,并涉及设备间的协调。

网络管理,在这里我们将其定义为与控制平面相关的一切,即如何设置网络设备的状态。

互联网早期,控制平面的目标很简单:实现基本连通性(域内路由)和符合策略的域间连通性(BGP)。然而,随着网络发展,控制平面需要实现的目标变得复杂多样。

以下是几个使网络管理复杂化的目标示例:

  • 逻辑网络(VLAN)隔离:为了限制广播域范围并实现资源隔离,虚拟局域网(VLAN)被广泛使用。但 VLAN 的配置通常通过脚本或手动完成,缺乏自动化的分布式算法。
  • 访问控制:出于安全考虑,网络需要使用访问控制列表(ACL)来限制流量。例如,禁止来自大堂 Wi-Fi 的访问连接到核心服务器。ACL 的配置也通常是手动或脚本化的。
  • 流量工程:在大规模网络中,需要根据流量矩阵计算路由,以避免链路过载。这通常通过集中式计算完成,然后将其推送到网络设备。

总而言之,现代网络的控制平面需要处理众多目标,但实现这些目标的机制(分布式算法、手动脚本、集中式计算)各自为政,缺乏统一的设计和良好的抽象。控制平面变成了一个功能平庸的复杂混乱体,这与简单高效、数十年保持不变的数据平面形成了鲜明对比。

那么,在 SDN 出现之前,我们是如何应对这种复杂性的呢?答案是依靠网络管理员。他们凭借专业知识,将所有这些复杂细节记在脑中,手动管理和配置网络。这种“掌控复杂性”的能力是一种祝福,但也是一种诅咒。

从“掌控复杂性”到“提取简单性”

“掌控复杂性”意味着深入系统内部,理解并操作所有细节使其工作。这在系统构建初期是必要的。然而,当目标是让系统易于使用时,我们需要的是“提取简单性”——即定义清晰的抽象接口,让使用者只需关注高层目标,而将底层复杂实现隐藏起来。

一个经典的类比是汽车的手动变速箱(手动挡)和自动变速箱(自动挡)。喜欢驾驶手动挡的人享受的是“掌控复杂性”的乐趣;而自动挡的设计目标是“提取简单性”,驾驶员只需告知车辆“前进”、“后退”或“停车”的意图即可。

在 SDN 出现之前,网络领域从未真正完成从“掌控复杂性”到“提取简单性”的转变。网络专家们满足于精通 OSPF 中某个特定字段的微妙作用,并认为这是运行网络的合理方式。但随着网络管理需求日益复杂,仅靠“掌控复杂性”已不足以应对。

如何促使人们做出这种转变?通常,需要让他们感受到现有方法的极限,即“让他们哭出来”。对网络运营商而言,这个转折点出现在他们需要管理大型多租户数据中心时。

当数据中心需要同时为数以千计的不同客户(租户)提供网络服务,并为每个客户营造独立管理其网络的假象时,管理的复杂度增加了数个数量级。传统的、临时拼凑的控制机制完全无法应对。网络运营商们被复杂性彻底击败,他们迫切需要一种更简单、更系统化的设计。就像做算术题无法解决复杂代数问题一样,他们需要“代数”——也就是 SDN 所提供的抽象方法。

SDN 的核心:为控制平面引入抽象

计算机科学构建大型可用系统的核心方法是基于抽象的模块化。通过定义清晰的抽象接口,我们可以将系统分解为模块,每个模块隐藏其内部复杂性,只通过接口与外界交互。

互联网的数据平面正是这种思想的成功典范,它拥有层次清晰、经受住时间考验的抽象:

  • 应用层构建在 可靠的字节流 抽象之上。
  • 传输层构建在 尽力而为的全局数据包交付 抽象之上。
  • 网络层构建在 L2 网络 抽象之上。
  • 链路层构建在 物理比特传输 之上。

然而,在 SDN 之前,控制平面没有任何类似的抽象。各种控制机制(路由算法、脚本、流量工程)都是从头开始设计,互不重用,导致机制繁多且各自功能并不出色。

SDN 的本质,就是为控制平面寻找合适的抽象。

控制平面的总体任务是:在满足各种需求(连通性、隔离、安全、流量工程等)的前提下,计算网络中所有设备的转发状态。这个任务可以分解为几个子任务或约束:

  1. 与底层硬件和软件兼容:网络设备来自不同厂商,内部数据结构和查找机制各异。
  2. 基于全网状态做决策:许多控制逻辑(如路由)需要了解整个网络的拓扑等信息。
  3. 计算每个物理设备的配置:需要将高层策略转化为每个交换机/路由器的具体转发条目。

传统的“前 SDN”方法是为每个特定任务设计一个“一站式”的分布式协议,该协议需要同时处理上述所有方面。SDN 则希望通过定义抽象来分离这些关注点。

以下是 SDN 提出的三层抽象,对应上述三个子任务:

1. 转发抽象(如 OpenFlow)

这个抽象的目标是:向设备表达“应该做什么”,而不关心其内部如何实现。

  • 核心思想:定义一个通用的转发模型,使控制层能够独立于厂商专有硬件和软件来指定转发行为。
  • 示例:OpenFlow 协议。它本质上是一个描述匹配-动作规则的接口。控制器可以告诉交换机:“如果数据包头符合某个模式,则执行某个动作(如转发、丢弃、修改)”。
  • 代码示例(概念性)
    # 伪代码:一个 OpenFlow 流表条目
    flow_entry = {
        "match": {"ipv4_dst": "10.0.0.1", "tcp_dst_port": 80},
        "action": "FORWARD(out_port=3)",
        "priority": 100
    }
    # 控制器将此条目下发给交换机
    switch.install_flow_entry(flow_entry)
    

2. 网络状态抽象(网络操作系统)

这个抽象的目标是:为控制程序提供一个全局的、一致的网络视图。

  • 核心思想:将分布式状态收集机制(如链路状态协议)抽象出来,集中到少数称为“控制器”的服务器上。这些控制器共同维护一个逻辑上集中的网络图谱。
  • 工作方式:网络设备(通过 OpenFlow 等)向控制器报告其邻居信息。控制器汇总这些信息,构建出全局网络拓扑图。控制程序可以基于这个全局视图(一个图)来编写算法,计算所需的转发状态。

3. 规范抽象(虚拟化层)

这个抽象的目标是:让控制程序只需指定“想要什么行为”,而无需操心“如何在物理网络上实现”。

  • 核心思想:在控制程序和网络操作系统之间引入一个“虚拟化层”。该层向控制程序呈现一个简化的、与任务语义相关的抽象网络视图(例如,一个将所有主机连接起来的大交换机)。控制程序在这个抽象视图上指定策略(如“A 不能与 B 通信”)。虚拟化层则充当编译器,将这些高级策略“编译”成具体的、针对物理网络设备的配置指令。

综合以上三层,完整的 SDN 架构栈如下图所示(概念图):

+-----------------------+
|   控制程序 (Control Apps)  |  (例如:路由、访问控制、负载均衡)
|  (基于抽象网络视图编程)      |
+-----------------------+
            | API
+-----------------------+
|   虚拟化层 (Virtualization) |  (将抽象策略编译为具体配置)
|  (提供抽象网络视图)         |
+-----------------------+
            | API
+-----------------------+
| 网络操作系统 (Network OS)   |  (维护全局网络视图,管理设备)
|  (逻辑集中式控制器)         |
+-----------------------+
            | OpenFlow/南向接口
+-----------------------+
|   物理网络设备 (Switches)   |  (执行数据包转发)
+-----------------------+

通过这种分层抽象,SDN 实现了关注点分离:

  • 控制程序变得简单:只需基于抽象视图声明意图。
  • 复杂性被局部化:所有复杂的分布式协调、状态一致性维护、策略编译等工作,都被封装在可重用的 SDN 平台(虚拟化层和网络操作系统)中。

SDN 的部署与实际影响

一个关键问题是:SDN 要求网络设备支持 OpenFlow 等新接口,如何能在现有网络中部署?答案在于发现:大多数控制平面功能(如访问控制、VLAN)可以在网络边缘实现,而网络核心只需高效地转发数据包。

当时迫切需要 SDN 的“多租户数据中心”恰好提供了完美的部署环境。在这些数据中心中,大量虚拟机运行在宿主机上,虚拟机之间的流量以及虚拟机外出流量,首先会经过宿主机上的虚拟交换机

通过将虚拟交换机(如 Open vSwitch)改造为支持 OpenFlow,就能在完全不更换硬件网络设备的情况下,在数据中心边缘部署 SDN。管理员只需与负责计算资源的团队合作,在服务器上部署软件即可。这正是 SDN 最初得以快速落地和推广的原因。

SDN 的设计带来了重要影响:

  • 促进创新:控制程序变得更简单、更模块化,加快了新网络功能的开发。
  • 赋能研究:清晰的抽象使得形式化验证等工具更容易应用于网络。
  • 改变生态:它挑战了传统网络设备厂商的商业模式,虽然未能完全颠覆,但深刻影响了云服务提供商( hyperscalers )的网络建设方式。

关于 SDN 的一些常见问题:

  • SDN 是否更不可扩展、不安全、不可靠? 否。SDN 的设计可以做到具有良好的扩展性、安全性和弹性。
  • SDN 能扩展到广域网吗? 可以,相关研究正在进行。
  • OpenFlow 是正确的转发抽象吗? 社区普遍认为不是。它过于复杂且不够灵活。像 P4 这样的可编程数据平面语言是更有前景的方向。
  • SDN 可以增量部署吗? 可以,正如在虚拟化数据中心中的部署所示。

总结

本节课中我们一起学习了软件定义网络(SDN)。我们从其诞生的背景和动机开始,分析了传统网络管理因目标增多而陷入复杂混乱的困境。我们探讨了从“掌控复杂性”到“提取简单性”这一思维转变的必要性,以及多租户数据中心的需求如何成为 SDN 发展的催化剂。

SDN 的核心贡献在于为控制平面引入了三层关键抽象:

  1. 转发抽象(如 OpenFlow):分离了转发行为规范与设备具体实现。
  2. 网络状态抽象(网络操作系统):提供了逻辑集中的全局网络视图。
  3. 规范抽象(虚拟化层):允许控制程序在高层声明意图,而由下层负责编译实现。

这些抽象通过基于模块化的设计,将复杂性封装在可重用的平台层,从而简化了网络管理程序的开发,并最终通过虚拟交换机在数据中心边缘成功部署,推动了网络的创新与发展。SDN 并非魔法,而是一次将计算机科学中经典的抽象与模块化思想应用于网络控制平面的成功实践。

25:数据中心

概述

在本节课中,我们将要学习数据中心网络。我们将探讨数据中心网络与传统互联网网络有何不同,重点关注其独特的设计目标、拓扑结构、路由寻址以及拥塞控制方法。我们将看到,由于数据中心具有单一管理控制、规模庞大且对性能要求极高的特点,其网络设计采用了全新的思路。


数据中心网络的独特之处

上一节我们介绍了课程的基本信息,本节中我们来看看数据中心网络与之前讨论的互联网网络有何根本区别。数据中心网络具有几个关键特征,这些特征深刻影响了其设计。

以下是数据中心网络的主要不同点:

  1. 单一地点的庞大规模:现代超大规模数据中心可以在一个物理位置容纳数十万甚至上百万台服务器。这种在一个地点内连接如此多端点的规模是独一无二的。
  2. 单一组织管理:整个数据中心网络由一个组织控制。这意味着可以实现集中策略和控制,并且网络运营商同时控制网络和(大部分)终端主机。例如,可以控制网卡、操作系统,甚至自定义TCP协议栈。此外,还能控制流量源和目的地的放置位置。
  3. 异构性大大降低:与互联网中设备类型、技术、往返时间差异巨大不同,数据中心内部主要是服务器,且通常是当前或上一代型号。链路技术(如以太网)和往返时间也相对统一且有界。
  4. 对性能的极致追求:在互联网设计初期,性能并非首要目标。但在数据中心,最小化延迟、最大化吞吐量至关重要,这直接关系到服务商的竞争力和用户体验。

数据中心网络拓扑:追求高二分带宽

了解了设计背景后,本节我们来看看如何构建数据中心网络的基础设施。核心目标是支持服务器间(东西向)的海量流量,并实现高二分带宽

二分带宽的定义是:将网络中的所有节点划分为两个数量相等的集合,需要切断的最小割集中所有链路的带宽总和。全二分带宽意味着,划分后两个集合中的所有节点可以同时以各自接入链路的全速相互通信。

为了实现高二分带宽,我们考虑几种拓扑方案:

  • 巨型交换机方案:将所有服务器连接到一个巨型交换机。这不可行,因为需要交换机端口数等于服务器数量,且交换容量需达到 petabits 级别,这超出了当前技术能力,且成本极高。
  • 树形网络:构建交换机层级树。如果所有链路容量相同,顶层的链路会成为瓶颈,无法实现高二分带宽。
  • 胖树网络:树形网络的改进版,越靠近树根的链路容量(带宽)越大。逻辑上,如果顶层链路足够“胖”,就能实现全二分带宽。但问题在于,构建这些超高速的“胖”链路本身非常昂贵且非标准化(非“商品化”)。
  • Clos网络(折叠Clos网络):这是目前的主流方案。它使用大量相同端口数、相同链路速率的小型、廉价(商品化)交换机,通过规则的互连方式,构建出一个大规模、高容量的交换网络。这种拓扑天然提供了多条并行路径,能够实现高二分带宽,并且具有容错能力。

Clos网络本质上是一种横向扩展设计:通过增加更多廉价组件来获得更高能力,而非纵向升级单个组件(纵向扩展)。这种思想贯穿于数据中心和云计算的软硬件设计。


路由与寻址:利用并行路径

我们有了提供高带宽的物理拓扑(Clos网络)后,本节中我们来看看如何设计上层的路由和寻址机制,以充分利用这些并行路径。我们探讨两种设计思路。

增量式方案:等成本多路径路由(ECMP)

一种方法是在传统距离矢量或链路状态路由协议上进行扩展。

  • 路由发现:修改协议,使其记录到达目的地的所有等成本下一跳,而不仅仅是选择一个。
  • 流量分发:在转发时,需要将流量分散到这些下一跳上。基于数据包的负载均衡可能导致同一TCP流的包乱序,严重干扰TCP性能。因此,通常采用基于流的负载均衡:使用数据包五元组(源/目的IP、端口、协议)的哈希值,为每个流选择一个固定的下一跳。这样,同一流的所有包走同一路径,不同流则被分散到不同路径上。

这种方法可行,但面对数据中心内数十万个目的地的规模,动态路由协议的可扩展性面临挑战。

革新式方案:基于位置的寻址

由于数据中心拓扑规则且已知,我们可以采用更激进、更可扩展的方案:基于位置的寻址

  • 核心思想:为服务器分配的IP地址直接编码了其在Clos拓扑中的精确位置。例如,地址 10.2.0.1 可能表示“第2个Pod,第0个机架,第1台服务器”。
  • 路由表生成:因为网络设计者知道拓扑结构和地址分配规则,所以可以直接计算出每个交换机上应该有哪些路由表项,而无需运行动态路由协议。每个交换机的路由表项数量仅与其端口数相关,与服务器总数无关,实现了完美的路由聚合。
  • 优势:极其可扩展,利用了所有路径,且负载均衡效果好。这体现了在单一控制下进行“白板”设计的优势。

拥塞控制:应对排队延迟挑战

路由解决了路径问题,本节我们来看看数据传输过程中的另一个关键问题:拥塞控制。在数据中心,排队延迟成为端到端数据包延迟的主导因素。

原因分析

  1. 数据中心的传播延迟极低(约10微秒量级),因为服务器间物理距离很近。
  2. 在10Gbps链路上,一个8000字节数据包的传输延迟约为0.8微秒。
  3. 如果平均队列中有10个包,则在该跳的排队延迟为 10 * 0.8 = 8 微秒。
  4. 经过多跳后,排队延迟累积(如5跳可达40微秒),可能远超传播延迟,成为总延迟的主要部分。

这对需要低延迟的短流(如数据库查询)影响巨大,而长流(如数据备份)则会填满队列。传统的TCP为了追求高吞吐量会主动填满缓冲区,加剧了这个问题。

以下是两种解决思路:

  • 增量式方案:DCTCP
    DCTCP 是对TCP的改进,它更积极地利用显式拥塞通知(ECN)。交换机在队列开始增长(而非已满)时就标记数据包,发送端据此更迅速、更精细地降低发送速率,从而将队列长度维持在一个很低的水平。实验表明,DCTCP能显著降低流完成时间,尤其在高负载下优于传统TCP。

  • 革新式方案:pFabric
    pFabric 提出了更根本性的改变。其核心思想是基于优先级调度。发送端根据剩余数据量(流大小)为数据包标记优先级:短流(小鼠)标记高优先级,长流(大象)标记低优先级。交换机始终优先发送高优先级包。这样,短流几乎不受排队影响,而长流利用剩余带宽。pFabric 的性能非常接近理想情况。


总结

本节课中我们一起学习了数据中心网络的设计。

  • 设计驱动力:我们首先了解了数据中心网络在规模、控制权、异构性和性能目标上与互联网的根本区别。
  • 拓扑结构:为了经济地实现高二分带宽,业界普遍采用基于商品化交换机的 Clos网络 拓扑,这是一种横向扩展的设计典范。
  • 路由寻址:为了充分利用Clos网络提供的多路径,可以采用 ECMP 进行基于流的负载均衡,或者更激进地采用 基于位置的寻址 来简化路由、提升可扩展性。
  • 拥塞控制:由于排队延迟在数据中心成为主要矛盾,传统的TCP不再适用。我们介绍了 DCTCPpFabric 等方案,它们通过不同的机制(ECN或优先级调度)来大幅降低排队延迟,以满足数据中心应用对低延迟和高吞吐量的双重需求。

数据中心网络是集中控制、规模化和性能优化思想的集中体现,展示了在网络条件可控的情况下,如何重新设计网络协议栈以获得极致性能。

posted @ 2026-03-29 09:26  布客飞龙II  阅读(43)  评论(0)    收藏  举报