LOADING

首次加载会比较慢,果咩~

请打开缓存,下次打开就会很快啦

计网读书笔记

2025/4/27 笔记 Web

#计网自顶向下读书笔记

  个人笔记,没有详细记录全部内容,存在一些补充或者个人理解。

#计算机网络和因特网

#网络核心

  网络核心由由各种路由/转发设备组成。通过路由设备将各个不同的网络进行相连,使各个端系统连接。

  通过网络链路和交换机传输数据有两种基本方法:分组交换电路交换

#分组交换

  在各种网络应用中、端系统彼此交换报文 。 报文能够包含协议设计者需要的任何东西。 报文可以执行一种控制功能,也可以包含数据,例如电子邮件数据、 JPEG 图像或 MP3 音频文件。

  为了从源端系统向目的端系统发送一个报文,源将长报文划分为较小的数据块,称之为分组。在源和目的地之间,每个分组都通过通信链路分组交换机传送。分组传输时会以其链路的最大传输速率通过。

交换机主要有两类,路由器链路层交换机

  1.存储转发传输

  多数分组交换机在链路的输入端使用存储转发传输机制。存储转发传输是指在交换机能够开始向输出链路传输该分组的第一个比特之前,必须接收到整个分组。

  2.排队时延和分组丢失

  每台分组交换机与多条链路相连。对于每条相连的链路,该分组交换机具有一个输出缓存 (也称为输出队列) , 用于存储路由器准备发往那条链路的分组。该输出缓存在分组交换中起着重要的作用。

  如果到达的分组需要传输到某条链路,但发现该链路正忙于传输其他分组,该到达分组必须在输出缓存中等待。因此,除了存储转发时延以外,分组还要承受输出缓存的排队时延。这些时延是变化的,变化的程度取决于网络的拥塞程度。因为缓存空间的大小是有限的,一个到达的分组可能发现该缓存已被其他等待传输的分组完全充满了。在此情况下,将出现分组丢失(即丢包),到达的分组或已经排队的分组之一将被丢弃。

  3.转发表和路由选择协议

  路由器从与它相连的一条通信链路得到分组,然后向与它相连的另一 条通信链路转发该分组。 但是路由器怎样决定它应当向哪条链路进行转发呢?

  在因特网中,每个端系统具有一个称为IP地址的地址。当源主机要向目的端系统发送一个分组时,源在该分组的首部包含了目的地的IP地址。当一个分组到达网络中的路由器时,路由器检查该分组的目的地址的一部分,并向一台相邻路由器转发该分组。

  每台路由器具有一个转发表, 用于将目的地址(或目的地址的一部分)映射成为输出链路。当某分组到达一台路由器时,路由器检查该地址,并用这个目的地址搜索其转发表,以发现适当的出链路。路由器则将分组导向该出链路。

因特网具有一些特殊的路由选择协议用于自动地设置这些转发表。 具体内容在之后的章节讨论。

#电路交换

  在电路交换网络中,在端系统间通信会话期间,预留了端系统间沿路径通信所需要的资源(缓存,链路传输速率)。在分组交换网络中.这些资源则不是预留的。会话的报文按需使用这些资源,其后果可能是不得不等待(即排队)接入通信线路。

  传统的电话网络是电路交换网络的例子。考虑当一个人通过电话网向另一个人发送信息(语音或传真)时所发生的情况。在发送方能够发送信息之前,该网络必须在发送方和接收方之间建立一条连接。这是一个名副其实的连接,因为此时沿着发送方和接收方之间路径上的交换机都将为该连接维护连接状态。用电话的术语来说,该连接被称为一条电路。当网络创建这种电路时,它也在连接期间在该网络链路上预留了恒定的传输速率(表示为每条链路传输容量的一部分)既然已经为该发送方–接收方连接预留了带宽,则发送方能够以确保的恒定速率向接收方传送数据。

  以下是一个例子:

  考虑一台主机要经过分组交换网络(如因特网)向另一台主机发送分组所发生的情况。与使用电路交换相同,该分组经过一系列通信链路传输。 但与电路交换不同的是,该分组被发送进网络,而不预留任何链路资源之类的东西。 如果因为此时其他分组也需要经该链路进行传输而使链路之一出现拥塞,则该分组将不得不在传输链路发送侧的缓存中等待而产生时延。 因特网尽最大努力以实时方式交付分组,但它不做任何保证。

1.电路交换网络中的复用

  复用是为了将链路划分为多个电路以满足多个用户数据传输的需求。如上图1-13中,每条链路被划分为4条电路,就是链路的复用。

  链路中的电路是通过频分复用 (FDM)时分复用 (TDM)来实现的。

  对于 FDM, 链路的频谱由跨越链路创建的所有连接共享。 特别是,在连接期间链路为每条连接专用一个频段。 在电话网络中,这个频段的宽度通常为4kHz (即每秒4000 周期)。毫无疑问,该频段的宽度称为带宽。 调频无线电台也使用 FDM来共享88MHz ~ 108MHz 的频谱、其中每个电台被分配 一个特定的频段。

  对于一条TDM链路,时间被划分为固定期间的帧,并且每个帧又被划分为固定数量的时隙。 当网络跨越一条链路创建一条连接(电路)时,网络在每个帧中为该连接指定一个时隙, 这些时隙专门由该连接单独使用,一个时隙(在每个帧内)可用于传输该连接的数据。

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

  • 电路连接不适合计算机之间的通信

  • 连接建立的时间成本较高。

  • 计算机之间的通信具有突发性,如果使用电路交换,则浪费的资源较多(即连接建立后就是两个端系统的专用连接,即使空闲,也不能被其他的呼叫(请求)利用。

#分组交换网中的时延,丢包和吞吐量

#分组交换网中的时延概述

  分组从一台主机(源)出发,通过一系列路由器传输,在另一台主机(目的地)中结束它的历程。 当分组从一个节点(主机或路由器)沿着这条路径到后继节点(主机或路由器),该分组在沿途的每个节点经受了几种不同类型的时延。 这些时延最为重要的是**节点处理时延 **排队时延发送时延(又称传输时延)传播时延, 这些时延总体累加起来是节点总时延

计算:
发送时延 = 数据帧长度(b) / 信道带宽(b/s)
传播时延 = 信道长度(m) / 电磁波在信道上的传播速率(m/s)

  1.时延对的类型

  • 处理时延 (nodal processing delay)

    检查分组首部和决定将该分组导向何处所需要的时间是处理时延的一部分。处理时延也能够包括其他因素,如检查比特级别的差错所需要的时间,该差错出现在从上游节点向路由器A传输这些分组比特的过程中。 高速路由器的处理时延通常是微秒或更低的数量级。在这种节点处理之后,路由器将该分组引向通往路由器B链路之前的队列。

  • 排队时延(queuing delay)

    在队列中,当分组在链路上等待传输时,它经受排队时延。实际的排队时延可以是毫秒到微秒量级。

  • 发送时延(传输时延)( transmission delay)

    假定分组以先到先服务方式传输,这在分组交换网中是常见的方式。仅当所有已经到达的分组被传输后,才能传输刚到达的分组。 用L bit表示该分组的长度,用 R bps表示从路由器A到路由器B的链路传输速率。例如,对于一条10Mbps的以太网链路,速率R=10Mbps; 对于 100Mhps的以太网链路,速率R=100Mbps。 传输时延是L/R。这是将所有分组的比特推向链路(即传输,或者说发射)所需要的时间。 实际的传输时延通常在毫秒到微秒量级。

  • 传播时延(propagation delay)

    数据从该链路的起点到目的地传播所需要的时间是传播时延。该传播速率取决于该链路的物理媒体,等于或略小于光速。

#排队时延和丢包

1.排队时延

  令a表示分组到达队列的平均速率 (a 的单位是分组/秒,即pkt/s) ,R bps是传输速率,为了简单起见,也假定所有分组都是由L bit组成的。 则比特到达队列的平均速率 是La bps。 最后,假定该队列非常大,因此它基本能容纳无限数量的比特。 比率La/R被称为流量强度。它在估计排队时延的范围方面经常起着重要的作用。

  若流量强度>1, 则比特到达队列的平均速率超过从该队列传输出去的速率。在这种情况下,该队列趋向于无限增加,并且排队时延将趋向无穷大。因此,流量工程中的一条铁律是:设计系统时流量强度不能大于1。

  随着流量强度接近1,平均排队时延迅速增加。该强度的少量增加将导致时延大比例增加。

2.丢包

   一条链路前的队列只有有限的容量。因为该排队容量是有限的,随着流量强度接近1, 到达的分组将发现一个满的队列。由于没有地方存储这个分组,路由器将丢弃该分组。

#计算机网络中的吞吐量

  除了时延和丢包,计算机网络中另一个至关重要的性能测度是端到端吞吐量

  为了定义吞吐量,考虑从主机A到主机B跨越计算机网络传送一个大文件,在任何瞬间的瞬时吞吐量是主机B接收到该文件的速率(bps)。如果该文件由 F 比特组成, 主机B接收到所有F比特用时T秒,则文件传送的平均吞吐量是F/T bps。

  对于一个链路传输,其吞吐量是瓶颈链路的传输速率,即连接两端的所有链路中,传输速率最小的链路的传输速率。

#协议层次及其服务模型

#分层的体系结构

1.协议分层

  为了给网络协议的设计提供一个结构,网络设计者以分层的方式组织协议以及实现这些协议的网络硬件和软件,每个协议属于这些层次之一。某层向它的上一层提供的服务, 即所谓一层的服务模型

  每层通过在该层中执行某些动作或使用直接下层的服务来提供服务。 例如,由第n层提供的服务可能包括报文从网络的 一边到另一边的可靠交付。 这可能是通过使用第n-1层的边缘到边缘的不可靠报文传送服务,加上第n层的检测和重传丢失报文的功能来实现的。

  一个协议层能够用软件、硬件或两者的结合来实现。诸如 HTTP和SMTP这样的应用 层协议几乎总是在端系统中用软件实现,运输层协议也是如此。 物理层和数据链路层负责处理跨越特定链路的通信,它们通常在与给定链路相关联的网络接口卡(例如以太网 或WiFi 接口卡)中实现。 网络层经常是硬件和软件实现的混合体。

  还要注意的是,一个第 n 层协议也分布在构成该网络的端系统、分组交换机和其他组件中。 这就是说,第n层协议的不同部分常常位于这些网络组件的各部分中。

  协议分层具有概念化和结构化的优点。如我们看到的那样,分层提供了一种结构化方式来讨论系统组件,模块化使更新系统组件更为容易。

  各层的所有协议被称为协议栈

1.1因特网协议栈

  因特网的协议栈由5个层次组成:物理层链路层网络层运输层应用层

本书的“自顶向下”既是“以因特网协议栈的层次从上往下”的意思

  • 5.应用层

  应用层是网络应用程序及它们的应用层协议存留的地方。 因特网的应用层包括许多协议,例如HTTP (它提供了Web文档的请求和传送)、SMTP (它提供了电子邮件报文的传输)和FTP (它提供两个端系统之间的文件传送)。我们将看到,某些网络功能,如将像 www. ietf. org 这样对人友好的端系统名字转换为32比特的网络地址(域名解析),也是借助于特定的应用层协议即域名系统(DNS)完成的。

  应用层协议分布在多个端系统上,而一个端系统中的应用程序使用协议与另一个端系统中的应用程序交换信息分组。我们把这种位于应用层的信息分组就是常常提及的报文

  • 4.运输层

  因特网的运输层在应用程序端点之间传送应用层报文。在因特网中,有两种运输协议,即TCP和UDP, 利用其中的任一个都能运输应用层报文。

  TCP 向它的应用程序提供了面向连接的服务。 这种服务包括了应用层报文向目的地的确保传递和流量控制(即发送方/接收方速率匹配)。TCP也将长报文划分为短报文,并提供拥塞控制机制,因此当网络拥塞时,源抑制其传输速率。

  UDP协议向它的应用程序提供无连接服务。这是一种不提供不必要服务的服务,没有可靠性,没有流量控制,也没有拥塞控制。

粗略的说,TCP协议更可靠,相对资源要求也更高(数据大小,时延等)。UDP则反之。

  在本书中,我们把运输层的分组称为报文段

  • 3.网络层

  网络层负责将网络层分组从一台主机移动到另一 台主机,网络层分组称为数据报。在一台源主机中的运输层协议 (TCP或UDP) 向网络层递交报文段和目的地址。

  网络层包括著名的网际协议IP, 该协议定义了在数据报中的各个字段以及端系统和路由器如何作用于这些字段。一个端系统的IP仅有一个,所有具有网络层的因特网组件必须运行IP。 因特网的网络层也包括决定路由的路由选择协议,它根据该路由将数据报从源传输到目的地。

  尽管网络层包括了其他网际协议和一些路由选择协议,但通常把它简单地称为IP层,这反映了 IP是将因特网连接在一起的黏合剂这样的事实。

  • 2.链路层

  为了将分组从一个 节点(主机或路由器)移动到路径上的下一个节点,网络层必须依靠该链路层的服务。特别是在每个节点,网络层将数据报下传给链路层,链路层沿着路径将数据报传递给下一个节点。在该下一个节点,链路层将数据报上传给网络层。

  由链路层提供的服务取决于应用于该链路的特定链路层协议。例如,某些协议基于链路提供可靠传递,从传输节点跨越一条链路到接收节点。

  值得注意的是,这种可靠的传递 服务不同于TCP的可靠传递服务,TCP提供从一个端系统到另一个端系统的可靠交付。 链路层的例子包括以太网、 WiFi和电缆接入网的DOCSIS协议。因为数据报从源到目的地传送通常需要经过几条链路,一个数据报可能被沿途不同链路上的不同链路层协议处理。 例如,一个数据报可能被一段链路上的以太网和下一段链路上的PPP所处理。网络层将受到 来自每个不同的链路层协议的不同服务。 在本书中,我们把链路层分组称为

  • 1.物理层

  虽然链路层的任务是将整个帧从一个网络元素移动到邻近的网络元素,而物理层的任务是将该帧中的一个个比特从一个节点移动到下一个节点。

1.2 OSI模型

  OSI模型即是开放系统互连模型,其参考模型有七层:应用层、表示层、会话层、运输层、网 络层、数据链路层和物理层。

  这些层次中, 5层的功能大致与它们名字类似的因特网对应层的功能相同。所以,我们来考虑OSI参考模型中附加的两个层,即表示层和会话层。表示层的作用是使通信的应用程序能够解释交换数据的含义。 这些服务包括数据压缩和数据 加密(它们是自解释的)以及数据描述(这使得应用程序不必担心在各台计算机中表示/存储的内部格式不同的问题)。会话层提供了数据交换的定界和同步功能,包括了建立检查点和恢复方案的方法。

#封装

  上图显示了这样一条物理路径:数据从发送端系统的协议栈向下,沿着中间的链 路层交换机和路由器的协议栈上上下下,然后向上到达接收端系统的协议栈。

  路由器和链路层交换机都是分组交换机,与端系统类似,路由器和链路层交换机以多层次的方式组织它们的网络硬件和软件。而路由器和链路层交换机并不实现协议栈中的所有层次。如图所示,链路层交换机实现了第一层和第二层;路由器实现了第一层到第三层。这意味着因特网路由器能够实现IP协议,而链路层交换机则不能。尽管链路层交换机不能识别IP地址,但它们能够识别第二层地址,如以太网地址。值得注意的是,主机实现了所有5个层次,这与因特网体系结构将它的复杂性放在网络边缘的观点是一致的。

  上图也说明了一个重要概念:封装。在发送主机端,一个应用层报文(图中的M) 被传送给运输层。在最简单的情况下,运输层收取到报文并附上附加信息(所谓运输层首部信息,图中的Ht)该首部将被接收端的运输层使用。应用层报文和运输层首部信息一道构成了运输层报文段。 运输层报文段因此封装了应用层报文。附加的信息也许包括了下列信息:允许接收端运输层向上向适当的应用程序交付报文的信息;差错检测位信息,该信息让接收方能够判断报文中的比特是否在途中已被改变。运输层则向网络层传递该报文段,网络层增加了如源和目的端系统地址等网络层首部信息(图中的Hn)生成了网络层数据报。该数据报接下来被传递给链路层,链路层增加它自己的链路层首部信息并生成链路层帧。 所以我们看到,在每一 层,一个分组具有两种类型的字段: 首部字段(头)有效载荷字段。 有效载荷通常是来自上一层的分组。

  封装的过程能够比前面描述的更为复杂。 例如,一个大报文可能被划分为多个运输层的报文段(这些报文段每个又可能被划分为多个网络层数据报)。在接收端,则必须从其连续的数据报中重构这样一个报文段。

#面对攻击的网络

  • 病毒

    病毒是一种需要某种形式的用户交互来感染用户设备的恶意软件。

  • 蠕虫

    蠕虫是一种无须任何明显用户交互就能进入设备的恶意软件。

  • Dos,拒绝服务攻击

    DoS攻击使得网络、 主机或其他基础设施部分不能由合法用户使用。大多数因特网DoS攻击属于下列三种类型之一:

    • 弱点攻击

      即针对对方的漏洞进行攻击

    • 带宽洪泛

      攻击者向目标主机发送大量的分组,分组数量之多使得目标的接入链路变得拥塞,使得合法的分组无法到达服务器。

    • 连接洪泛

      攻击者在目标主机中创建大量的半开或全开TCP连接,该主机因这些伪造的连接而陷入困境,并停止接受合法的连接。

    下图所示的即是分布式DoS(DDoS),攻 击者控制多个源并让每个源向目标猛烈发送流量。

  • 分组嗅探

      在无线传输设备的附近放置一台被动的接收机,该接收机就能得到传输的每个分组的副本。这些分组包含了各种敏感信息,包括口令、社会保险号、商业秘密和个人信息等。记录每个流经的分组副本的被动接收机被称为分组嗅探器(wireshark就是其中之一)。

      由于分组嗅探是被动的,它并不会在信道中注入其他分组,所以基本无法检测其存在。所以我们才要加密数据。

  • IP欺诈

      具有虚假源地址的分组注入因特网的能力被称为IP哄骗, 而它只是一个用户能够冒充另一个用户的许多方式中的一种。为了解决这个问题,我们需要采用端点鉴别,即一种使我们能够确信一个报文源自我 们认为它应当来自的地方的机制(比如http请求中的remote_addr)。

#应用层

#网络应用原理

  研发网络应用程序的核心是写出能够运行在不同的端系统通过网络彼此通信的程序

例如,在Web应用程序中,有两个互相通信的不同的程序: 一个是运行在用户主机(桌面机、膝上机、平板电脑、智能电话等) 上的浏览器程序,另一个是运行在Web服务器主机。

  网络核心设备并不在应用层上起作用,而仅在较低层起作用,特别是在网络层及以下层次起作用。将应部用软件限制在端系统的方法,促进了大量的网络应用程序的迅速研发和部署(因为不用考虑下层了)。

#网络应用体系

  当进行软件编码之前,应当对应用程序有一个宽泛的体系结构计划。应用程序的体系结构明显不同于网络的体系结构(例如在第1章中所讨论的5层因特网体系结构)。

从应用程序研发者的角度看,网络体系结构是固定的,并为应用程序提供了特定的服务集合

  另外,应用体系结构由应用程序研发者设计,规定了如何在各种端系统上组织该应用程序。在选择应用程序体系结构时,应用程序研发者很可能利用现代网络应用程序中所使用的两种主流体系结构之一:客户-服务器体系结构对等(P2P)体系结构

  • 客户-服务器体系结构

  在客户-服务器体系结构中,有一个总是打开的主机,称为服务器,它服务于来自许多其他称为客户的主机。值得注意的是,客户-服务器体系结构下,客户相互之间不直接通信

  客户-服务器体系结构的另一个特征是该服务器具有固定的、周知的地址,该地址称为IP地址。

  在一个客户-服务器应用中,常常会出现一台单独的服务器主机跟不上它所有客户请求的情况。例如,一个流行的社交网络站点如果仅有一台服务器来处理所有请求,将很快变得不堪重负。为此,托管大量主机的数据中心常被用于创建强大的虚拟服务器,用以满足客户需求。

  

  • P2P体系结构

  在P2P体系结构中,对位于数据中心的专用服务器有最小的(或者没有)依赖。相反,应用程序在间断连接的主机对之间使用直接通信,这些主机对被称为对等方。这些对等方并不为服务提供商所有,而是为用户的台式机和笔记本电脑所控制。因为这种对等方通信不必通过专门的服务器,该体系结构被称为对等方到对等方的。流行的P2P应用的例子是文件共享应用BitTorrent。

  P2P体系结构的特性之一是其自扩展性。例如,在一个P2P文件共享应用中,尽管每个对等方都由于请求文件产生工作负载,但每个对等方通过向其他对等方分发文件也为系统整体增加服务能力。P2P体系结构也是有成本效率的,因为它通常不需要庞大的服务器基础设施和服务器带宽。然而,未来P2P应用由于高度非集中式结构,面临安全性、性能和可靠性等挑战。

#进程通信

  在构建网络应用程序前,还需要对程序如何运行在多个端系统上以及程序之间如何相互通信有基本了解。用操作系统的术语来说,进行通信的实际上是进程而不是程序。

  • 一个进程可以被认为是运行在端系统中的一个程序。
  • 当多个进程运行在相同的端系统上时,它们使用进程间通信机制相互通信。
  • 进程间通信的规则由端系统上的操作系统确定。

  而在本书中,我们并不特别关注同一台主机上的进程间通信,而关注运行在不同端系统(可能具有不同的操作系统)上的进程间通信。

  在两个不同端系统上的进程,通过跨越计算机网络交换报文而相互通信。发送进程生成并向网络中发送报文;接收进程接收这些报文并可能通过回送报文进行响应。

  1.客户和服务器进程

  网络应用程序由成对的进程组成,这些进程通过网络相互发送报文。

  例如,在Web应用程序,一个客户浏览器进程与一个Web服务器进程交换报文。在一个P2P文件共享系统, 文件从一个对等方中的进程传输到另一个对等方中的进程。对每对通信进程,我们通常将这两个进程之一为客户,而另一个进程为服务器。对于Web而言,浏览器是一个客户进程,Web服务器是一个服务器进程。对于P2P文件共享,下载文件的对等方为客户,上载文件的对等方为服务器。

  客户和服务器进程的定义如下:在一对进程之间的通信会话场景,发起通信(即在该会话开始时发起与其他进程的联系)的进程为客户进程,在会话开始时等待联系的进程为服务器进程。

  2. 进程与计算机网络之间的接口

  如上所述,多数应用程序由通信进程对组成,每对中的两个进程相互发送报文。从一个进程向另一个进程发送的报文必须通过下面的网络。进程通过一个称为套接字的软件接口向网络发送报文和从网络接收报文。

  下图显示了两个经过因特网通信的进程之间的套接字通信(假定由该进程使用的运输层协议是TCP协议)。如该图所示,套接字是同一台主机内应用层与运输层之间的接口。由于该套接字是建立网络应用程序的可编程接口,因此套接字也称为应用程序和网络之间的应用编程接口(API)。应用程序开发者可以控制套接字在应用层端的一切,但是对该套接字的运输层端几乎没有控制权。

  应用程序开发者对于运输层的控制仅限于:

  • 选择运输层协议
  • 可能能设定几个运输层参数,如最大缓存和最大报文段长度

  3. 进程寻址

  为了向特定目的地发送邮政邮件,目的地需要有一个地址。类似地,在一台主机上运行的进程为了向另一台主机上运行的进程发送分组,接收进程需要有一个地址。为了标识该接收进程,需要定义两种信息:

  • 主机的地址(因特网中就是IP地址)
  • 目标主机中指定接收进程的标识符

  除了知道报文发送目的地的主机地址,发送进程还必须指定运行在接收主机上的接收进程。因为一般而言一台主机能够运行许多网络应用,所以这些信息是必要的。目的地端口号用于这个目的。常用的应用有固定的端口号,如Web服务器用端口号80来标识。邮件服务器进程(使用SMTP协议)用端口号25来标识。

用于所有因特网标准协议的周知端口号的列表能够在www.iana.org处找到

#可供应用程序使用的运输服务

  包括因特网在内的很多网络提供了不止一种运输层协议。当开发一个应用时,必须选择一种可用的运输层协议。如何做出这种选择呢?最可能的方式是,通过研究这些可用的运输层协议所提供的服务,选择一个最能为你的应用需求提供恰当服务的协议。

  我们大体能够从四个方面对应用程序服务要求进行分类:

  • 可靠数据传输
  • 吞吐量
  • 定时
  • 安全性

  1. 可靠数据传输

  分组在计算机网络中可能丢失。例如,分组能够使路由器中的缓存溢出,或者当分组中的某些比特损坏后可能被丢弃。

  因此,为了支持这些应用,必须做一些工作以确保由应用程序的一端发送的数据正确并完全地交付给该应用程序的另一端。如果一个协议提供了这样的确保数据交付服务,就认为提供了可靠数据传输

  运输层协议能够潜在地向应用程序提供的一个重要服务就是进程到进程的可靠数据传输。当一个运输协议提供这种服务,发送进程只要将其数据传递进套接字,就可以完全相信该数据将能无差错地到达接收进程。

  当一个运输层协议不提供可靠数据传输时,由发送进程发送的某些数据可能到达不了接收进程。这可能能被容忍丢失的应用所接受,最值得注意的是多媒体应用,如交谈式音频/视频,它能够承受一定量的数据丢失。在多媒体应用中,丢失的数据会引起播放的音频/视频出现小干扰,而不是致命的损伤。

  2. 吞吐量

  具有吞吐量要求的应用程序被称为带宽敏感的应用。许多当前的多媒体应用是带宽敏感的,尽管某些多媒体应用可能采用自适应编码技术对数字语音或视频以与当前可用带宽相匹配的速率进行编码。

  带宽敏感的应用具有特定的吞吐量要求,而弹性应用能够根据当时可用的带宽或多或少地利用可供使用的乔吐量。电子邮件、文件传输以及Web传送都属于弹性应用。

  3. 定时

  运输层协议也能提供定时保证。如同具有吞吐量保证那样,定时保证能够以多种形式实现。一个保证的例子如:发送方注入套接字中的每个比特到达接收方的套接字不迟于100ms。

  交互式实时应用程序对于定时有较高要求,对于非实时的应,较低的时延总比较高的时延好,但对端到端的时延没有严格的约束。

  4. 安全性

  运输协议能够为应用程序提供一种或多种安全性服务。例如,在发送主机中,运输协议能够加密由发送进程传输的所有数据;在接收主机中,运输协议能够在将数据交付给接收进程之前解密这些数据。这种服务将在发送和接收进程之间提供机密性,以防数据以某种方式在这两个进程之间被观察。运输协议还能提供除了机密性以外的其他安全性服务,包括数据完整性和端点鉴别等。

#因特网提供的运输服务

  我们已经考虑了计算机网络能够提供的通用运输服务。现在我们要更为具体地考察由因特网提供的运输服务类型。因特网(更一般的是TCP/IP网)为应用程序提供两个运输层协议,即TCP和UDP。

  1. TCP服务

  TCP服务模型包括面向连接服务可靠数据传输服务。当某个应用程序调用TCP作为其运输协议时,该应用程序就能获得来自TCP的这两种服务。

  • 面向连接的服务:在应用层数据报文开始流动之前,TCP让客户和服务器相互交换运输层控制信息。这个所谓的握手过程提醒客户和服务器,让它们为大量分组的到来做好准备。在握手阶段后,一个TCP连接就在两个进程的套接字之间建立了。这条连接是全双工的,即连接双方的进程可以在此连接上同时进行报文收发。当应用程序结束报文发送时,必须拆除该连接。
  • 可靠的数据传输服务:通信进程能够依靠TCP,无差错、按适当顺序交付所有发送的数据。当应用程序的一端将字节流传进套接字时,它能够依靠TCP将相同流交付给接收方的套接,而没有字节的丢失和冗余。

  TCP还具有拥塞控制机制,这种服务不一定能直接为通信进程带来好处,但对因特网整体有利。当发送方和接收方之间的网络出现拥塞时,TCP的拥塞控制机制会抑制发送进程(客户或服务器)。

EX. TCP安全

无论TCP还是UDP都没有提供任何加密机制,这就是说发送进程传进其套接字的数据,与经网络传送到目的进程的数据相同。因此,举例来说,如果某发送进程以明文方式发送了一个口令进入它的套接字,该明文口令将经过发送方与接收方之间的所有链路传送,这就可能在任何中间链路被嗅探和发现。

因为隐私和其他安全问题对许多应用而言已经成为至关重要的问题,所以因特网界已经研制了TCP的“强化模块”,称为运输层安全(TLS)。用TLS加强后的TCP不仅能够做传统的TCP所能做的一切,而且提供了关键的进程到进程的安全性服务,包括加密、数据完整性和端点鉴别。

注意,TLS并不是因特网运输层传输协议,它只是一种对TCP的加强,这种加强是在应用层实现的。

TLS有它自己的套接字API,这类似于传统的TCP套接字API。当一个应用使用TLS时,发送进程向TLS套接字传递明文数据;发送主机中的TLS则加密该数据,并将加密的数据传递给TCP套接字。加密的数据经因特网传送到接收进程中的TCP套接字。该接收套接字将加密数据传递给TLS,由其进行解密。最后,TLS通过它的TLS套接字将明文数据传递给接收进程。

  2. UDP服务

  UDP是一种不提供不必要服务的轻量级运输协议,它仅提供最低限度的服务。

  UDP是无连接的,因此在两个进程通信前没有握手过程。UDP提供一种不可靠数据传输服务,也就是说,当进程将一个报文发送进UDP套接字时,UDP并不保证该报文将到达接收进程。不仅如此,到达接收进程的报文也可能是乱序到达的。

  UDP不包括拥塞控制机制,所以UDP的发送端可以用它选定的任何速率向其下层(网络层)注入数据。(然而,值得注意的是实际端到端吞吐量可能小于该速率,这可能是由中间链路的带宽受限或拥塞而造成的。)

  3. 因特网运输协议所不提供的服务

  TCP提供了可靠的端到端数据传输。并且我们也知道TCP在应用层可以很容易地用TLS来加强以提供安全服务。所以运输协议服务中的可靠数据传输和安全性都可以得到满足

  今天的因特网通常能够为时间敏感应用提供满意的服务,但它不能提供任何定时或知吐量保证。

  下图给出了一些流行的因特网应用所使用的运输协议。可以看到,电子邮件、远程终端访问、Web、文件传输都使用了TCP。这些应用选择TCP的最主要原因是TCP提供了可靠数据传输服务,确保所有数据最终到达目的。因为因特网电话应用(如Skype)通常能够容忍某些丢失但要求达到一定的最小速率才能有效工,所以因特网电话应用的开发者通常愿意将该应用运行在UDP上,从而设法避开TCP的拥塞控制机制和分组开销。但因为许多防火墙被配置成阻挡(大多数类型的)UDP流量,所以因特网电话应用通常被设计成如果UDP通信失败就使用TCP作为备选项。

#应用层协议

  我们刚刚学习了通过把报文发送进套接字实现网络进程间的互相通信。那么如何构造这些报文?在这些报文中,各个字段的含义是什么?进程何时发送这些报文?这些问题将我们带进应用层协议的范围。

  应用层协议(application-layer protocol)定义了运行在不同端系统上的应用程序进程如何相互传递报文。特别是应用层协议定义了以下内容:

  • 交换的报文类型,例如请求报文和响应报文。
  • 各种报文类型的语法,如报文中的各个字段及这些字段是如何描述的。
  • 字段的语义,即这些字段中信息的含义。
  • 确定一个进程何时以及如何发送报文,对报文进行响应的规则。

  有些应用层协议是由RFC文档定义的,因此它们位于公共域中。例如,Web的应用层协议HTTP(超文本传输协议[RFC 7230])就作为一个RFC可供使用。如果浏览器开发者遵从HTTP RFC规则,所开发出的浏览器就能访问任何遵从该文档标准的Web服务器并获取相应Web页面。还有很多别的应用层协议是专用的,有意不为公共域所用。例如,Skype使用了专用的应用层协议。

  区分网络应用和应用层协议是很重要的。应用层协议只是网络应用的一部分(尽管它是应用非常重要的一部分)。例如,Web是一种客户-服务器应用,它允许客户按照需求从Web服务器获得文档。该Web应用有很多组成部分,包括文档格式的标准(即HIML)、Web浏览器(如Chrome和Microsoft Internet Explorer)、Web服务器(如Apache、Microsoft服务器程序),以及一个应用层协议。Web的应用层协议是HITP,它定义了在浏览器和Web服务器之间传输的报文格式和序列。因此,HTTP只是Web应用的一个部分(尽管是重要部分)。

#Web和HTTP

#HTTP概述

  Web的应用层协议是超文本传输协议(HyperText Transfer Protocol,HTTP),它是Web的核心。HTTP由两个程序实现:一个客户程序和一个服务器程序。客户程序和服务器程序运行在不同的端系统中通过交换HTTP报文进行会话。HTTP定义了这些报文的结构以及客户和服务行报文交换的方式。在详细解释HITP之前,先了解一些Web术语。

  Web页面(Web page)(也叫文档)是由对象组成的。一个对象(object)只是一个文件,诸如一个HIML文件、一个JPEG图形、一个JavaScript文件、一个CCS样式表文件或一个视频片段,它们可通过一个URL寻址。多数Web页面含有一个HTML基本文件(base HTML file)以及几个引用对象。例如,如果一个Web页面包含HTML文本和5个JPEGC图形,那么这个Web页面有6个对象:一个HTML基本文件和5个图形。HTML基本文件通过对象的URL引用页面中的其他对象。每个URL由两部分组:存放对象的服务器主机和名和对象的路径。例如,一个URL为http:www.someSchool.edu/ysomeDepartment/picture.gif,其中的www.someSchool.edu就是主机名,/someDepartment/picture.gif就是路径名。因为Web浏览器(Web browser)实现了HTTP的客户端,所以在Web环境中我们经常交换使用浏览器和客户这两个术语。Web服务器(Webserver)实现了HTTP的服务器端,它用于存储Web对象,每个对象由URL寻址。流行的Web服务器有Apache和Nginx等。

  HTTP定义了Web客户向Web服务器请求Web页面的方式,以及服务器向客户传送Web页面的方式。我们稍后详细讨论客户和服务器的交互过程,而其基本思想在下图中进行了图示。当用户请求一个Web页面(如点击一个超链接)时浏览器向服务器发出对该页面中所包含对象的HTTP请求报文,服务器接收到请求并用包含这些对象的HTTP响应报文进行啊应。

  HTTP使用TCP作为它的支撑运输协议(而不是在UDP上运行)。HTTP客户首先发起一个与服务器的TCP连接。一旦连接建立,该浏览器和服务器进程就可以通过套接字接口访问TCP。

  客户向它的套接字接口发送HTTP请求报文并从它的套接字接口接收HTTP响应报文。类似,服务器从它的套接字接口接收HTTP请求报文并向它的套接字接口发送HTTP响应报文。一旦客户向它的套接字接口发送了一个请求报文,该报文就脱离了客户控制并进入TCP的控制。

  TCP为HTTP提供可靠数据传输服务。这意味着,一个客户进程发出的每个HTTP请求报文最终能完整地到达服务器;类似,服务器进程发出的每个HTTP响应报文最终能完整地到达客户。这里我们看到了分层体系结构最大的优点,即HTTP不用担心数据丢失,也不关注TCP从网络的数据委失和乱序故障中恢复的细节。那是TCP以及协议栈较低层协议的工作。

  需要注意:服务器向客户发送被请求的文件,而不存储任何关于该客户的状态信息。假如某个特定的客户在短短的几秒内两次请求同一个对象,服务器并不会因为刚刚为该客户提供了该对象就不再做出反应,而是重新发送该对象。因为HTTP服务器并不保存关于客户的任何信息,所以我们说HTTP是一个无状态协议(stateless protocol)。我们同时也注意到Web使用了客户服务器应用程序体系结构。Web服务器总是打开的,具有一个固定的IP地址,且它服务于可能来自数以百万计的不同浏览器的请求。

HTTP的初始版本称为HTTP/1.0,其可追溯到20世纪90年代早期[RFC1945]。到2020年为止,绝大部分的HTTP事务都采用HTTPZ1.1[RFC7230]。然而,越来越多的浏览器和Web服务器也支持新版的HTTP,称为HPTP/2.0[RFC7540]。在本节结束时,将给出HTTP/2.0的简介。

#非持续连接和持续链接

  在许多因特网应用程序中,客户和服务器在一个相当长的时间范围内通信,在此期间,客户发出一系列请求,并且服务器对每个请求进行响应。依据应用程序以及该应用程序的使用方式,这一系列请求可以以规则的间隔周期性地或者间断性地一个接一个发出。

  • 当每个请求/响应对是经一个单独的TCP连接发送,则该应用程序被称为使用非持续连接(non-persistentconnection)

  • 当所有的请求及其响应经相同的TCP连接发送,则该应用程序被称为使用持续连接(persistentconnection)

  为了深入地理解该设计问题,我们研究在特定的应用程序即HTTP的情况下持续连接的优点和缺点,HTTP既能够使用非持续连接,也能够使用持续连接。尽管HTTP默认使用持续连接,但HITP客户和服务器也能配置成使用非持续连接。

1. 采用非持续连接的HTTP

  我们看看在非持续连接情况下从服务器向客户传送一个Web页面的步骤。假设该页面含有1个HTML基本文件和10个JPEG图形,并且这11个对象位于同一台服务器上。进一步假设该HTML文件的URL为http://www.example.com/index.html,以下是请求发送的情况:

  1. HTTP客户进程在端口号80发起一个到服务器www.example.com的TCP连接,该端口号是HTTP的默认端口。在客户和服务器上分别有一个套接字与该连接相关联。
  2. HTTP客户经它的套接字向该服务器发送一个HTTP请求报文。请求报文中包含了路径名/index.html。
  3. HTTP服务器进程经它的套接字接收该请求报文,从其存储器(通常是RAM)中检索出对象index.html(注意工作目录问题),在一个HTTP响应报文中封装对象,并通过其套接字向客户发送响应报文。
  4. HTTP服务器进程通知TCP断开该TCP连接。(但直到TCP确认客户得到完整的响应报文后,它才会实际中断连接。)
  5. HTTP客户接收响应报文,TCP连接关闭。该报文指出封装的对象是一个HTML文件,客户从响应报文中提取出该文件,检查该HTML文件,得到对10个JPEG图形的引用。
  6. 对每个引用的JPEG图形对象重复前4个步骤。

  当浏览器收到Web页面后,向用户显示该页面。两个不同的浏览器也许会以不同的方式解释(即向用户显示)该页面。HTTP与客户如何解释一个Web页面毫无关系。HTTP规范([RFC1945][RFC7540])仅定义了在HTTP客户程序与HTTP服务器程序之间的通信协议。

  上面的步骤举例说明了非持续连接的使用,其中每个TCP连接在服务器发送一个对象后关闭,即该连接并不为其他的对象而持续下来。HTTP71.0应用了非持续TCP连接。值得注意的是每个TCP连接只传输一个请求报文和一个响应报文。因此在本例,当用户请求该Web页面时,要产生11个TCP连接。

  在上面描述的步骤,我们有意没有明确客户获得这10个JPEG图形对象是使用10个串行的TCP连接,还是某些JPEG对象使用了一些并行的TCP连接。事实上,用户能够配置现代浏览器来控制连接的并行度。浏览器打开多个TCP连接,并且请求经多个连接请求某Web页面的不同部分。我们在下一章会看到,使用并行连接可以缩短响应时间。

  在继续讨论之前,我们来简单估算一下从客户请求HTML基本文件起到该客户收到整个文件止所花费的时间。为此,我们给出往返时间(Round-TripTime,RTT)的定义,该时间是指一个短分组从客户到服务器然后再返回客户所花费的时间。RTT包括分组传播时延、分组在中间路由器和交换机上的排队时延以及分组处理时延。现在考虑当用户点击超链接时会发生什么现象。如下图所示,,这引起浏览器在它和Web服务器之间发起一个TCP连接;这涉及一次“三次握手”过程,即客户向服务器发送一个小TCP报文段,服务器用一个小TCP报文段做出确认和响应,最后,客户向服务器返回确认(确认连接建立)。三次握手中前两个部分所耗费的时间占一个RTT。完成了三次握手的前两个部分后,客户结合三次握手的第三部分向该TCP连接发送一个HTTP请求报文。一旦该请求报文到达服务器,服务器就在该TCP连接上发送HTML文件。该HTTP请求/响应用去了另一个RTT。因此,粗略地讲,总的响应时间就是两个RTT加上服务器传输HTML文件的时间。

  非持续性连接的缺点非常明显。第一,必须为每一个请求的对象建立和维护一个全新的连接。对于每个这样的连接,在客户和服务器中都要分配TCP的缓冲区和保持TCP变量,这给Web服务器带来了严重的负担,因为一台Web服务器可能同时服务于数以百计不同客户的请求。第二,每一个对象经受两倍RTT的交付时延,即一个RTT用于创建TCP,另一个RTT用于请求和接收一个对象。

2. 采用持续连接的HTTP

  在采用HTTP1.1持续连接的情况,服务器在发送响应后保持该TCP连接打开。在相同的客户与服务器之,后续的请求和响应报文能够通过相同的连接进行传送。特别是,一个完整的Web页面(上例中的HTML基本文件加上10个图形)可以用单个持续TCP连接进行传送。更有甚者,位于同一台服务器的多个Web页面在从该服务器发送给同一个客户时,可以在单个持续TCP连接上进行。对对象的这些请求可以一个接一个地发出,而不必等待对未决请求(流水线)的回答。通常,如果一条连接经过一定时间间隔(一个可配置的超时间隔)仍未被使用,HTTP服务器就关闭该连接。HTTP的默认模式是使用带流水线的持续连接。

#HTTP 报文格式

  HTTP规范[RFC1945,RFC7230,RFC7540]包含了对HTTP报文格式的定义。HTTP报文有两种:请求报文和响应报文。下面讨论这两种报文。

1. HTTP请求报文

  下面提供了一个典型的HTTP请求报文:

 GET /soemedir/page.htmL HTTIP/1.1
 Host: www.Someschool.edu
 Connection: Close
 User-agent: Mozilla/5.0
 Accept-Language: fr

  通过仔细观察这个简单的请求报文,我们就能学到很多东西。首先,我们看到该报文是用普通的ASCII文本书写的,这样有一定计算机知识的人都能够阅读它。其次,我们看到该报文由5行组成,每行由一个回车和换行符(/t/n)结束。最后一行后再附加一个回车和换行符。虽然这个特定的报文仅有5行,但一个请求报文能够具有更多的行或者至少为一行。

  HTTP请求报文的第一行叫作请求行(requestline),其后继的行叫作首部行(headerline)。请求行有3个字段:方法字段URL字段HTTP版本字段。方法字段可以取几种不同的值,包括GET、POST、HEAD、PUT和DELETE。绝大部分的HTTP请求报文使用GET方法。当浏览器请求一个对象时,使用GET方法,在URL字段带有请求对象的标识。在本例中,该HTTP报文在请求对象/somedirpage.html。其版本字段是自解释的,在本例中,浏览器实现的是HTTP/1.1版本。

  现在我们看看本例的首部行。首部行Host:www.someschool.edu指明了的主机。你也许认为该首部行是不必要的,因为在该主机中已对象所在经有一条TCP连接存在了。但是,如我们将在2.2.5节中所见,该首部行提供的信息是Web代理高速缓存所要求的。通过包含Connection:close首部行,该浏览吉告诉服务器不要麻烦地使用持续连接,它要求服务吉在发送完被请求的对象后就关闭这条连接。User-agent:首部行用来指明用户代理,即向服务器发送请求的浏览器的类型。这里浏览器类型是Mozila/5.0,即Firefox浏览器。这个首部行是有用的,因为服务器可以有效地为不同类型的用户代理实际发送相同对象的不同版本(每个版本都由相同的URL寻址)。最后,Accept-language:首部行表示用户想得到该对象的法语版本(如果服务器中有这样的对象的话);否则,服务器应当发送它的默认版本。Accept-language:首部行仅是HTTP中可用的众多内容协商首部之一。

  看过一个例子之后,我们再来看看如下图所示的一个请求报文的通用格式。我们看到该通用格式与我们前面的例子密切对应。然而,在首部行(与附加的回车和换行符)后有一个实体体(entitybody)。使用GET方法时整个实体体为空,而使用POST方法(并不止)时才使用该实体体。当用户提交表单时,HTTP客户常常使用POST方法,例如当用户向搜索引擎提供搜索关键词时。使用POST报文时,用户仍可以向服务器请求一个Web页面,但Web页面的特定内容依赖于用户在表单字段中输入的内容。如果方法字段的值为POST,则实体体中包含的就与用户在表单字段中的输入值有关。

  HTML表单将是经常使用GET方法,并在(表单字段)所请求的URL中包括输入的数据。例如,一个表单使用GET方法,它有两个字段,分别填写的是monkeys和bananas这样,该URL结构为www.somesite.com/animalsearch?monkeys&bananas

这里的monkeybananas就是GET Params。

  • HEAD方法类似于GET方法。当服务器收到一个使用HEAD方法的请求时,将会用一个HTTP报文进行响应,但是并不返回请求对象。应用程序开发者常用HEAD方法进行调试跟踪。

  • PUT方法常与Web发行工具联合使用,它允许用户上传对象到指定的Web服务需上指定的路径(目录)。PUT方法也被那些需要向Web服务器上传对象的应用程序使用。

  • DELETE方法允许用户或者应用程序删除Web服务器上的对象。

2.HTTP响应报文

  下面提供了一条典型的HTTP响应报文。该响应报文可以是对刚刚讨论的例子中请求报文的响应。

HTTP/1.1 200 OK
Conriection: close
Date: Tue, 18 Aug 2015 15:44:04 GMT
Server: Apache/2.2.3 (CentOS)
Last-Modified: Tue, 18 Aug 2015 15:11:03 GMT
Content-Length: 6821
Content-Type: text/html
(data ...)

  仔细看一下这个响应报文。它有三个部:一个初始状态行(status line),6个首部行(header line),然后是实体体(entity body)。实体体部分是报文的主要部分,即它包含了所请求的对象本身(表示为data…)。

  状态行有3个字段:协议版本字段、状态码和响应状态信息。在这个例子中,状态行指示服务器正在使用HTTP/1.1,并且一切正常(状态码200,即服务器已经找到并正在发送所请求的对象)。

  现在来看看首部行。

  • Connection: close首部行告诉客户,发送完报文后将关闭该TCP连接。

  • Date首部行指示服务器产生并发送该响应报文的日期和时间。值得一提的是,这个时间不是指对象创建或者最后修改的时间,而是服务器从它的文件系统中检索到该对像,将该对象插入响应报文,并发送该响应报文的时间。

  • Server首部行指示该报文是由一台Apache Web服务器产生的,它类似于HTTP请求报文中的User-agent首部行。

  • Last-Modiftied首部行指示该对象创建或最后修改的时间与日期。“Last-Modified首部行对既可能在本地客户也可能在网络缓存服务器(即代理服务器)上的对象缓存来说非常重要,下文将更为详细地讨论Last-Modified首部行。

  • Content-Length首部行指示了被发送对象中的字节数。Content-Type首部行指示了实体体中的对象是HTML文本。(该对象类型应该正式地用Content-Type首部行而不是文件扩展名来指示。)

    Content-Length也是可以引发安全问题的,比如CVE-2024-21096。

一些常见的状态码和相关的短语包括:

  • 200 OK:请求成功,信息在返回的响应报文中。
  • 301 Moved Permanently:请求的对象已经被永久转移了,新的URL定义在响应报文的Location首部行。客户软件将自动获取新的URL。
  • 400 Bad Request: 一个通用差错代码,指示该请求不能被服务器理解。
  • 403 Forbidden: 拒绝访问(无权限访问或不合规范等)。
  • 404 Not Found: 被请求的文档不在服务器上。
  • 500 Internal Server Error: 服务器内部错误。
  • 505 HTTP Version Not Supported: 服务器不支持请求报文使用的HTTP版本。

  在本节中,我们讨论了HTTP请求报文和响应报文中的一些首部行。HTTP规范中定义了许许多多的首部行,这些首部行可以被浏览器、Web服务器和网络缓存服务器插入(当然也可以自己来加)。我们只提到了全部首部行中的少数几个,在2.2.5节中我们讨论网络Web缓存时还会涉及其他几个。

  浏览器是如何决定在一个请求报文中包含哪些首部行的呢?Web服务器又是如何决定在一个响应报文中包含哪些首部行呢?浏览器产生的首部行与很多因素有关,包括浏览器的类型和版本、浏览器的用户配置、浏览器当前是否有一个缓存的但可能超期的对象版本。Web服务器的表现也类似:在产品、版本和配置上都有差异,所有这些都会影响响应报文中包含的首部行。

  我们前面提到了HTTP服务器是无状态的。这简化了服务器的设计,并且允许工程师开发可以同时处理数千个TCP连接的高性能Web服务器。然而一个Web站点通常希望能够识别用户,可能是因为服务器希望限制用户的访问,或者因为它希望把内容与用户身份联系起来。为此,HTTP使用了Cookie。Cookie在[RFC 6265]中定义,它允许用户进行跟踪。目前大多数商务Web站点都使用了Cookie。

如下图所示,Cookie技术有4个组件:

  • 在HTTP响应报文中的一个Cookie首部行;

  • 在HTTP请求报文中的一个Cookie首部行;

  • 在用户端系统中保留的一个Cookie文件,并由用户的浏览器进行管理;

  • 位于Web站点的一个后端数据库。

这里省去一个例子。

  Cookie可以用于标识一个用户。用户首次访问一个站点时,可能需要提供一个用户标识(可能是名字[实际上会是根据某个密钥加一些个人信息加密生成的])。在后继会话中,浏览器向服务器传递一个Cookie首部,从而向该服务器标识了用户。因此Cookie可以在无状态的HTTP之上建立一个用户会话层。例如,当用户向一个基于Web的电子邮件系统注册时,浏览需向服务器发送Cookie信息,允许该服务器在用户与应用程序会话的过程标识该用户。

  尽管Cookie通常能够简化用户的因特网购物活动,但是其使用仍具有争议,因为它被认为是对用户隐私的一种侵害。如我们刚才所见,结合Cookie和用户提供的账户信息,Web站点可以得知许多有关用户的信息,并可能将这些信息卖给第三方。

所以现在很多站点会询问是否允许记录Cookie。

#Web缓存

  Web缓存器(Web cache)也叫代理服务器(proxy server),它是能够代表初始Web服务器来满足HTTP请求的网络实体。Web缓存器有自己的磁盘存储空间,并在存储空间中保存最近请求过的对象的副本。如下图所示,可以配置用户的浏览器,使得用户的所有HTTP请求首先指向Web缓存器[RFC 7234]。一且某浏览器被配置,每个对某对象的浏览器请求首先被定向到该Web缓存器。举例来说,假设浏览器正在请求对象http:www.someschool.edu/campus.gif,将会发生如下情况:

  值得注意的是Web缓存器既是服务器又是客户。当它接收浏览器的请求并发回响应时,它是一个服务器。当它向初始服务器发出请求并接收响应时,它是一个客户。

  在因特网上部署Web绥存器有两个原因。首先,Web缓存器可以大大减少对客户请求的响应时间,特别是当客户与初始服务器之间的瓶颈带宽远低于客户与Web缓存器之间的瓶颈带宽时更是如此。如果在客户与Web缓存器之间有一个高速连接(情况常常如此),并且如果用户所请求的对象在Web缓存器上,则Web缓存器可以迅速将该对象交付给用户。其次,如我们马上用例子说明的那样,Web缓存器能够大大减少一个机构的接入链路到因特网的通信量。通过减少通信量,该机构(如一家公司或者一所大学)就不必急于增加带宽,因此降低了费用。此外,Web缓存器能从整体上大大减少因特网上的Web流量,从而改善了所有应用的性能。

  为了深刻理解缓存器带来的好处,我们考虑在下图场景下的一个例子。该图显示了两个网络,即机构(内部)网络和公共因特网的一部分。机构网络是一个高速的局域网,它的一台路由器与因特网上的一台路由器通过一条15Mbps的链路连接。这些初始服务器与因特网相连但位于全世界各地。假设对象的平均长度为1Mb,从机构内的浏览器对这些初始服务器的平均访问速率为每秒15个请求。假设HTTP请求报文小到可以忽略,因而不会在网络中以及接入链路(从机构内部路由器到因特网路由器)上产生什么通信量。我们还假设在图中从因特网接入链路一侧的路由器转发HTTP请求报文(在一个IP数据报中)开始,到它收到其响应报文(通常在多个IP数据报中)为止的时间平均为2s。我们将该持续时延非正式地称为“因特网时延”

  

  总的响应时间,即从浏览器请求一个对象到接收到该对象为止的时间,是局域网时延、接入时延(即两台路由器之间的时延)和因特网时延之和。我们来粗略地估算一下这个时延,局域网上的流量强度为:

(15个请求/s)x(1Mb/请求)/(100Mbps)=0.15

然而接入链路上的流量强度(从因特网路由器到机构路由器)为

(15个请求/s)x(1Mb/请求)/(15Mbps)=1

  局域网上强度为0.15的通信量通常最多导致数十毫秒的时延,因此我们可以忽略局域网时延。然而,如在1.4.2节讨论的那样,如果流量强度接近1(就像图中接入链路的情况那样),链路上的时延会变得非常大并且无限增长。因此,满足请求的平均响应时间将在分钟的量级上。显然,必须想办法来改进时间响应特性。

  一个可能的解决办法就是增加接入链路的速率,如从15Mbps增加到100Mbps。这可以将接入链路上的流量强度减少到0.15,这样一来,两台路由器之间的链路时延也可以忽略了。这时,总的响应时间将大约为2s,即为因特网时延。但这种解决方案也意味着该机构必须将它的接入链路由15Mbps升级为100Mbps,这是一种代价很高的方案(很贵的)。

  现在来考虑另一种解决方案,即不升级链路带宽而是在机构网络中安装一个Web缓存器。这种解决方案如图2-13所示。现实中的命中率(即由一个缓存器所满足的请求的比率)通常在0.2~0.7之间。为了便于阐述,我们假设该机构的缓存命中率为0.4。因为客户和缓存连接在一个相同的高速局域网上,这样40%的请求将几乎立即会由缓存器得到响应,时延约在10ms以内。然而,剩下的60%的请求仍然要由初始服务器来满足。但是只有60%的被请求对象通过接入链路,接人链路上的流量强度从1.0减小到0.6。一般而言在15Mbps链路上,当流量强度小于0.8时对应的时延较小,约为几十毫秒。这个时延与2s因特网时延相比是微不足道的。

  因此,第二种解决方案提供的响应时延甚至比第一种解决方案更低,也不需要该机构升级它到因特网的链路。该机构理所当然地要购买和安装Web缓存器。除此之外其成本较低,很多缓存器使用了运行在廉价PC上的公共域软件。

  通过使用内容分发网络(Content Distribution Network,CDN),Web缓存器正在因特网中发挥着越来越重要的作用。CDN公司在因特网上安装了许多地理上分散的缓存器,因而使大量流量实现了本地化。有多个共享的CDN(例如Akamai和Limelight)和专用的CDN(例如谷歌和Netflix)。

条件GET方法

  尽管高速缓存能减少用户感受到的响应时间,但也引入了一个新的问题,即存放在缓存器中的对象副本可能是陈旧的。换名话说,保存在服务器中的对象自该副本缓存在客户上以后可能已经被修改了。幸运的是,HTTP有一种机制,允许缓存器证实它的对象是最新的。这种机制就是条件GET(conditional GET)[RFC7232]。如果HTTP请求报文使用GET方法,并且请求报文中包含一个If-modified-since首部行,那么,这个HTTP请求报文就是一个条件GET请求报文。

  为了说明GET方法的操作方式,我们看一个例子。首先,一个代理缓存器(proxy cache)代表一个请求浏览器,向某Web服务器发送一个请求报文:

GET /fruit/kiwi.gif HTTP/1.1
Host: www.exotiquecuisine.com

  其次,该Web服务器向缓存器发送具有被请求的对象的响应报文:

HTTP/1.1 200 OK
Date: Sat, 3 Oct 2015 15:39:29
Server: Apache/1.3.0 (Unix)
Last-Modified: Wed,9 Sep 2015 09:23:24
Content-Type: image/gif

(data...)

  该缓存器在将对象转发到请求的浏览器的同时,也在本地缓存了该对象。重要的是,缓存器在存储该对象时也存储了最后修改日期。最后,一个星期后,另一个用户经过该缓存器请求同一个对象,该对象仍在这个缓存器中。由于在过去的一个星期中位于Web服务器上的该对象可能已经被修改了,该缓存器通过发送一个条件GET执行最新检查。具体来说,该缓存器发送:

 GET /fruit/kiwi.gif HTTP/1.1
 Host: www.exotiquecuisine,com
 If-modified-since: Wed, 9 Sep 2015 09:23:24

  值得注意的是If-modified-since首部行的值正好等于一星期前服务器发送的响应报文中的Last-Modified首部行的值。该条件GET报文告诉服务器,仅当自指定日期之后该对象被修改过,才发送该对象。假设该对象自2015年9月9日09:23:24后没有被修改。接下来的第四步,Web服务器向该缓存器发送一个响应报文:

 HTTP/1.1 304 Not Modified
 Date: Sat, 10 Oct 2015 15:39:29
 Server: Apache/1.3.0 (Unix)
 
 (empty entity body)

  我们看到,作为对条件CET方法的响应,该Web服务器仍发送一个响应报文,但并没有在该响应报文中包含所请求的对象。包含该对象只会浪费带宽,并增加用户感受到的响应时间,特别是如果该对象很大更是如此。值得注意的是在最后的响应报文中,状态行中为304 Not Modified,它告诉缓存器可以使用该对象,能向请求的浏览器转发它(该代理缓存器)缓存的对象副本。

#HTTP/2

  于2015年标准化的HTTP/2[RFC 7540]是自HTTP/1.1以后的首个新版本,而HTTP/1.1是1997年标准化的。HTTP/2公布后,2020年,在排名前1000万的Web站点中,超过40%的站点支持HTTP/2。大多数浏览器(包括Chrome、Internet Explorer、Safari、0pera和Firefox)也支持HITP/2。

  HTTP/2的主要目标是减小感知时延,其手段是经单一TCP连接使请求与响应多路复用,提供请求优先次序和服务推送,并提供HTTP首部字段的有效压缩。HTTP/2不改变HTTP方法、状态码、URL或首部字段,而是改变数据格式化方法以及客户和服务器之间的传输方式。

  回想HTTP/1.1,其使用持续TCP连接,允许经单一TCP连接将一个Web页面从服务器发送到客户。由于每个Web页面仅用一个TCP连接,服务器的套接字数量被压缩,并且所传送的每个Web页面平等共享网络带宽(如下面所讨论的)。但Web浏览器的开发者很快就发现了经单一TCP连接发送一个Web页面中的所有对象存在队首阻塞[Head Of Line (HOL) blocking]问题。

  为了理解HOL阻塞,考虑一个Web页面,它包括一个HTML基本页面、靠近Web页面顶部的一个大视频片段和该视频下面的许多小对象。进一步假定在服务器和客户之间的通路上有一条低速/中速的瓶颈链路(例如一条低速的无线链路)。使用一条TCP连接,视频片段将花费很长时间来通过该瓶颈链路,与此同时,那些小对象将被延迟,因为它们在视频片段之后等待。也就是说,链路前面的视频片段阻塞了后面的小对象。HTTP/1.1浏览器解决该问题的典型方法是打开多个并行的TCP连接,从而让同一Web页面的多个对象并行地发送给浏览器。采用这种方法,小对象到达并呈现在浏览器上的速度要快得多,因此可减小用户感知时延。

  TCP拥塞控制(将在第3章中详细讨论)也使得浏览器倾向于使用多条并行TCP连接而非单一持续连接。粗略来说,TCP拥塞控制针对每条共享同一条瓶颈链路的TCP连接,给出一个平等共享该链路的可用带宽。如果有n条TCP连接运行在同一条瓶颈链路上,则每条连接大约得到1/n带宽。通过打开多条并行TCP连接来传送一个Web页面,浏览器能够”欺骗”并霸占该链路的大部分人带宽。许多HTTP/1.1打开多达6条并行TCP连接并非为了避免HOL阻塞,而是为了获得更多的带宽。

  HTTP/2的基本目标之一是摆脱(或至少减少其数量)传送单一Web页面时的并行TCP连接。这不仅减少了需要服务器打开与维护的套接字数量,而且允许TCP拥塞控制像设计的那样运行。但与只用一个TCP连接来传送一个Web页面相比,HTTP/2要求仔细设计相关机制以避免HOL阻塞。

  1. HTTP/2 成帧

  用于HOL阻塞的HTTP/2解决方案是将每个报文分成小帧,并且在相同TCP连接上交错发送请求和响应报文。为了理解这个问题,再次考虑由一个大视频片段和许多小对象(例如8个)组成的Web页面的例子。此时,服务器将从希望查看该Web页面的浏览器处接收到9个并行的请求。对于每个请求,服务器需要向浏览器发送9个相互竞争的报文。假定所有帧具有固定长度,该视频片段由1000帧(报文帧)组成,并且每个较小的对象由2帧组成。使用帧交错技术,在视频片段发送第一帧后,发送每个小对象的第一帧。然后在视频片段发送第二帧后,发送每个小对象的第二帧。因此,在发送视频片段的18帧后,所有小对象就发送完成了。如果不采用交错,则发送完其他小对象共需要发送1016帧。因此HTTP/2成帧机制能够极大地减小用户感知时延。

  将一个HTTP报文分成独立的帧、交错发送它们并在接收端将其装配起来的能力,是HTTP/2最为重要的改进。这一成帧过程是通过HTTP/2协议的成帧子层来完成的。当某服务器要发送一个HTTP响应,其响应由成帧子层来处理,即将响应划分为帧。响应的首部字段成为一帧,报文体被划分为一帧以用于更多的附加帧。通过服务器中的成帧子层,该响应的帧与其他响应的帧交错并经过单一持续TCP连接发送。当这些帧到达客户时,它们先在成帧子层装配成初始的响应报文,然后像以往一样由浏览器处理。类似地,客户的HTTP请求也被划分成帧并交错发送。

  除了将每个HTTP报文划分为独立的帧外,成帧子层也对这些帧进行二进制编码。二进制协议解析更为高效,会得到略小一些的帧,并且更不容易出错。

  2. 响应报文的优先次序和服务器推(推送)

  报文优先次序允许研发者根据用户要求安排请求的相对优先权,从而更好地优化应用的性能。如前文所述,成帧子层将报文组织为并行数据流发入相同的请求方。当某客户向服务器发送并发请求时,它能够为正在请求的响应确定优先次序,方法是为每个报文分配1到256之间的权重。较大的数字表明较高的优先。通过这些权重,服务器能够为具有最高优先权的响应发送第一帧。此外,客户也可通过指明相关的报文段ID,来说明每个报文段与其他报文段的相关性。

  HTTP/2的另一个特征是允许服务器为一个客户请求而发送多个响应。即除了对初始请求的啊应外,服务器能够向该客户推额外的对象,而无须客户再进行任何请求。因为HTML基本页指示了需要在页面呈现的全部对象,所以这一点是可实现的。因此无须等待对这些对象的HTTP请求,服务器就能够分析该HTML页,识别需要的对象,并在接收到对这些对象的明确的请求前将它们发送到客户。服务器推消除了因等待这些请求而产生的额外时延。

  HTTP/3

  QUIC(在第3章讨论)是一种新型的“运输”协议,它在应用层中最基本的UDP之上实现。QUIC具有几个能够满足HTTP的特征,例如报文复用(交错)、每流流控和低时延连接创建。HTTP/3是一种设计在QUIC之上运行的新HTTP。到2020年为止,HTTP/3处于因特网草案阶段,还没有全面标准化。许多HTTP/2特征(如报文交错)已被收入QUIC中,使得对HTTP/3的设计更为简单合理。

#因特网中的电子邮件

  自从有了因特网,电子邮件就在因特网上流行起来。当因特网还在襁褓之中时,电子邮件已经成为最流行的应用程序,年复一年,它变得越来越精细,越来越强大。它仍然是当今因特网上最重要和实用的应用程序之一。

  与普通邮件一样,电子邮件是一种异步通信媒介,即人们方便时就可以发送邮件,不必与他人的计划进行协调。与普通邮件相比,电子邮件更为快速,易于分发,而且价格便宜。现代电子邮件具有许多强大的功能,包括添加附件,超链接,HTML格式文本和图片。

  在本节中,我们将讨论于因特网电子邮件核心地位的应用层协议。在深入讨论这些应用层协议之前,我们先总体看看因特网电子邮件系统和他的关键组件。

  图2-14给出了因特网电子邮件系统的总体情况。从该图中,我们可以看到它有3个主要组成部分:用户代理(user agent)邮件服务器(mail server)和简单邮件传输协议〈Simple Mail Transfer Protocol,SMTP)。下面我们结合发送方Alice发电子邮件给接收方Bob的场景,对每个组成部分进行描述。用户代理允许用户阅读,恢复,转发,保存和撰写报文。微软的Outlook、Apple Mail、基于Web的Gmail和运行在智能手机上的Gmail客户端等都是电子邮件用户代理。当Alice完成邮件撰写时,她的邮件代理向其邮件服务器发送邮件,此时邮件放在邮件服务器的外出报文队列。当Bob要阅读代理在其邮件服务器的邮箱中取得该报文。

  邮件服务器形成了电子邮件体系结构的核心。每个接收方(如Bob)在其中的某个邮件服务器上有一个邮箱(mail box)。Bob的邮箱管理和维护着发送给他的报文。一个典型的邮件发送过程是:从发送方的用户代理开始,传输到发送方的邮件服务器,再传输到接收方的邮件服务器,然后在这里被分发到接收方的邮箱中。当Bob要在他的邮箱中读取该报文时,包含他邮箱的邮件服务器(使用用户名和口令)鉴别其身份。Alice的邮箱也必须能处理Bob的邮件服务器的故障。如果Alice的服务器不能将邮件交付给Bob的服务器(比如Bob的邮件服务器发生了故障),Alice的邮件服务器在一个报文队列(message queue)中保持该报文并在以后尝试再次发送。通常每30分钟左右进行一次和尝试,如果几天后仍不能成功,服务器就删除该报文并以电子邮件的形式通知发送方(Alice)。

  SMTP是因特网电子邮件中主要的应用层协议。它使用TCP可靠数据传输服务,从发送方的邮件服务器向接收方的邮件服务器发送邮件。像大多数应用层协议一样,SMTP也有两个部分:运行在发送方邮件服务器的客户端和运行在接收方邮件服务器的服务器端。每台邮件服务器上既运行SMTP的客户端也运行SMTP的服务器端。当一个邮件其他邮件服务器发送邮件时,它服务器向就表现为SMTP的客户;当一个邮件服务器从其他邮件服务器上接收邮件时,它就表现为SMTP的服务器。

#SMTP

  RFC 5321给出了SMTP的定义。SMTP是因特网电子邮件的核心。如前所述,SMTP用于从发送方的邮件服务器发送报文到接收方的邮件服务器。SMTP问世的时间比HTTP要长得多(初始的SMTP的RFC可追溯到1982年,而SMTP在此之前很长一段时间就已经出现了)。尽管电子邮件应用在因特网上的独特地位可以证明SMTP有着众多非常出色的性质,但它所具有的某种陈旧特征表明它仍然是一种继承的技术。例如,它限制所有邮件报文的体部分(不只是其首部)只能采用简单的7比特ASCII表示。在20世纪80年代早期,这种限制是明智的,因为当时传输能力不足,没有人会通过电子邮件发送大的附件或大的图片、声音、视频文件然而,在今天的多媒体时代,7比特ASCII的限制的确有点痛苦,即在用SMTP传送邮件之前,需要将二进制多媒体数据编码为ASCI码,并且在使用SMTP传输后要求将相应的ASCII码邮件解码还原为多媒体数据。2.2节讲过,使用HTTP传送前不需要将多媒体数据编码为ASCI码。

  为了描述SMTP的基本操作,我们观察一种常见的情景。假设Alice想给Bob发送一封简单的ASCII报文。

  • Alice调用她的邮件代理程序并提供Bob的邮件地址(例如bob@someschool.edu),撰写报文,然后指示用户代理发送该报文。

  • Alice的用户代理把报文发到她的邮件服务器,在那里该报文被放在报文队列中。

  • 运行在Alice的邮件服务器上的SMTP客户发现了报文队列中的这个报文,它创建一个到运行在Bob的邮件服务器上的SMTP服务器的TCP连接。

  • 在经过一些初始SMTP握手后,SMTP客户通过该TCP连接发送Alice的报文。

  • 在Bob的邮件服务器上,SMTP的服务器接收该报文。Bob的邮件服务器然后将该报文放入Bob的邮箱中。

  • 在Bob方便的时候,他调用用户代理阅读该报文。

  需要注意,SMTP一般不使用中间邮件服务器发送邮件,即使这两个邮件服务器位于地球的两端也是这样。假设Alice的邮件服务器在中国香港,而Bob的服务器在美国圣路易斯,那么这个TCP连接也是从香港服务器到圣路易斯服务器之间的直接相连。特别是,如果Bob的邮件服务器没有开机,该报文会保留在Alice的邮件服务器上并等待进行新的尝试,这意味着邮件并不在中间的某个邮件服务器中存留。

  我们现在仔细观察一下,SMTP是如何将一个报文从发送邮件服务器传送到接收邮件服务器的。首先,客户端SMTP(运行在发送邮件服务器主机上)在25号端口建立一个到服务器SMTP(运行在接收邮件服务器主机上)的TCP连接。如果服务器没有开机,客户端SMTP服务会在稍后继续尝试连接。一旦连接建立,服务器和客户执行某些应用层的握手(就像人们在相互交流前先进行自我介绍一样)。SMTP的客户和服务器在传输信息前先相互介绍。在SMTP握手的阶段,客户端SMTP指示发送方的邮件地址和接收方的邮件地址。一旦该SMTP客户和服务器彼此介绍之后,客户端SMTP服务发送该报文。SMTP能依赖TCP提供的可靠数据传输无差错地将邮件投递到接收服务器。该客户如果有另外的报文要发送到该服务器,就在该相同的TCP连接上重复这种处;否则,它指示TCP关闭连接。

  接下来我们分析一个在SMTP客户端(C)和SMTP服务器(S)之间交换报文文本的例子。客户的主机名为crepes.fs,服务器的主机名为hamburger.edu。以C:开头的ASCII码文本行正是客户交给其TCP套接字的那些行,以S:开头的ASCII码文本则是服务器发送给其TCP套接字的那些行。一旦创建了TCP连接,就开始下列过程:

S:  220 hamburger.edu
C:  HELO crepes.fr
S:  250 Hello crepes.fr,Pleased to meet YoU
C:  MAIL FROM:<alice@crepes.fs>
S:  250 alice@crepes.fr ... Sender ok
C:  RCPT TO: <bob@hamburger.edu>
S:  250 bob@hamburger.edu...Recipient ok
C:  DATA
S:  354 Enter mail,end with "." on a line by itself
C:  DO you like ketchup?
S:  How about Pickles?
C:  .
S:  250 Message accepted for delivery
C:  QUIT
S:  221 hamburger.edua closing connection

  在上例中,客户从邮件服务器crepes.fr在向邮件服务器hamburger.edu发送了一个报文(Do you like ketchup?How about pickles?)。作为对话的一部分,该客户发送了5条命令:HELO(是HELLO的缩写)、MAILFROM、RCPTTO、DATA以及QUIT。这些命令都是自解释的。该客户通过发送一个只包含一个句点的行,向服务器指示该报文结束了。(按照ASCII码的表示方法,每个报文以CRLF.CRLF结束,其中的CR和IF分别表示回车和换行。)服务器对每条命令做出回答,其中每个回答含有一个回答码和一些(可选的)英文解释。我们在这里指出SMTP用的是持续连接:如果发送邮件服务器有几个报文发往同一个接收邮件服务器,它可以通过同一个TCP连接发送所有这些报文。对每个报文,该客户用一个新的MAIL FROM:crepes.re开始,用一个独立的句点指示该邮件的结束,并且仅当所有邮件发送完后才发送QUIT。

  我们强烈推荐你使用Telnet与一个SMTP服务器进行一次直接对话。使用的命令是telnet ServerName 25其中serverName是本地邮件服务器的名称。当你这么做时,就直接在本地主机与邮件服务器之间建立了一个TCP连接。输完上述命令后,你立即会从该服务器收到220回答。接下来,在适当的时机发出HELO、MAIL FROM、RCPT TO、DATA、CRLF.CRLF以及QUIT等SMTP命令。

  强烈推荐你做本章后面的编程作业3。在该作业中,你将在SMTP的客户端实现一个简单的用户代理,它允许你经本地邮件服务器向任意的接收方发送电子邮件报文。

作业3:邮件客户

  这个编程作业的目的是创建一个向任何接收方发送电子邮件的简单邮件客户。你的客户将必须与邮件服务器(如谷歌的电子邮件服务)创建一个TCP连接,使用SMTP协议与邮件服务器进行交谈,经该邮件服务器向某接收方(如你的朋友)发送一个电子邮件报文,最后关闭与该邮件服务器的TCP连接。

  对本作业,配套Web站点为你的客户提供了框架代码。你的任务是完善该代码并通过向不同的用户账户发送电子邮件来测试你的客户。你也可以尝试通过不同的服务器(例如谷歌的邮件服务器和你所在大学的邮件服务器)进行发送。

#邮件报文格式

  当Alice给Bob写一封邮寄时间很长的普通信件时,她可能要在信的上部包含各种各样的环境首部信息,如Bob的地址、她自己的回复地址以及日期等。类似地,当一个人给另一个人发送电子邮件,一个包含环境信息的首部位于报文体前面。这些环境信息包括在一系列首部行,这些行由RFC 5322定义。首部行和该报文的体用空(即\t\n)进行分隔。RFC 5322定义了邮件首部行和它们的语义解释的精确格式。如同HTTP一样,每个首部行包含了可读的文本,是由关键词后跟冒号及其值组成的。某些关键词是必需的,另一些则是可选的。每个首部必须含有一个From:首部行和一个To:首部行,一个首部也许包含一个Subjeet:首部行以及其他可选的首部行。注意:这些首部行不同于我们在2.3.1节所学到的SMTP命令(即使那里包含了某些相同的词汇,如from和to)。那节中的命令是SMTP握手协议的一部分。本节中考察的首部行则是邮件报文自身的一部分。

  一个典型的报文首部如下:

From: alice@crepes.fr
TD: bob@hamburger.edu
Subject: Searching for the meaning of 1ife

  在报文首部之后,紧接一个空白行,然后是ASCII格式表示的报文体。你应当用telnet向邮件服务器发送包含一些首行部的报文,包括Subject:首部行。

#邮件访问协议

  一旦SMTP将邮件报文从Alice的邮件服务器交付给Bob的邮件服务器,该报文就被放入了Bob的邮箱中。假设Bob(接收方)在其本地主机(如智能手机或PC)上运行用户代理程序,考虑在他的本地PC上也放置一个邮件服务器是自然而然的事,在这种情况下,Alice的邮件服务器就能直接与Bob的PC进行对话了。然而这种方法会有一个问题:前面讲过邮件服务器管理用户的邮箱,并且和运行SMTP的客户端和服务器端。如果Bob的邮件服务器位于他的PC上,那么为了能够及时接收可能在任何时候到达的新邮件,他的PC必须总是不间断地运行着并一直保持在线。这对于许多因特网用户而言是不现实的。相反,典型的用户通常在本地PC上运行一个用户代理程序,它访问存储在总是保持开机的共享邮件服务顺上的邮箱。该邮件服务顺与其他用户共享。

  现在我们考虑当从Alice向Bob发送一个电子邮件报文时所采取的路径。我们刚才已经知道,在沿着该路径的某些点上,需要将电子邮件报文存放在Bob的邮件服务器上。通过让Alice的用户代理直接向Bob的邮件服务器发送报文,就能够做到这一点。然而,通常Alice的用户代理和Bob的邮件服务器之间并没有一个直接的SMTP对话。相反,如图2-16所示,Alice的用户代理用SMTP或HTTP将电子邮件报文推入她的邮件服务器,接着她的邮件服务器(作为一个SMTP客户)再用SMTP将该邮件中继到Bob的邮件服务需。为什么该过程要分成两步?主要是因为不通过Alice的邮件服务器进行中继,Alice的用户代理将没有任何办法到达一个不可达的目的地邮件服务器。通过首先将邮件存放在自己的邮件服务器中,Alice的邮件服务器可以重复地尝试向Bob的邮件服务顺发送该报文,如每30分钟一次直到Bob的邮件服务器变得运行为止。(并且如果Alice的邮件服务器关机,则她能向系统管理员进行申告)

  但是对于该难题仍然有一个疏漏的环节,像Bob这样的接收方,是如何通过运行其本地PC上的用户代理,获得位于他的某ISP的邮件服务器上的邮件呢?值得注意的是Bob的用户代理不能使用SMTP得到报文,因为SMTP是一个推协议,取得报文是一个拉操作。

  今天,Bob从邮件服务器取回邮件有两种常用方法。如果Bob使用基于Web的电子邮件或智能手机上的客户端(如Gmail),则用户代理将使用HTTP来取回Bob的电子邮件。这种情况要求Bob的电子邮件服务器具有HTTP接口和SMTP接口(与Alice的邮件服务器通信)。另一种方法是使用由RFC 3501定义的因特网邮件访问协议(Internet Mail Access Protocol,IMAP),这通常用于微软的Outlook等。HTTP和TMAP方法都支持Bob管理自己邮件服务器中的文件夹,包括将邮件移动到他创建的文件夹中,删除邮件,将邮件标记为重要邮件等。

#DNS:因特网的目录服务

  因特网上的主机和人类一样,可以使用多种方式进行标识。主机的一种标识方法是用主机名(hostname),如www.facebook.com、www.google.com、gaia.cs.umass.edu等,这些名字便于记忆也乐于被人们接受。然而,主机名几乎没有提供(即使有也很少)关于主机在因特网中位置的信息。(一个名为www.eurecom.fr的主机以国家码.fr结束,告诉我们该主机很可能在法国,仅此而已。)况且,主机名可能由不定长的字母数字组成,路由器难以处理。为此,主机也可以使用所谓的IP地址(IP address)进行标识。

  我们将在第4章更为详细地讨论下地址,但现在简略地介绍一下还是有必要的。一个IP地址(这里仅指IPv4)由4个字节组成,并有着严格的层次结构。例如121.7.106.83这样一个IP地址,其中的每个字节都被句点分隔开来,表示了0~255的十进制数字。我们说IP地址具有层次结构,是因为当我们从左至右扫描它时,会得到越来越具体的关于主机位于因特网何处的信息(即在众多网络的哪个网络里)。类似地,当我们从下向上查看邮政地址时,能够获得该地址位于何处的越来越具体的信息。

#DNS提供的服务

  我们刚刚看到了识别主机有两种方式一一主机名和IP地址。人们喜欢便于记忆的主机名标识方式,而路由器则喜欢定长的、有着层次结构的IP地址。为了对这些不同的偏好进行折中,我们需要一种能进行主机名到IP地址转换的目录服务。这就是域名系统(Domain Name System,DNS)的主要任务。DNS是:

  • 一个由分层的DNS服务器(DNS server)实现的分布式数据库。
  • 一个使得主机能够查询分布式数据库的应用层协议。

  DNS服务器通常是运行了BIND(Berkeley Internet Name Domain)软件[BIND 2020]的UNIX机器。DNS协议运行在UDP之上,使用53号端口。

什么是BIND?

  BIND是一款实现DNS服务器的开放源码软件,够提供双向解析,转发,子域授权,view等功能,是世界上使用最为广泛的DNS服务器软件,目前Internet上半数以上的DNS服务器有都是用Bind来架设的。

DNS:通过客户-服务器模式提供的重要网络功能

与HTTP,FTP和SMP一样,DNS、协议是应用层协议,其原因在于:

  • 使用客户-服务器模式运行在通信的端系统之间
  • 在通信的端系统之间通过下面(指下层)的端到端协议来传送DNS报文。

然而,在其他意义上,DNS的作用非常不同于Web应用、文件传输应用以及电子邮件应用。与这些应用程序的不同之处在于,DNS不是一个直接和用户打交道的应用,而是为因特网上的用户应用程序以及其他软件提供一种核心功能,即将主机名转换为其背后的IP地址。我们在1.2节就提到,因特网体系结构的复杂性大多数位于网络的“边缘”。DNS通过采用位于网络边缘的客户和服务器,实现了关键的名字到数字的转化功能,他还是这种设计模式的另一个范例。

  DNS通常是由其他应用层协议所使用的,包括HTTP和SMTP,将用户提供的主机名解析为下地址。举一个例子,考虑运行在某用户主机上的一个浏览器(即一个HTTP客户)请求URLwww.someschool.edu/index.html页面时会发生什么现象。为了使用户的主机能够将一个HTTP请求报文发送到Web服务器www.someschool.edu,该用户主机必须获得www.someschool.edu的IP地址。其做法如下:

  • 同一台用户主机上运行着DNS应用的客户端
  • 浏览器从上述URL中抽取出主机名www.someschool.edu,并将主机名传给DNS应用的客户端。
  • DNS客户向DNS服务器发送一个包含主机名的请求。
  • DNS客户最终会收到一份回答报文,其中含有对应于该主机名的IP地址。
  • 一旦浏览器接收到来自DNS的该IP地址,它就向位于该了IP地址80端口的HTTP服务器进程发起一个TCP连接。

  从这个例子中,我们可以看到DNS给使用它的因特网应用带来了额外的时延,有时还相当可观。幸运的是,如我们下面讨论的那样,想获得的卫IP址通常就缓存在一个“附近的”DNS服务器,这有助于减少DNS的网络流量和DNS的平均时延。

除了进行主机名到IP地址的转换外,DNS还提供了一些重要的服务:

  • 主机别名(host aliasing)。有着复杂主机名的主机能拥有一个或者多个别名。例如,一台名为relay1.west-coast.enterprise.com的主机,可能还有两个别名enterprise.com和www.enterprise.com。在这种情况下,relay1.west-coast,enterprise.com也称为规范主机名(canonical hostname)。主机别名(当存在时)比主机规范名更加容易记忆。应用程序可以调用DNS来获得主机别名对应的规范主机名以及主机的IP地址。
  • 邮件服务器别名(mail server aliasing)。显而易见,人们也非常希望电子邮件地址好记忆。例如,如果Bob在雅虎邮件上有一个账户,Bob的邮件地址就像bob@yahoo.com这样简单。然而,雅虎邮件服务器的主机名可能更为复杂,不像yahoo.com那样简单好记(例如,规范主机名可能像relay1.west-coast.hotmail.com那样)。电子邮件应用程序可以调用DNS,对提供的主机别名进行解析,以获得该主机的规范主机名及其IP地址。。事实上,MX记录(参见后面)允许一个公司的邮件服务器和Web服务器使用相同(别名)的主机名,例如,一个公司的Web服务器和邮件服务器都能叫作enterprise.com。
  • 负载分配(load distribution)。DNS也用于在冗余的服务器(如冗余的Web服务器等)之间进行负载分配。繁忙的站点(如cnn.com)被冗余分布在多台服务器上(相同服务但是服务器不同),每台服务器均运行在不同的端系统上,每个都有着不同的IP地址。由于这些冗余的Web服务器,一个IP地址集合与同一个规范主机名相联系。DNS数据库中存储着这些IP地址集合。当客户对映射到某地址集合的名字发出一个DNS请求时,该服务器用IP地址的整个集合进行响应,但在每个回答中循环这些地址次序。因为客户通常总是向IP地址排在最前面的服务器发送HTTP请求报文,所以DNS就在所有这些冗余的Web服务器之间循环分配了负载。DNS的循环同样可以用于邮件服务器,因此多个邮件服务器可以具有相同的别名。一些内容分发公司也以更复杂的方式使用DNS,以提供Web内容分发(参见2.6.3节)。

  DNS由RFC 1034和RFC 1035定义,并且在几个附加的RFC中进行了更新。DNS是一个复杂的系统,我们在这里只是就其运行的主要方面进行学习。感兴趣的读者可以参考这些RFC文档以及Albitz和Liu的书[Albitz 1993|,亦可参阅文章[Mockapetris 1998]和[Mockapetris 2005],其中[Mockapetris 1998]是回顾性的文章,它对DNS组成和工作原理进行了细致的讲解。

#DNS工作机理概述

  下面给出一个DNS工作过程的总体概述,我们的讨论将集中在主机名到IP地址转换服务方面。

  假设运行在用户主机上的某些应用程序(如Web浏览器或邮件阅读器)需要将主机名转换为IP地址。这些应用程序将调用DNS的客户端,并指明需要被转换的主机名(在很多基于UNIX的机器上,应用程序为了执行这种转换需要调用函数gethostbyname())。用户主机上的DNS接到后,向网络中发送一个DNS查询报文。所有的DNS请求和回答报文使用UDP数据报经端口53发送。经过若干毫秒到若干秒的时延后,用户主机上的DNS接收到一个提供所希望映射的DNS回答报文。这个映射结果则被传递到调用DNS的应用程序。因此,从用户主机上调用应用程序的角度看,DNS是一个提供简单、直接的转换服务的黑盒子。但事实上,实现这个服务的黑盒子非常复杂,它由分布于全球的大量DNS服务器以及定义了DNS服务器与查询主机通信方式的应用层协议组成。

  DNS的一种简单设计是在因特网上只使用一个DNS服务器,该服务器包含所有的映射。在这种集中式设计中,客户直接将所有查询直接发往单一的DNS服务器,同时该DNS服务器直接对所有的查询客户做出响应。尽管这种设计的简单性非常具有吸引力,但它不适用于当今的因特网,因为因特网有着数量巨大(并持续增长)的主机。这种集中式设计的问题包括:

  • 单点故障〈single point of failure):如果该DNS服务器崩溃,整个因特网随之瘫痪。
  • 通信容量(traffic volume): 。单个DNS服务器不得不处理所有的DNS查询(用于为上亿台主机产生的所有HTTP请求报文和电子邮件报文服务)。
  • 远距离的集中式数据库(distant centralized database):单个DNS服务器不可能“邻近”所有查询客户。如果我们将单台DNS服务器放在纽约市,那么所有来自澳大利亚的查询必须传播到地球的另一边,中间也许还要经过低速和拥塞的链路。这将导致严重的时延。
  • 维护(maintenance):单个DNS服务器将不得不为所有的因特网主机保留记录。这不仅将使这个中央数据库无比庞大,而且它还不得不为解决每个新添加的主机而频繁更新。

  总的来说,在单一DNS服务器上运行集中式数据库完全没有可扩展能力。因此,DNS采用了分布式的设计方案。事实上,DNS是一个在因特网上实现分布式数据库的精彩范例。

1.分布式,层次数据库

   为了处理扩展性问题,DNS使用了大量的DNS服务器,它们以层次方式组织并且分布在全世界范围内。没有一台DNS服务器拥有因特网上所有主机的映射,这些映射分布在所有的DNS服务器上。大致说来,有3种类型的DNS服务器:根DNS服务器顶级域(Top-Level Domain,TLD)DNS服务器权威DNS服务器。这些服务器以图2-17中所示的层次结构组织起来。为了理解这3种类型的DNS服务器交互的方式,假定一个DNS客户要确定主机名www.amazon.com的IP地址。粗略说来,将发生下列事件。

  • 根DNS服务器:有超过1000台根DNS服务器实体遍及全世界。这些根服务器是13个不同根服务器的副本,由12个不同组织管理,并通过因特网号码分配机构来协调[IANA 2020]。根名字服务器的全部清单连同管理它们的组织及其下地址可以在[Root Servers 2020]中找到。根服务器提供TLD服务的IP地址。
  • 顶级域(TLD)DNS服务:对于每个顶级域(如com、org、net、edu和gov)和所有国家的顶级域(如uk、fr、cn和jp等),都有TLD服务器(或服务器集群)。Verisign Global Registry Services公司维护com顶级域的TLD服务器,Educause公司维护edu项级域的TLD服务器。支持TLD的网络基础设施可能是大而复杂的,[Osterweil 2012]对Verisign网络进行了很好的概述。所有项级域的列表参见[TLD list 2020]。TLD服务器提供了权威DNS服务器的IP地址。
  • 权威DNS服务器:在因特网上具有公共可访问主机(如Web服务器和邮件服务器)的每个组织机构必须提供公共可访问的DNS记录,这些记录将这些主机的名字上映射为IP地址。一个组织机构的权威DNS服务器收藏了这些DNS记录。一种方法是,一个组织机构可以选择实现自己的权威DNS服务器以保存这些记录;另一种方法是,该组织能够支付费用,让这些记录存储在某个服务提供商的一个权威DNS服务器中。多数大学和大公司实现并维护它们自己的基本和辅助(备份)的权威DNS服务器。

  根、TLD和权威DNS服务器都处在该DNS服务器的层次结构中,如图2-17所示。还有另一类重要的DNS服务器,称为本地DNS服务器(local DNS server)。严格说来,一个本地DNS服务器并不属于该服务器的层次结构,但它对DNS层次结构是至关重要的。每个ISP(如一个居民区的ISP或一个机构的ISP)都有一台本地DNS服务器(也叫默认名字服务器)。当主机与某个ISP连接时,该ISP提供一台主机的卫地址,该主机具有一台或多台其本地DNS服务器的IP地址(通常通过DHCP,将在第4章中讨论)。通过访问Windows或UNIX的网络状态窗口,用户能够容易地确定自己的本地DNS服务器的IP地址.主机的本地DNS服务器通常“邻近”本主机。对某机构ISP而言,本地DNS服务器可能就与主机在同一个局域网中;对于某居民区ISP来说,本地DNS服务器通常与主机相隔不超过几台路由器。当主机发出DNS请求时,该请求被发往本地DNS服务器,它起着代理的作用,并将该请求转发到DNS服务器层次结构中,下面我们将更为详细地讨论。

  我们来看一个简单的例子,假设主机cse.nyu.edu想知道主机gaia.cs.umass.edu的IP地址。同时假设纽约大学(NYU)的cse.nyu.edu主机的本地DNS服务器为dns.nyu.edu,并且gaia.cs.umass.edu的权威DNS服务器为dns.umass.edu。如图2-18所示,主机cse.nyu.edu首先向它的本地DNS服务器dns.nyu.edu发送一个DNS查询报文。该查询报文含有被转换的主机名gaia.cs.umass.edu。本地DNS服务器将该报文转发到根DNS服务骼。该根DNS服务器注意到其edu后缀并向本地DNS服务器返回负责edu的TLD服务器的IP地址列表.该本地DNS服务器则再次向这些TLD服务器之一发送查询报文.该TLD服务器注意到umass.edu后缀,并用权威DNS服务器的IP地址进行响应,该权威DNS服务器是负责马萨诸塞大学的dns.umass.edu.最后,本地DNS服务器直接向dns.umass.edu重发查询报文,dns.umass.edu用gaia.cs.umass.edu的IP地址进行响应。注意到在本例中,为了获得一台主机名的映射,共发送了8份DNS报文:4份查询报文和4份回答报文!我们将很快看到利用DNS缓存减少这种查询流量的方法。

  前面的例子假设了TLD服务器知道用于主机的权威DNS服务器的IP地址。一般而言,这种假设并不总是正确。相反,TLD服务器只是知道中间的某个DNS服务器,该中间DNS服务器才可能能知道用于该主机的权威DNS服务器,若不知道,则接着查询下一个中间服务器。例如,再次假设马萨诸塞大学有一台用于本大学的DNS服务器,称为dns.umass.edu。同时假设该大学的每个系都有自己的DNS服务器,每个系的DNS服务器是本系所有主机的权威服务器。在这种情况下,当中间DNS服务器dns.umass.edu收到了对某主机的请求时,该主机名是以cs.umass.edu结尾,它向dns.nyu.edu(前面提到的请求者的本地DNS服务器)返回dns.cs.umass.edu的IP地址,后者是所有以cs.umass.edu结尾的主机的权威服务器。本地DNS服务dns.nyu.edu则向权威DNS服务器发送查询,该权威DNS服务器向本地DNS服务器返回所希望的映射,该本地服务次向请求主机返回该映射。在这个例子中,共发送了10份DNS报文。相当于在访问权威DNS服务器时可能存在中间DNS服务器的情况。

  图2-18所示的例子利用了递归查询(recursive query)迭代查询(iterative query)。从cse.nyu.edu到dns.nyu.edu发出的查询是递归查询,因为该查询以自己的名义请求dns.nyu.edu来获得该映射。而后继的3个查询是迭代查询,因为所有的回答都是直接返回给dns.nyu.edu。从理论上讲,任何DNS查询既可以是迭代的也可以是递归的。例如,图2-19显示了一条DNS查询链,其中的所有查询都是递归的。实践中,查询通常遵循图2-18中的模式:从请求主机到本地DNS服务器的查询是递归的,其余的查询是迭代的。

2.DNS缓存

  至此我们的讨论一直忽略了DNS系统的一个非常重要的特色:DNS缓存(DNS caching)。实际上,为了改善时延性能并减少在因特网上到处传输DNS报文数量,DNS广泛使用了缓存技术。DNS缓存的原理非常简单。在一个请求链中,当某DNS服务器接收一个DNS回答(例如,包含某主机名到IP地址的映射)时,它就能将映射缓存在本地存储器中。例如,在图2-18中,每当DNS服务器dns.nyu.edu从某个DNS服务器接收到一个回答,他就能够缓存包含在该回答中的任何信息.如果在DNS服务器中缓存了一个主机名/IP地址对,另一个对相同主机名的查询到达该DNS服务器时,该DNS服务器就能够提供所要求的IP地址,即使它不是该主机名的权威服务器。由于主机和主机名与耻地址间的映射并不是永久的,DNS服务器在一段间后将丢弃缓存的信息。

#DNS记录和报文

  共同实现DNS分布式数据库的所有DNS服务器存储了资源记录(Resource Record,RR),RR提供了主机名到IP地址的映射(或者主机名到另一主机名的映射,后述)。每个DNS回答报文包含了一条或多条资源记录。在本小节以及后续小节中,我们概要地介绍DNS资源记录和报文,更详细的信息可以在[Albitz 1993]或有关DNS的REFC文档[RFC 1034,RFC 1035]中找到。

资源记录是一个包含了下列字段的4元组:

(Name, Value, Type, TTL)

  TTL(Time To Life)是该记录的生存时间,它决定了资源记录应当从缓存中删除的时间。在下面给出的记录例子,我们忽略掉TTL字段。Name和Value的意义取决于Type:

  • 如果Type=A,则对该主机名而言Name是主机名,Value是该主机名对应的IP地址。因此,一条类型为A的资源记录提供了标准的主机名到IP地址的映射。例如(relay1.bar.foo.com, 145.37.93.126, A)就是一条类型A的记录。

  • 如果Type=NS,则对该域中的主机而言Name是域(如foo.com),而Value是一个知道如何获得该域中主机IP地址的权威DNS服务器的主机名。这个记录用于沿着查询链来路由DNS查询。例如(foo.com,dns.foo.com,NS)就是一条类型为NS的记录。

  • 如果Type=CNAME,则Value是主机别名Name对应的规范主机名。该记录能够向查询的主机提供一个主机名对应的规范主机名,例如(foo.com,relay1.bar.foo.com,CNAME)就是一条CNAME类型的记录。

  • 如果Type=MX,则Value是一个别名为Name的邮件服务器的规范主机名。举例来说,(foo.com,mail.bar.foo.com,MX)就是一条MX记录。MX记录人允许邮件服务器主机名有具有简单的别名。值得注意的是,通过使用MX记录,一个公司的邮件服务器和其他服务器(如它的Web服务器)可以使用相同的别名。为了获得邮件服务器的规范主机名,DNS客户应当请求一条MX记录;而为了获得其他服务器的规范主机名,DNS客户应当请求CNAME记录.

    也就是说,在以邮件服务为目的检索foo.com时,会请求MX记录,再根据其邮件服务器的规范主机名来查询其IP;以其他服务为目的检索foo.com时,则会请求CNAME记录,得到规范主机名,再查询其IP地址.

  如果一台DNS服务器是某特定主机名的权威DNS服务器,那么该DNS服务器会有一条包含用于该主机名的类型A记录(即使该DNS服务器不是其权威DNS服务器,它也可能在缓存中包含几条类型A记录)。如果服务器不是用于某主机名的权威服务器,那么该服务器将包含一条类型NS记录,该记录对应于包含主机名的域;它还将包含一条类型A记录,该记录提供了在NS记录的Value字段中的DNS服务器的IP地址.

  举例来说,假设一台edu TLD服务器不是主机gaia.cd.umass.edu的权威DNS服务器,则该服务器将包含一条主机gaia.cs.umass.edu的域记录,如(umass.edu,dns.umass.edu,NS);该edu TLD服务器还将包含一条类型A记录,如(dns.umass.edu,128.119.40.111,A),该记录将名字dns.umass.edu映射为一个IP地址。

1.DNS报文

  在本节前面,我们提到了DNS查询和回答报文。DNS只有这两种报文,并且查询和回答报文有着相同的格式,如图2-20所示。

DNS报文中的各个字段语义如下:

  • 前12字节是首部区域,其中有几个字段。

    • 第一个字段(标识符/id)是一个16bit的数,用于标识该查询。这个标识符会被复制到对查询的回答报文中,以便让客户用它来匹配发送的请求和接收到的回答。标志字段中含有若干标志。
    • 标志位有以下几种:
      • “查询/回答(Response,QR)”标志位指出报文是查询报文(0)还是回答报文(1)。
      • 操作码(Opcode)中,0表示标准查询,1表示反向查询,2表示服务器状态请求。
      • 当某DNS服务器是所请求名字的权威DNS服务器时,1bit的“权威的(Authoritative,AA)”标志位被置于回答报文中,以标志该响应服务器就是查询主机名的权威服务器。
      • TC(Truncated)表示是否被截断。值为1时,表示响应已超过512字节并已被截断,只返回前512个字节。
      • 如果客户(主机或者DNS服务器)在该DNS服务器没有某记录时,它将执行递归查询,并设置1bit的“希望递归(Recursion Desired,RD)”标志位。
      • 如果该DNS服务器支持递归查询,在它的回答报文中会设置1bit的“递归器查可用(Recursion Available,RA)”标志位。
      • Z是保留字段,在所有的请求和应答报文中,它的值必须为0。
      • rcode(Reply code)是返回码字段,表示响应的差错状态。当值为0时,表示没有错误;当值为1时,表示报文格式错误,服务器不能理解请求的报文;当值为2时,表示域名服务器失败,因为服务器的原因导致没办法处理这个请求;当值为3时,表示名字错误,只有对授权域名解析服务器有意义,指出解析的域名不存在;当值为4时,表示查询类型不支持,即域名服务器不支持查询类型;当值为5时,表示拒绝,一般是服务器由于设置的策略拒绝给出应答,如服务器不希望对某些请求者给出应答。

    在该首部中还有4个有关数量的字段.这些字段指出了在首部后的4类数据区域出现的数量。

  • 问题区域:包含了正在进行的查询信息。该区域包括:

    • 名字字段,包含正在被查询的主机名字;
    • 类型字段,指出有关该名字的正被询问的问题类型

    例如主机地址是与个名字相关联(类型A)还是与某个名字的邮件服务器相关联(类型MX)。

  • 回答区域:包含了对最初请求的Name的资源记录。前面讲过每个资源记录中有Type(如A、NS、CNAME和MX)字段、Value字段和TTL字段。在回答报文的回答区域中可以包含多条RR,因此一个主机名能够有多个IP地址(例如,就像本节前面讨论的冗余Web服务器)。

  • 权威区域:包含了其他权威服务器的信息(注意是其他权威服务器的信息不是该权威服务器的其他信息)。

  • 附加信息区域:包含了其他有帮助的记录。例如,对于一个MX请求的回答报文的回答区域包含了一条资源记录,该记录提供了邮件服务器的规范主机名。该附加信息包含一个类型A记录,该记录提供了用于该邮件服务器的规范主机名的IP地址。

DNS报文的详细解释,实例等可以查看DNS报文格式解析(非常详细)

  使用nslookup(nslookup program)可以从正在工作的主机直接向某些DNS服务器发送一个DNS查询。对于多数Windows和UNIX平台,nslookup程序是可用的。例如,从一台Windows主机打开命令提示符界面,直接键人nslookup即可调用nslookup程序。在调用nslookup后,你能够向任何DNS服务器(根、TLD或权威)发送DNS查询。在接收到来自DNS服务器的回答后,nslookup将显示包括在该回答中的记录(以人可读的格式)。从你自己的主机运行nslookup还有一种方法,即访问允许你远程应用nslookup的许多Web站点之一(在一个搜索引擎中键入nslookup就能够得到这些站点中的一个)。本章最后的DNS Wireshark实验将使你更为详细地研究DNS.

2.在DNS数据库中插入记录

  上面的讨论只是关注如何从DNS数据库中取数据。你可能想知道这些数据最初是怎么进入数据库中的。我们在一个特定的例子中看看这是如何完成的。假定你刚刚创建了一个称为网络乌托邦(Network Utopia)的令人兴奋的创业公司。你必定要做的第一件事是在注册登记机构注册域名networkutopia.com。注册登记机构(registrar)是一个商业实体,它验证该域名的唯一性,将该域名输入DNS数据库(如下面所讨论的那样),对提供的服务收取少量费用。1999年前,唯一的注册登记机构是Nework Solutions,它独家经营对于com、net和org域名的和注册。但是现在有许多注册登记机构竞争客户,因特网名字和地址分配机构(Internet Corporation for Assigned Names and Numbers,ICANN)向各种注册登记机构授权。在http://www.internic.net上可以找到授权的注册登记机构的完整列表。

  当你向某些注册登记机构注册域名networkutopia.com时,需要向该机构提供你的基本、辅助权威DNS服务器的名字和IP地址。假定该名字和IP地址是dns1.networkutopia.com和dns2.networkutopia.com及212.212.212.1和212.212.212.2。对这两个权威DNS服务器的每一个,该注册登记机构确保将一个类型NS和一个类型A的记录输入TLD com服务器。特别是对于用于networkutopia.com的基本权威服务器,该注册登记机构将下列两条资源记录插和人DNS系统中:

(networkutopia.com, dns1.networkutopia.com, NS)
(dns1.networkutoepia.com, 212.212.212.1, A)

  你还必须确保用于Web服务器www.networkutopia.com的类型A资源记录和用于邮件服务器mail.networkutopia.com的类型MX资源记录被输入你的权威DNS服务器中。[最近,DNS协议中添加了一个更新(UPDATE)选项,允许通过DNS报文对数据库中的内容进行动态添加或者删除。[RFC 2136]和[RFC 3007]定义了DNS动态更新。]

  一旦完成所有这些步骤,人们将能够访问你的Web站点,并向你公司的雇员发送电子邮件。我们通过验证该说法的正确性来总结DNS的讨论。这种验证也有助于充实我们已经学到的DNS知识。假定在澳大利亚的Alice要观看www.networkutopia.com的Web页面。如前面所讨论,她的主机将首先向其本地DNS服务器发送请求。该本地服务器接着联系一个TLD com服务器。(如果TLD com服务器的地址没有被缓存,该本地DNS服务器也将必须与根DNS服务器相联系。)该TLD服务器包含前面列出的类型NS和类型A资源记录,因为注册登记机构将这些资源记录搬入所有的TLD com服务器。该TLD com服务器向Alice的本地DNS服务器发送一个回答,该回答包含了这两条资源记录。本地DNS服务需则加212.212.212.1发送一个DNS查询,请求对应于www.networkutopia.com的类型A记录。该记录提供了所和希望的Web服务器的IP地址,如212.212.71.4,本地DNS服务器将该地址回传给Alice的主机。Alice的浏览器此时能够向主机212.212.71.4发起一个TCP连接,并在该连接上发送一个HTTP请求。

DNS脆弱性

我们已经看到DNS是因特网基础设施的一个至关重要的组件,对于包括Web、电子邮件等的许多重要的服务,没有它都不能正常工作。因此,我们自然要问:DNS会受到攻击吗?DNS是一个易受攻击的目标吗?它是将会被淘汰的服务吗?大多数因特网应用会随之一起无法工作吗?

第一种针对DNS服务的攻击是分布式拒绝服务(DDoS)带宽洪泛攻击。倒如,某攻击者可能试图向每个DNS根服务器发送大量的分组,使得大多数合法DNS请求得不到回答。这种对DNS根服务器的DDoS大规模攻击实际发生在2002年10月21日。在这次攻击中,攻击者利用用一个僵尸网络向13个DNS根服务器中的每个都发送了大批的ICMP ping报文负载。(5.6节中讨论ICMP报文。此时,知道ICMP分组是特殊类型的IP数据报就可以了.)幸运的是,这种大规模攻击所带来的损害很小,对用户的因特网体验几乎没有或根本没有影响。攻击者的确成功地将大量的分组指向了根服务器,但许多DNS根服务器受到了分组过滤器的保护,配置的分组过滤器阻挡了所有指向根服务器的ICMP ping报文.这些被保护的服务器因此未受伤并且与平常一样发挥着作用。此外,大多数本地DNS服务器缓存了顶级域名服务器的IP地址,使得这些请求过程通常为DNS根服务器分流.

对DNS的更为有效的潜在DDoS攻击将是向顶级域名服务器(例如向所有处理.com域的顶级域名服务器)发送大量的DNS请求。过滤指向DNS服务器的DNS请求将更为困难,并且顶级域名服务器不像根服务器那样容易绕过。这种对顶级域名服务提供商的攻击发生在2016年10月21日。该DDoS攻击是通过发送大量的DNS查找请求进行的,这些请求来自一个由十万多个物联网设备组成的僵尸网络,这些设备包括被Miral恶意软件感染的打印机、网络相机、住宅网关和婴儿监视器等。攻击几乎持续了一整天,亚马逊、推特、Netflix、GitHub和Spotify都受到了干扰。

DNS也可能潜在地以其他方式被攻击。在中间人攻击中,攻击者截获来自主机的请求并返回伪造的回答。在DNS投毒攻击中,攻击者向一台DNS服务器发送伪造的回答,诱使服务器在它的缓存中接收伪造的记录。这些攻击中的任意一种都可能被用于不良用途,例如将没有疑心的Web用户重定向到攻击者的Web站点。DNS安全扩展套件(已经设计并部署了DNSSEC[Gieben 2004;RFC 4033])用于防范这些漏洞。作为DNS的安全版本,DNSSEC处理了许多类似这样的攻击并在因特网上得到了普及。

#P2P文件分发

  到目前为止本章中描述的应用(包括Web、电子邮件和DNS)都采用了客户-服务器体系结构,极大地依赖于总是打开的基础设施服务器。在2.1.1节讲过,使用P2P体系结构,对总是打开的基础设施服务器依赖最少(或者没有依赖)。与之相反,成对间歇连接的主机(称为对等方)彼此直接通信。这些对等方并不为服务提供商所拥有,而是受用户控制的计算机。

  在本节中我们将研究一个非常自然的P2P应用,即从单一服务器向大量主机(称为对等方)分发一个大文件。该文件也许是一个新版的Linux操作系统,也许是对于现有操作系统或应用程序的一个软件补丁,或一个MPEG视频文件。在客户-服务器文件分发中,该服务器必须向每个对等方发送该文件的一个副本,即服务器承受了极大的负担,并且消耗了大量的服务器带宽。在P2P文件分发中,每个对等方能够向任何其他对等方重新分发它已经收到的该文件的任何部分,从而在分发过程中协助该服务器。到2020年止,最为流行的P2P文件分发协议是BitTorrent。该应用程序最初由Bram Cohen研发,现不在有许多不同的独立且符合BitTorrent协议的BitTorrent客户,就在有许多像有许多符合HTTP协议的Web浏览器客户一样。在下面的小节中,我们首先考察在文件分发环境中P2P体系结构的自扩展性。然后我们更为详细地描述BitTorrent,突出它的最为重要的特性。

1.P2P体系结构的扩展性

  为了将客户-服务器体系结构与P2P体系结构进行比较,阐述P2P的内在自扩展性,我们现在考虑一个用于两种体系结构类型的简单定量模型,将一个文件分发给一个固定对等方集合。如图2-21所示,服务器和对等方使用接入链路与因特网相连。其中$u_s$表示服务器接入链路的上载速率,$u_i$表示第i对等方接入链路的上载速率,$d_i$表示第i对等方接入链路的下载速率。用F表示被分发的文件长度(以bit计),N表示要获得该文件副本的对等方的数量。分发时间(distribution time)是所有N个对等方得到该文件的副本所需要的时间。在下面分析分发时间的过程,我们对客户-服务器和P2P体系结构做了简化(并且通常是准确的[Akela 2003])的假设,即因特网核心具有足够的带宽,这意味着所有瓶颈都在网络接入链路。我们还假设服务器和客户没有参与任何其他网络应用,因此它们的所有上传和下载访问带宽能被全部用于分发该文件。

  我们首先来确定对于客户-服务器体系结构的分发时间,我们将其表示为$D_{cs}$。在客户-服务器体系结构,没有对等方帮助分发文件。那么情况大致如下:

  • 服务器必须向N个对等方的每个传输该文件的一个副本。因此该服务器必须传输NF bit量值的数据。因为该服务器的上载速率是$u_s$,分发该文件的时间必定至少为$NF/u_{s}$。
  • 令$d_{min}$表示具有最小下载速率的对等方的下载速率,即$d_{min}=min{d_1, d_2, …, d_N}$。具有最小下载速率的对等方不可能在少于$F/d_{min}$s的时间内获得该文件的所有F bit。因此最小分发时间至少为$F/d_{min}$s。

将以上两条综合,我们就可以得到:$$D_{cs} \geqslant max\lbrace\frac{NF}{u_s}, \frac{F}{d_{min}}\rbrace$$  该式提供了对于客户-服务器体系结构的最小分发时间的下界。因此我们取上面提供的这个下界作为实际发送时间,即下式(2-1):$$D_{cs} = max\lbrace\frac{NF}{u_s},\frac{F}{d_{min}}\rbrace$$  我们从式(2-1)看到,对于足够大的N,客户-服务器分发时间由$ND/u_s$确定。所以,该分发时间随着对等方N的数量线性地增加。因此举例来说,如果从某星期到下星期对等方的数量从1000增加了到了100万,将该文件分发到所有对等方所需要的时间就要增加1000倍。

  我们现在来对P2P体系结构进行简单的分析,其中每个对等方能够帮助服务器分发该文件。特别是当一个对等方接收到某些文件数据,它能够使用自己的上载能力重新将数据分发给其他对等方。计算P2P体系结构的分发间在某种程度上比计算客户-服务器体系结构的更为复杂,因为分发时间取决于每个对等方如何向其他对等方分发该文件的各个部分。无论如何,能够得到对该最小分发时间的一个简单表达式[Kumar 2006]。至此,我们先做如下观察:

  • 在分发的开始,只有服务器具有文件。为了使社区的这些对等方得到该文件,该服务器必须经其接入链路至少发送该文件的额每个bit一次。因此,最小分发时间至少是$F/u_s$。(与客户-服务器方案不同,由服务器发送过一次的比特可能不必由该服务器再次发送,因为对等方在它们之间可以重新分发这些比特。)
  • 与客户-服务器体系结构相同,具有最低下载速率的对等方不能够以小于$F/d_{min}$s的分发时间获得所有F bit。因此最小分发时间至少为$F/d_{min}$。
  • 最后,观察到系统整体的总上载能力等于服务器的上载速率加上每个单独的对等方的上载速率,即$u_{total}=u_s+u_1+…+u_N$。整个系统必须向这N个对等方交付F bit的数据,因此总共交付NF bit。这不能以快于$u_{total}$的速率完成。因此,最小的分发时间也至少是$NF/(u_s+u_1+…+u_N)$。

将这三项观察放在一起,我们获得了对P2P的最小分发时间,表达为$D_{P2P}$(下式记作式2-2)。$$D_{cs} \geqslant max\lbrace\frac{F}{u_s},\frac{F}{d_{min}},\frac{NF}{u_s+\sum_{i=1}^Nu_i}\rbrace$$  式(2-2)提供了对于P2P体系结构的最小分发时间的下界。这说明,如果我们认为一旦每个对等方接收到一个比特就能够重分发一个比特的话,则存在一个重新分发方案能实际取得这种下界[Kumar 2006]。实际上,备份发的是文件块而不是一个个bit。式(2-2)能够作为1实际最小分发时间的近似值。

  图2-22比较了客户-服务器和P2P体系结构的最小分发时间,其中假定所有的对等方具有相同的上载速率u。在图2-22中,我们已经设置了F/u=1小时,$u_s=10u, d_{min} \geqslant u_s$。即在一个小时中一个对等方能够传输整个文件,该服务器的传输速率是对等方上载速率的10倍,并且对等方的下载速率被设置得足够大,使之不会产生影响。我们从图2-22中看到,对于客户-服务器体系结构,随着对等方数量的增加,分发时间呈线性增长并且没有界。然而,对于P2P体系结构,最小分发时间不仅总是小于客户-服务器体系结构的分发时间,并且对于任意的对等方数量N,总是小于1小时。因此,具有P2P体系结构的应用程序能够是自扩展的。这种扩展性的直接成因是:对等方除了是比特的消费者外还是它们的重新分发者。

2.BitTorrent

  BitTorrent是一种用于文件分发的流行P2P协议[Chao 2011]。用BitTorment的术语来讲,参与一个特定文件分发的所有对等方的集合被称为一个洪流(torrent)。在一个洪流中的对等方彼此下载等长度的文件块(chunk),典型的块长度为256KB。当一个对等方首次加入一个洪流时,它没有块。随着时间的流逝,它累积了越来越多的块。当它下载块时,也为其他对等方上载了多个块。一且某对等方获得了整个文件,它也许离开潜流,或留在该洪流中并继续向其他对等方上载块。同时,任何对等方可能在仅具有块的子集的情况下就离开该洪流,并在以后重新加入该洪流中。

  我们现在更为仔细地观察BitTorrent运行的过程。因为BitTorrent是一个相当复杂的协以,所以我们将仅描述它最重要的机制。每个洪流具有一个基础设施节点,称为追踪器(tracker)。当一个对等方加入某洪流时,它向追踪器注册自己,并周期性地通知追踪器它仍在该洪流中。以这种方式,追踪器跟踪参与在洪流中的对等方。一个给定的洪流可能在任何时刻具有数以百计或数以千计的对等方。

  如图2-23所示,当一个新的对等方Alice加入该洪流时,追踪器随机地从参与对等方的集合中选择对等方的一个子集(为了具体起见,设有50个对等方),并将这50个对等方的了IP地址发送给Alice。Alice持有对等方的这张列表,试图与该列表上的所有对等方创建并行的TCP连接。我们称所有这样与Alice成功地创建一个TCP连接的对等方为“邻近对等方”(在图2-23中,Alice显示了仅有三个邻近对等方。通常,她应当有更多的对等方)。随着时间的流逝,这些对等方中的某些可能离开,其他对等方(最初50个以外的)可能试图与Alice创建TCP连接。因此一个对等方的邻近对等方将随时间而波动。

  在任何给定的时,每个对等方将具有来自该文件的块的子集,并且不同的对等方具有不同的子集。Alice周期性地(经TCP连接)询问每个邻近对等方它们所具有的块列表。如果Alice具有L个不同的邻居,她将获得L个块列表。有了这个信息,Alice将对她当前还没有的块发出请求(仍通过TCP连接)。

  因此在任何给定的时刻,Alice将具有块的子集并知道它的邻居具有哪些块。利用这些信息,Alice将做出两个重要决定。第一,她应当从她的邻居请求哪些块?第二,她应当向哪些向她请求块的邻居发送块?在决定请求哪些块的过程中,Alice使用一种称为最稀缺优先(rarest first)的技术。这种技术的思路是,针对她没有的块在她的邻居中决定最稀缺的块(最稀缺的块就是那些在她的邻居中副本数量最少的块)并首先请求那些最稀缺的块。这样,最稀缺块得到更为迅速的重新分发,其目标是(大致地)均衡每个块在洪流中的副本数量。

  为了决定她响应哪个请求,BitTorrent使用了一种机灵的对换算法。其基本想法是,Alice根据当前能够以最高速率向她提供数据的邻居,给出其优先权。特别是,Alice对于她的每个邻居都持续地测量接收到比特的速率,并确定以最高速率流入的4个邻居。每过10秒,她重新计算该速率并可能修改这4个对等方的集合。用BitTorrent术语来说,这4个对等方被称为疏通(unchoked)。重要的是,每过30秒,她也要随机地选择另外一个邻居并向其发送块。我们将这个被随机选择的对等方称为Bob。因为Alice正在向Bob发送数据,她可能成为Bob前4位上载者之一,这样的话Bob将开始向Alice发送数据。如果Bob向Alice发送数据的速率足够高,Bob接下来也能成为Alice的前4位上载者。换言之,每过30秒Alice将随机地选择一名新的对换伴侣并开始与那位伴侣进行对换。如果这两名对等方都满足此对换,它们将对方放入其前4位列表中并继续与对方进行对换,直到该对等方之一发现了一个更好的伴侣为止。这种效果是对等方能够以趋向于找到彼此的协调的速率上载。随机选择邻居也人允许新的对等方得到块,因此它们能够具有对换的东西。除了这5个对等方(前4个对等方和一个试探的对等方)的所有其他相邻对等方均被“阻塞”,即它们不能从Alice接收到任何块。BitTorrent有一些有趣的机制没有在这里讨论,包括片(小块)、流水线、随机优先选择、残局模型和反念慢[Cohen 2003]。

  刚刚描述的关于交换的激励机制常被称为“一报还一报”(tit-for-tat)[Cohen 2003]。已证实这种激励方案能被回避[Liogkas 2006;Locher 2006;Piatek 2008]。无论如何,BitTorrent“生态系统”取得了广泛成功,数以百万计的并发对等方在数十万条洪流中积极地共享文件。如果BitTorrent被设计为不采用一报还一报(或一种变种),然而在别的方面却完全相同的协议,BitTorrent现在将很可能不复存在了,因为大多数用户将成为搭便车者了[Sarouiu 2002]。

  我们简要地提一下另一种P2P应用——分布式散列表(DHT)来结束我们的讨论。分布式散列表是一种简单的数据库,其数据库记录分布在一个P2P系统的多个对等方上。DHT得到了广泛实现(如在BitTorrent中),并成为大量研究的主题.

以下内容来自分布式哈希表 (DHT) 和 P2P 技术

早期的一种P2P网络采取了不同的策略,它不设置中央服务器;当用户请求资源时,它会请求它所有的邻接节点,邻接节点再依次请求各自的邻接节点,并使用一些策略防止重复请求,直到找到拥有资源的节点.也就是说,这是一种泛洪搜索(Flooding Search).

这种P2P网络去除了中央服务器, 它的稳定性就强多了. 然而它太慢了. 一次查找可能会产生大量的请求, 可能会有大量的节点卷入其中. 一旦整个系统中的的节点过多, 性能就会变得很差.

为了解决这些问题, 分布式哈希表(即前文提到的分布式散列表)应运而生. 在一个有n个节点的分布式哈希表中, 每个节点仅需存储$O(lg⁡n)$个其他节点, 查找资源时仅需请求$O(lg⁡n)$个节点, 并且无需中央服务器, 是一个完全自组织的系统.

地址管理

首先, 在分布式哈希表中, 每个节点和资源都有一个唯一标识, 通常是一个160位整数. 为方便起见, 我们称节点的唯一标识为ID, 称资源的唯一标识为Key. 我们可以把一个节点的IP地址用SHA-1算法哈希得到这个节点的ID; 同样地, 把一个资源文件用SHA-1算法哈希就能得到这个资源的Key了.

定义好ID和Key之后, 就可以发布和存储资源了. 每个节点都会负责一段特定范围的Key, 其规则取决于具体的算法. 例如, 在Chord算法中, 每个Key总是被第一个ID大于或等于它的节点负责. 在发布资源的的时候, 先通过哈希算法计算出资源文件的Key, 然后联系负责这个Key的节点, 把资源存放在这个节点上. 当有人请求资源的时候, 就联系负责这个Key的节点, 把资源取回即可.

发布和请求资源有两种做法, 一种是直接把文件传输给负责的节点, 由它存储文件资源; 请求资源时再由这个节点将文件传输给请求者. 另一种做法是由发布者自己设法存储资源, 发布文件时把文件所在节点的地址传输给负责的节点, 负责的节点仅存储一个地址; 请求资源的时候会联系负责的节点获取资源文件的地址, 然后再取回资源. 这两种做法各有优劣. 前者的好处是资源的发布者不必在线, 请求者也能获取资源; 坏处是如果文件过大, 就会产生较大的传输和存储成本. 后者的好处是传输和存储成本都比较小, 但是资源的发布者, 或者说资源文件所在的节点必须一直在线.

路由算法

上面我们简述了地址系统,以及如何发布和取回资源.但是现在还有一个大问题:如何找到负责某个特定Key的节点呢? 这里就要用到路由算法了.不同的分布式哈希表实现有不同的路由算法,但它们的思路是一致的.

首先每个节点会路由若干个其他节点的联系方式(IP地址,端口), 称之为路由表. 一般来说一个有着n个节点的分布式哈希表中, 一个节点的路由表的长度为$O(lg⁡n)$.每个节点都会按照特定的规则构建路由表, 最终所有的节点会形成一张网络.从一个节点发出的消息会根据特定的路由规则,沿着网络逐步接近目标节点,最终达到目标节点.在有着n个节点的分布式哈希表中, 这个过程的转发次数通常为$O(lg⁡n)$次.

自我组织(self-organization)

分布式哈希表中的节点都是由各个用户组成,随时有用户加入,离开或失效;并且分布式哈希表没有中央服务器,也就是说着这个系统完全没有管理者.这意味着分配地址,构建路由表,节点加入,节点离开,排除失效节点等操作都要靠自我组织策略实现.

要发布或获取资源,首先要有节点加入.一个节点加入通常有以下几步.首先,一个新节点需要通过一些外部机制联系分布式哈希表中的任意一个已有节点;接着新节点通过请求这个已有节点构造出自己的路由表,并且更新其他需要与其建立连接的节点的路由表;最后这个节点还需要取回它所负责的资源.

此外我们必须认为节点的失效是一件经常发生的事,必须能够正确处理它们.例如,在路由的过程中遇到失效的节点,会有能够替代它的其他节点来完成路由操作;会定期地检查路由表中的节点是否有效;将资源重复存储在多个节点上以对抗节点失效等.另外分布式哈希表中的节点都是自愿加入的,也可以自愿离开.节点离开的处理与节点失效类似,不过还可以做一些更多的操作,比如说立即更新其他节点的路由表,将自己的资源转储到其他节点等.