写在前面

该篇博客是我在看《网络是怎样连接的》这本书时记录的学习笔记✍~~

第1章 浏览器生成消息——探索浏览器内部

image-20220415142511225

1.1 生成HTTP请求消息

image-20220415151726574

image-20220415152043521

image-20220415152051092

image-20220415153019176

image-20220415153028593

image-20220415153041574

image-20220415153616884

image-20220415153624530

image-20220415153630971

1.2 向 DNS 服务器查询 Web 服务器的 IP 地址

IP地址的基本知识

生成HTTP消息之后,接下来我们需要委托操作系统将消息发送给Web服务器。尽管浏览器能够解析网址并生成HTTP消息,但它本身并不具备将消息发送到网络中的功能,因此这一功能需要委托操作系统来实现。在进行这一操作时,我们还有一个工作需要完成,那就是查询网址中服务器域名对应的IP地址。在委托操作系统发送消息时,必须要提供的不是通信对象的域名,而是它的IP地址。因此,在生成HTTP消息之后,下一个步骤就是根据域名查询IP地址。在讲解这一操作之前,让我们先来简单了解一下IP地址。

互联网和公司内部的局域网都是基于TCP/IP的思路来设计的,所以我们先来了解TCP/IP的基本思路。TCP/IP的结构如图1.8所示,==就是由一些小的子网,通过路由器(一种对包进行转发的设备)连接起来组成一个大的网络。这里的子网可以理解为用集线器(一种对包进行转发的设备,分为中继式集线器和交换式集线器两种)连接起来的几台计算机,我们将它看作一个单位,称为子网。将子网通过路由器连接起来,就形成了一个网络。==

在网络中,所有的设备都会被分配一个地址。这个地址就相当于现实中某条路上的“××号××室”。其中“号”对应的号码是分配给整个子网的,而“室”对应的号码是分配给子网中的计算机的,这就是网络中的地址。“号”对应的号码称为网络号,“室”对应的号码称为主机号,这个地址的整体称为IP地址。通过IP地址我们可以判断出访问对象服务器的位置,从而将消息发送到服务器。发送者发出的消息首先经过子网中的集线器,转发到距离发送者最近的路由器上(图1.8①)。接下来,路由器会根据消息的目的地判断下一个路由器的位置,然后将消息发送到下一个路由器,即消息再次经过子网内的集线器被转发到下一个路由器(图1.8②)。前面的过程不断重复,最终消息就被传送到了目的地。

image-20220415145005882

前面这些就是 TCP/IP中IP地址的基本思路。了解了这些知识之后,让我们再来看一下实际的IP地址。如图1.9所示,实际的IP地址是一串32比特的数字,按照8比特(1字节)为一组分成4组,分别用十进制表示然后再用圆点隔开。这就是我们平常经常见到的地址格式,但仅凭这一串数字我们无法区分哪部分是网络号,哪部分是主机号。在IP地址的规则中,网络号和主机号连起来总共是32比特,但这两部分的具体结构是不固定的。在组建网络时,用户可以自行决定它们之间的分配关系,因此,我们还需要==另外的附加信息来表示IP地址的内部结构:子网掩码==。

image-20220415145542649

这一附加信息称为子网掩码。子网掩码的格式如图1.10②所示,是一串与IP地址长度相同的32比特数字,其左边一半都是1,右边一半都是0。其中,子网掩码为1的部分表示网络号,子网掩码为0的部分表示主机号。将子网掩码按照和IP地址一样的方式以每8比特为单位用圆点分组后写在IP地址的右侧,这就是图1.9(b)的方法。这种写法太长,我们也可以把1的部分的比特数用十进制表示并写在P地址的右侧,如图1.9(c)所示。这两种方式只是写法上的区别,含义是完全一样的。

image-20220415145721082

顺带―提,主机号部分的比特全部为О或者全部为1时代表两种特殊的含义。主机号部分全部为0代表整个子网而不是子网中的某台设备(图1.9(d))。此外,主机号部分全部为1代表向子网上所有设备发送包,即广播(图1.9(e))。

IP地址的主机号 全0:表示整个子网 全1:表示向子网上所有设备发送包,即“广播”

域名和IP地址并用的理由

互联网中存在无数的路由器,它们之间相互配合,根据 IP 地址来判断应该把数据传送到什么地方。那么如果我们不用 IP 地址而是改用名称会怎么样呢? IP 地址的长度为 32 比特,也就是 4 字节,相对地,域名最短也要几十个字节,最长甚至可以达到 255 字节。换句话说,使用 IP 地址只需要处理 4 字节的数字,而域名则需要处理几十个到 255 个字节的字符,这增加了路由器的负担,传送数据也会花费更长的时间 。

于是,现在我们使用的方案是让人来使用名称,让路由器来使用 IP 地 址。为了填补两者之间的障碍,需要有一个机制能够通过名称来查询 IP 地 址,或者通过 IP 地址来查询名称,这样就能够在人和机器双方都不做出牺牲的前提下完美地解决问题。这个机制就是 DNS

DNS:Domain Name System,域名服务系统。将服务器名称和 IP 地址进行关联是 DNS 最常见的用法,但 DNS 的功能并不仅限于此,它还可以将邮件地址和邮件服务器进行关联,以及为各种信息关联相应的名称。

查询 IP 地址的方法非常简单,只要询问最近的 DNS 服务器“www.lab.glasscom.com 的 IP 地址是什么”就可以了,DNS 服务器会回答说“该服务器的 IP 地址为 xxx.xxx.xxx.xxx”。

向 DNS 服务器发出查询,也就是向 DNS 服务器发送查询消息,并接收服务器返回的响应消息。换句话说,对于 DNS 服务器,我们的计算机上一定有相应的 DNS 客户端,而相当于 DNS 客户端的部分称为 DNS 解析器,或者简称解析器。通过 DNS 查询 IP 地址的操作称为域名解析,因此负责执行解析(resolution)这一操作的就叫解析器(resolver)了。

解析器的内部原理

解析器实际上是一段程序,它包含在操作系统的 Socket 库中。

Socket 库是用于调用网络功能的程序组件集合。

根据域名查询 IP 地址时,浏览器会使用 Socket 库中的解析器。

image-20220415150957462

一般来说,应用程序编写的操作内容是从上往下按顺序执行的,当到达需要调用解析器的部分时,对应的那一行程序就会被执行,应用程序本身的工作就会暂停(图 1.12 ①)。然后,Socket 库中的解析器开始运行(图 1.12 ②),完成应用程序委托的操作。像这样,由于调用了其他程序,原本运行的程序进入暂停状态,而被调用的程序开始运行,这就是“控制流程转移”。

当控制流程转移到解析器后,解析器会生成要发送给 DNS 服务器的查询消息。这个过程与浏览器生成要发送给 Web 服务器的 HTTP 请求消息的过程类似,解析器会根据 DNS 的规格,生成一条表示“请告诉我 www.lab.glasscom.com 的 IP 地址”B 的数据,并将它发送给 DNS 服务器(图 1.12 ③)。发送消息这个操作并不是由解析器自身来执行,而是要委托给操作系统内部的协议栈 A 来执行。这是因为和浏览器一样,解析器本身也不具备使用网络收发数据的功能。解析器调用协议栈后,控制流程会再次转移,==协议栈(操作系统内部的网络控制软件,也叫“协议驱动” “TCP/IP 驱动”等)会执行发送消息的操作,然后通过网卡将消息发送给 DNS 服务器(图 1.12 ④⑤)。==

DNS 服务器收到查询消息后,它会根据消息中的查询内容进行查询。

总之,如果要访问的 Web 服务器已经在 DNS 服务器上注册,那么这条记录就能够被找到,然后其 IP 地址会被写入响应消息并返回给客户端(图 1.12 ⑥)。接下来,消息经过网络到达客户端,再经过协议栈被传递给解析器(图 1.12 ⑦⑧),然后解析器读取出消息取出 IP 地址,并将 IP 地址传递给应用程序(图 1.12 ⑨)。实际上,解析器会将取出的 IP 地址写入应用程序指定的内存地址中,图 1.11 用“< 内存地址 >”来表示,在实际的程序代码中应该写的是代表这一内存地址的名称。

到这里,解析器的工作就完成了,控制流程重新回到应用程序(浏览器)。现在应用程序已经能够从内存中取出 IP 地址了,所以说 IP 地址是用这种方式传递给应用程序的。

计算机的内部结构就是这样一层一层的。也就是说,很多程序组成不同的层次,彼此之间分工协作。当接到上层委派的操作时,本层的程序并不会完成所有的工作,而是会完成一部分工作,再将剩下的部分委派到下层来完成。

1.3 全世界 DNS 服务器的大接力

DNS 服务器的基本工作

DNS 服务器的基本工作就是接收来自客户端的查询消息,然后根据消息的内容返回响应。

其中,来自客户端的查询消息包含以下 3 种信息。

(a)域名

服务器、邮件服务器(邮件地址中 @ 后面的部分)的名称

(b) Class

在最早设计 DNS 方案时,DNS 在互联网以外的其他网络中的应用也被考虑到了,而 Class 就是用来识别网络的信息。不过,如今除了互联网并没有其他的网络了,因此 Class 的值永远是代表互联网的 IN

(c)记录类型

表示域名对应何种类型的记录。例如,当类型为 A 时,表示域名对应的是 IP 地址;当类型为 MX 时,表示域名对应的是邮件服务器。对于不同的记录类型,服务器向客户端返回的信息也会不同

DNS 服务器上事先保存有前面这 3 种信息对应的记录数据,如图 1.14所示。DNS 服务器就是根据这些记录查找符合查询请求的内容并对客户端作出响应的。

image-20220415155212930

例如,如果要查询 www.lab.glasscom.com 这个域名对应的 IP 地址,客

户端会向 DNS 服务器发送包含以下信息的查询消息。

(a)域名 = www.lab.glasscom.com

(b) Class = IN

(c)记录类型 = A

然后,DNS 服务器会从已有的记录中查找域名、Class 和记录类型全部匹配的记录。假如 DNS 服务器中的记录如图 1.14 所示,那么第一行记录与查询消息中的 3 个项目完全一致。于是,DNS 服务器会将记录中的192.0.2.226 这个值返回给客户端。

在查询 IP 地址时我们使用 A==(A 是 Address 的缩写)== 这个记录类型,而查询邮件服务器时则要使用 MX 类型==(Mail eXchange,邮件交换)==。这是因为在 DNS 服务器上,IP 地址是保存在 A 记录中的,而邮件服务器则是保存在 MX 记录中的。例如,对于一个邮件地址tone@glasscom.com,当需要知道这个地址对应的邮件服务器时,我们需要提供 @ 后面的那一串名称。查询消息的内容如下。

(a)域名 = glasscom.com

(b) Class = IN

(c)记录类型 = MX

DNS 服务器会返回 10 和 mail.glasscom.com 这两条信息。当记录类型为 MX 时,DNS 服务器会在记录中保存两种信息,分别是邮件服务器的域名和优先级 A。此外,MX 记录的返回消息还包括邮件服务器 mail.glasscom.com 的 IP 地址。上表的第三行就是 mail.glasscom.com 的 IP 地址,因此只要用 mail.glasscom.com 的域名就可以找到这条记录。在这个例子中,我们得到的 IP 地址是 192.0.2.227。

综上所述,DNS 服务器的基本工作就是根据需要查询的域名和记录类型查找相关的记录,并向客户端返回响应消息

域名的层次结构

首先,==DNS服务器中的所有信息都是按照域名以分层次的结构来保存的。==层次结构这个词听起来可能有点不容易懂,其实就类似于公司中的事业集团、部门、科室这样的结构。层次结构能够帮助我们更好地管理大量的信息。

DNS中的域名都是用句点来分隔的,比如 www.lab.glasscom.com,这里的句点代表了不同层次之间的界限,就相当于公司里面的组织结构不用部、科之类的名称来划分,只是用句点来分隔而已。在域名中,==越靠右的位置表示其层级越高==,比如 www.lab.glasscom.com这个域名如果按照公司里的组织结构来说,大概就是“com事业集团glasscom部 lab科的www"这样。其中,相当于一个层级的部分称为域。因此,com域的下一层是glasscom域,再下一层是 lab域,再下面才是www这个名字。

寻找相应的 DNS 服务器并获取 IP 地址

首先,将负责管理下级域的 DNS 服务器的 IP 地址注册到它们的上级 DNS 服务器中,然后上级 DNS 服务器的 IP 地址再注册到更上一级的 DNS 服务器中,以此类推。也就是说,负责管理 lab.glasscom.com 这个域的 DNS 服务器的 IP 地址需要注册到 glasscom.com 域的 DNS服务器中,而 glasscom.com 域的 DNS 服务器的 IP 地址又需要注册到 com域的 DNS 服务器中。这样,我们就可以通过上级 DNS 服务器查询出下级DNS 服务器的 IP 地址,也就可以向下级 DNS 服务器发送查询请求了。

除此之外还需要完成另一项工作,那就是将根域的 DNS 服务器信息保存在互联网中所有的 DNS 服务器中。这样一来,任何 DNS 服务器就都可以找到并访问根域 DNS 服务器了。因此,客户端只要能够找到任意一台DNS 服务器,就可以通过它找到根域 DNS 服务器,然后再一路顺藤摸瓜找到位于下层的某台目标 DNS 服务器(图 1.15)。分配给根域 DNS 服务器的 IP 地址在全世界仅有 13 个 ,而且这些地址几乎不发生变化,因此将这些地址保存在所有的 DNS 服务器中也并不是一件难事。

image-20220415161719598

如图 1.16 所示,客户端首先会访问最近的一台 DNS 服务器(也就是客户端的 TCP/IP 设置中填写的 DNS 服务器地址),假设我们要查询 www.lab.glasscom.com 这台 Web 服务器的相关信息(图 1.16 ①)。由于最近的 DNS 服务器中没有存放 www.lab.glasscom.com 这一域名对应的信息,所以我们需要从顶层开始向下查找。最近的 DNS 服务器中保存了根域 DNS 服务器的信息,因此它会将来自客户端的查询消息转发给根域 DNS 服务器(图 1.16 ②)。根域服务器中也没有 www.lab.glasscom.com 这个域名,但根据域名结构可以判断这个域名属于 com 域,因此根域 DNS 服务器会返回它所管理的 com 域中的DNS 服务器的 IP 地址,意思是“虽然我不知道你要查的那个域名的地址,但你可以去 com 域问问看”。接下来,最近的 DNS 服务器又会向 com 域的DNS 服务器发送查询消息(图 1.16 ③)。com 域中也没有 www.lab.glasscom.com这个域名的信息,和刚才一样,com 域服务器会返回它下面的 glasscom.com域的 DNS 服务器的 IP 地址。以此类推,只要重复前面的步骤,就可以顺藤摸瓜找到目标 DNS 服务器(图 1.16 ⑤),只要向目标 DNS 服务器发送查询消息,就能够得到我们需要的答案,也就是 www.lab.glasscom.com 的 IP 地址了。

收到客户端的查询消息之后,DNS 服务器会按照前面的方法来查询 IP地址,并返回给客户端(图 1.16 ⑥)。这样,客户端就知道了 Web 服务器的 IP 地址,也就能够对其进行访问了(图 1.16 ⑦)。

image-20220415161821979

1.4 委托协议栈发送消息

数据收发操作概览

要发送给 Web 服务器的 HTTP 消息是一种数字信息(digital data),因此也可以说是委托协议栈来发送数字信息。

向操作系统内部的协议栈发出委托时,需要按照指定的顺序来调用 Socket 库中的程序组件。

使用 Socket 库来收发数据的操作过程如图 1.17 所示 。简单来说,收发数据的两台计算机之间连接了一条数据通道,数据沿着这条通道流动,最终到达目的地。我们可以把数据通道想象成一条管道,将数据从一端送入管道,数据就会到达管道的另一端然后被取出。数据可以从任何一端被送入管道,数据的流动是双向的。不过,这并不是说现实中真的有这么一条管道,只是为了帮助大家理解数据收发操作的全貌。

image-20220415170733810

收发数据的整体思路就是这样,但还有一点也非常重要。光从图上来看,这条管道好像一开始就有,实际上并不是这样,在进行收发数据操作之前,双方需要先建立起这条管道才行。建立管道的关键在于管道两端的数据出入口,这些出入口称为套接字。我们需要先创建套接字,然后再将套接字连接起来形成管道。实际的过程是下面这样的。首先,服务器一方先创建套接字,然后等待客户端向该套接字连接管道 。当服务器进入等待状态时,客户端就可以连接管道了。具体来说,客户端也会先创建一个套接字,然后从该套接字延伸出管道,最后管道连接到服务器端的套接字上。当双方的套接字连接起来之后,通信准备就完成了。接下来,就像我们刚刚讲过的一样,只要将数据送入套接字就可以收发数据了。

我们再来看一看收发数据操作结束时的情形。当数据全部发送完毕之后,连接的管道将会被断开。==管道在连接时是由客户端发起的==,但在断开时可以由客户端或服务器任意一方发起(Web 使用的 HTTP 协议规定,当 Web 服务器发送完响应消息之后,应该主动执行断开操作 ,因此 Web 服务器会首先调用close 来断开连接) 。其中一方断开后,另一方也会随之断开,当管道断开后,套接字也会被删除。到此为止,通信操作就结束了。

综上所述,收发数据的操作分为若干个阶段,可以大致总结为以下 4 个。

(1)创建套接字(创建套接字阶段)

(2)将管道连接到服务器端的套接字上(连接阶段)

(3)收发数据(通信阶段)

(4)断开管道并删除套接字(断开阶段)

在每个阶段,Socket 库中的程序组件都会被调用来执行相关的数据收发操作。不过,在探索其具体过程之前,我们来补充一点内容。==前面这 4个操作都是由操作系统中的协议栈来执行的==,浏览器等应用程序并不会自己去做连接管道、放入数据这些工作,而是==委托协议栈来代劳==。本章将要介绍的只是这个==“委托”==的操作。关于协议栈收到委托之后具体是如何连接管道和放入数据的,我们将在第 2 章介绍。此外,==这些委托的操作都是通过调用 Socket 库中的程序组件来执行的==,但这些数据通信用的程序组件其实仅仅充当了一个桥梁的角色,并不执行任何实质性的操作,==应用程序的委托内容最终会被原原本本地传递给协议栈==。因此,我们无法形象地展示这些程序组件到底完成了怎样的工作,与其勉强强调 Socket 库的存在,还不如将 Socket 库和协议栈看成一个整体并讲解它们的整体行为让人

image-20220415171123952

描述符:应用程序用来识别套接字的机制 IP 地址和端口号:客户端和服务器之间用来识别对方套接字的机制

下面两个网址有什么不同?

a. http://www.nikkeibp.co.jp/sample

b. http://www.nikkeibp.co.jp/sample/

==a 中的 sample 代表文件名,b 中的 sample 代表目录名==

image-20220415171453588

image-20220415171503571

第2章 用电信号传输TCP/IP数据——探索协议栈和网卡

image-20220415171807046

2.1 创建套接字

协议栈的内部结构

image-20220415172651770

图中最上面的部分是网络应用程序,也就是浏览器、电子邮件客户端、Web 服务器、电子邮件服务器等程序,它

们会将收发数据等工作委派给下层的部分来完成。

应用程序的下面是 Socket 库,其中包括解析器,解析器用来向 DNS服务器发出查询

再下面就是操作系统内部了,其中包括协议栈。协议栈的上半部分有两块,分别是负责用 TCP 协议收发数据的部分和负责用 UDP 协议收发数据的部分,它们会接受应用程序的委托执行收发数据的操作。==像浏览器、邮件等一般的应用程序都是使用 TCP 收发数据的,而像 DNS 查询等收发较短的控制数据的时候则使用 UDP。==

下面一半是用 IP 协议控制网络包收发操作的部分。在互联网上传送数据时,数据会被切分成一个一个的网络包 A,而将网络包发送给通信对象的操作就是由 IP 来负责的。此外,IP 中还包括 ICMP 协议和 ARP 协议。ICMP 用于告知网络包传送过程中产生的错误以及各种控制消息,ARP 用于根据 IP 地址查询相应的以太网 MAC 地址 。

IP 下面的网卡驱动程序负责控制网卡硬件,而最下面的网卡则负责完成实际的收发操作,也就是对网线中的信号执行发送和接收的操作。

套接字的实体就是通信控制信息

套接字中记录了用于控制通信操作的各种控制信息,协议栈则需要根据这些信息判断下一步的行动,这就是套接字的作用。

协议栈是根据套接字中记录的控制信息来工作的。

下面来看看真正的套接字。在 Windows 中可以用 netstat 命令显示套接字内容(图 2.2)。图中每一行相当于一个套接字,当创建套接字时,就会在这里增加一行新的控制信息,赋予“即将开始通信”的状态,并进行通信的准备工作,如分配用于临时存放收发数据的缓冲区空间。既然有图,我们就来讲讲图上这些到底都是什么意思。比如第 8 行,它表示 PIDB 为 4 的程序正在使用 IP 地址为 10.10.1.16 的网卡与 IP 地址为10.10.1.18 的对象进行通信。此外我们还可以看出,本机使用 1031 端口,对方使用 139 端口,而 139 端口是 Windows 文件服务器使用的端口,因此我们就能够看出这个套接字是连接到一台文件服务器的。我们再来看第 1 行,这一行表示 PID 为 984 的程序正在 135 端口等待另一方的连接,其中本地 IP 地址和远程 IP 地址都是 0.0.0.0,这表示通信还没开始,IP 地址不确定 。

image-20220415185548520

image-20220415195604025

2.2 连接服务器

负责保存控制信息的头部

控制信息可以大体上分为两类。

第一类是客户端和服务器相互联络时交换的控制信息。这些信息不仅连接时需要,包括数据收发和断开连接操作在内,整个通信过程中都需要,这些内容在 TCP 协议的规格中进行了定义。具体来说,表 2.1 中的这些字段就是 TCP 规格中定义的控制信息 。这些字段是固定的,在连接、收发、断开等各个阶段中,每次客户端和服务器之间进行通信时,都需要提供这些控制信息。具体来说,如图 2.4(a)所示,这些信息会被添加在客户端与服务器之间传递的网络包的开头。在连接阶段,由于数据收发还没有开始,所以如图 2.4(b)所示,网络包中没有实际的数据,只有控制信息。这些控制信息位于网络包的开头,因此被称为头部。此外,以太网和 IP 协议也有自己的控制信息,这些信息也叫头部,为了避免各种不同的头部发生混淆,我们一般会记作 TCP 头部、以太网头部 、IP 头部。

image-20220415192231993

image-20220415192250955

控制信息还有另外一类,那就是保存在套接字中,用来控制协议栈操作的信息 。应用程序传递来的信息以及从通信对象接收到的信息都会保存在这里,还有收发数据操作的执行状态等信息也会保存在这里,协议栈会根据这些信息来执行每一步的操作。

通信操作中使用的控制信息分为两类。 (1)头部中记录的信息 (2)套接字(协议栈中的内存空间)中记录的信息

连接操作的实际过程

连接操作的第一步是在 TCP 模块处创建表示连接控制信息的头部。

通过 TCP 头部中的发送方和接收方端口号可以找到要连接的套接字。

2.3 收发数据

将 HTTP 请求消息交给协议栈

当控制流程从 connect 回到应用程序之后,接下来就进入数据收发阶段了。数据收发操作是从应用程序调用 write 将要发送的数据交给协议栈开始的

如果一收到数据就马上发送出去,就可能会发送大量的小包,导致网络效率下降,因此需要在数据积累到一定量时再发送出去。至于要积累多少数据才能发送,不同种类和版本的操作系统会有所不同,不能一概而论,但都是根据下面几个要素来判断的。

==第一个判断要素是每个网络包能容纳的数据长度==,协议栈会根据一个叫作 MTU 的参数来进行判断。MTU 表示一个网络包的最大长度。

==另一个判断要素是时间==,当应用程序发送数据的频率不高的时候,如果每次都等到长度接近 MSS 时再发送,可能会因为等待时间太长而造成发送延迟,这种情况下,即便缓冲区中的数据长度没有达到 MSS,也应该果断发送出去。为此,协议栈的内部有一个计时器,当经过一定时间之后,就会把网络包发送出去。

使用 ACK 号确认网络包已收到

到这里,网络包已经装好数据并发往服务器了,但数据发送操作还没有结束。TCP 具备确认对方是否成功收到网络包,以及当对方没收到时进行重发的功能,因此在发送网络包之后,接下来还需要进行确认操作。

image-20220415200106691

image-20220415200046492

==通过“序号”和“ACK 号”可以确认接收方是否收到了网络包。==

使用窗口有效管理 ACK 号

如图 2.10(a)所示,每发送一个包就等待一个 ACK 号的方式是最简单也最容易理解的,但在等待 ACK 号的这段时间中,如果什么都不做那实在太浪费了。为了减少这样的浪费,TCP 采用图 2.10(b)这样的滑动窗口方式来管理数据发送和 ACK 号的操作。所谓滑动窗口,就是在发送一个包之后,不等待 ACK 号返回,而是直接发送后续的一系列包。这样一来,等待 ACK 号的这段时间就被有效利用起来了。

image-20220415200806598

==接收方需要告诉发送方自己最多能接收多少数据,然后发送方根据这个值对数据发送操作进行控制,这就是滑动窗口方式的基本思路。==

关于滑动窗口的具体工作方式,还是看图更容易理解(图 2.11)。在这张图中,接收方将数据暂存到接收缓冲区中并执行接收操作。当接收操作完成后,接收缓冲区中的空间会被释放出来,也就可以接收更多的数据了,这时接收方会通过 TCP 头部中的窗口字段将自己能接收的数据量告知发送方。这样一来,发送方就不会发送过多的数据,导致超出接收方的处理能力了

image-20220415200901066

ACK 与窗口的合并

要提高收发数据的效率,还需要考虑另一个问题,那就是返回 ACK号和更新窗口的时机。

==当接收方将数据传递给应用程序,导致接收缓冲区剩余容量增加时,就需要告知发送方,这就是更新窗口大小的时机。==

==当接收方收到数据时,如果确认内容没有问题,就应该向发送方返回 ACK 号==,因此我们可以认为收到数据之后马上就应该进行这一操作。

如果将前面两个因素结合起来看,首先,发送方的数据到达接收方,在接收操作完成之后就需要向发送方返回 ACK 号,而再经过一段时间 A,当数据传递给应用程序之后才需要更新窗口大小。但如果根据这样的设计来实现,每收到一个包,就需要向发送方分别发送 ACK 号和窗口更新这两个单独的包 B。这样一来,接收方发给发送方的包就太多了,导致网络效率下降。

因此,==接收方在发送 ACK 号和窗口更新时,并不会马上把包发送出去,而是会等待一段时间==,在这个过程中很有可能会出现其他的通知操作,==这样就可以把两种通知合并在一个包里面发送了==。举个例子,在等待发送ACK 号的时候正好需要更新窗口,这时就可以把 ACK 号和窗口更新放在一个包里发送,从而减少包的数量。当需要连续发送多个 ACK 号时,也可以减少包的数量,这是因为 ACK 号表示的是已收到的数据量,也就是说,它是告诉发送方目前已接收的数据的最后位置在哪里,因此当需要连续发送 ACK 号时,只要发送最后一个 ACK 号就可以了,中间的可以全部省略。当需要连续发送多个窗口更新时也可以减少包的数量,因为连续发生窗口更新说明应用程序连续请求了数据,接收缓冲区的剩余空间连续增加。这种情况和 ACK 号一样,可以省略中间过程,只要发送最终的结果就可以了。

接收 HTTP 响应消息

首先,协议栈会检查收到的数据块和 TCP 头部的内容,判断是否有数据丢失,如果没有问题则返回 ACK 号。然后,协议栈将数据块暂存到接收缓冲区中,并将数据块按顺序连接起来还原出原始的数据,最后将数据交给应用程序。具体来说,协议栈会将接收到的数据复制到应用程序指定的内存地址中,然后将控制流程交回应用程序。将数据交给应用程序之后,协议栈还需要找到合适的时机向发送方发送窗口更新 。

2.4 从服务器断开并删除套接字

无论哪种情况,完成数据发送的一方会发起断开过程,这里我们以服务器一方发起断开过程为例来进行讲解。首先,服务器一方的应用程序会调用 Socket 库的 close 程序。然后,服务器的协议栈会生成包含断开信息的 TCP 头部,具体来说就是将控制位中的 FIN 比特设为 1。接下来,协议栈会委托 IP 模块向客户端发送数据(图 2.12 ①)。同时,服务器的套接字中也会记录下断开操作的相关信息。

image-20220415201936542

接下来轮到客户端了。当收到服务器发来的 FIN 为 1 的 TCP 头部时,客户端的协议栈会将自己的套接字标记为进入断开操作状态。然后,为了告知服务器已收到 FIN 为 1 的包,客户端会向服务器返回一个 ACK 号 (图 2.12 ②)。这些操作完成后,协议栈就可以等待应用程序来取数据了。

过了一会儿,应用程序就会调用 read 来读取数据 。这时,协议栈不会向应用程序传递数据 ,而是会告知应用程序(浏览器)来自服务器的数据已经全部收到了。根据规则,服务器返回请求之后,Web 通信操作就全部结束了,因此只要收到服务器返回的所有数据,客户端的操作也就随之结束了。因此,客户端应用程序会调用 close 来结束数据收发操作,这时客户端的协议栈也会和服务器一样,生成一个 FIN 比特为 1 的 TCP 包,然后委托 IP 模块发送给服务器(图 2.12 ③)。一段时间之后,服务器就会返回ACK 号(图 2.12 ④)。到这里,客户端和服务器的通信就全部结束了。

和服务器的通信结束之后,==用来通信的套接字也就不会再使用了,这时我们就可以删除这个套接字了==。不过,==套接字并不会立即被删除,而是会等待一段时间之后再被删除。等待这段时间是为了防止误操作。==

数据收发操作小结

image-20220415202152634

数据收发操作的第一步是创建套接字。一般来说,服务器一方的应用程序在启动时就会创建好套接字并进入等待连接的状态。客户端则一般是在用户触发特定动作,需要访问服务器的时候创建套接字。在这个阶段,还没有开始传输网络包。

创建套接字之后,客户端会向服务器发起连接操作。首先,客户端会生成一个 SYN 为 1 的 TCP 包并发送给服务器(图 2.13 ①)。这个 TCP 包的头部还包含了客户端向服务器发送数据时使用的初始序号,以及服务器向客户端发送数据时需要用到的窗口大小 。当这个包到达服务器之后,服务器会返回一个 SYN 为 1 的 TCP 包(图 2.13 ②)。和图 2.13 ①一样,这个包的头部中也包含了序号和窗口大小,此外还包含表示确认已收到包①的ACK 号 。当这个包到达客户端时,客户端会向服务器返回一个包含表示确认的 ACK 号的 TCP 包(图 2.13 ③)。到这里,连接操作就完成了,双方进入数据收发阶段。

数据收发阶段的操作根据应用程序的不同而有一些差异,以 Web 为例,首先客户端会向服务器发送请求消息。TCP 会将请求消息切分成一定大小的块,并在每一块前面加上 TCP 头部,然后发送给服务器(图 2.13 ④)。TCP 头部中包含序号,它表示当前发送的是第几个字节的数据。当服务器收到数据时,会向客户端返回 ACK 号(图 2.13 ⑤)。在最初的阶段,服务器只是不断接收数据,随着数据收发的进行,数据不断传递给应用程序,接收缓冲区就会被逐步释放。这时,服务器需要将新的窗口大小告知客户端。当服务器收到客户端的请求消息后,会向客户端返回响应消息,这个过程和刚才的过程正好相反(图 2.13 ⑥⑦)

服务器的响应消息发送完毕之后,数据收发操作就结束了,这时就会开始执行断开操作。以 Web 为例,服务器会先发起断开过程 。在这个过程中,服务器先发送一个 FIN 为 1 的 TCP 包(图 2.13 ⑧),然后客户端返回一个表示确认收到的 ACK 号(图 2.13 ⑨)。接下来,双方还会交换一组方向相反的 FIN 为 1 的 TCP 包(图 2.13 ⑩)和包含 ACK 号的 TCP 包(图 2.13k)。最后,在等待一段时间后,套接字会被删除。

2.5 IP与以太网的包收发操作

包的基本知识

包是由头部和数据两部分构成的(图 2.14(a))。头部包含目的地址等控制信息,大家可以把它理解为快递包裹的面单;头部后面就是委托方要发送给对方的数据,也就相当于快递包裹里的货物。一个包发往目的地的过程如图 2.15 所示。

image-20220415203121182

TCP/IP 包的结构是在这个基本结构的基础上扩展出来的,因此更加复杂。在第 1 章讲过子网的概念,还讲过网络中有路由器和集线器两种不同的转发设备,它们在传输网络包时有着各自的分工。

(1)路由器根据目标地址判断下一个路由器的位置

(2)集线器在子网中将网络包传输到下一个路由

实际上,集线器是按照以太网规则传输包的设备,而路由器是按照 IP规则传输包的设备,因此我们也可以作如下理解。

==(1)IP 协议根据目标地址判断下一个 IP 转发设备的位置==

==(2)子网中的以太网协议将包传输到下一个转发设备==

具体来说,如图 2.14(b)所示,TCP/IP 包包含如下两个头部。

(a)MAC 头部(用于以太网协议)

(b)IP 头部(用于 IP 协议)

这两个头部分别具有不同的作用。首先,发送方将包的目的地,也就是要访问的服务器的 IP 地址写入 IP 头部中。这样一来,我们就知道这个包应该发往哪里,==IP 协议就可以根据这一地址查找包的传输方向,从而找到下一个路由器的位置,也就是图 2.16 中的路由器 R1==。接下来,IP 协议会委托以太网协议将包传输过去。这时,IP 协议会查找下一个路由器的以太网地址(MAC 地址),并将这个地址写入 MAC 头部中。这样一来,以太网协议就知道要将这个包发到哪一个路由器上了。

网络包在传输过程中(图 2.16 ①)会经过集线器,集线器是根据以太网协议工作的设备。为了判断包接下来应该向什么地方传输,集线器里有一张表(用于以太网协议的表),可根据以太网头部中记录的目的地信息查出相应的传输方向。这张图中只有一个集线器,当存在多个集线器时,网络包会按顺序逐一通过这些集线器进行传输。

接下来,包会到达下一个路由器(图 2.16 ②)。==路由器中有一张 IP 协议的表,可根据这张表以及 IP 头部中记录的目的地信息查出接下来应该发往哪个路由器==。为了将包发到下一个路由器,我们还需要==查出下一个路由器的 MAC 地址,并记录到 MAC 头部中==,大家可以理解为改写了 MAC 头部 。这样,网络包就又被发往下一个节点了。

再往后的过程图上就没有画出来了。网络包会通过路由器到达下一个路由器 R2。这个过程不断重复,最终网络包就会被送到目的地,当目的地设备成功接收之后,网络包的传输过程就结束了。

image-20220415204443064

包收发操作概览

image-20220415210401598

包收发操作的起点是 TCP 模块委托 IP 模块发送包的操作(图 2.17 中 的“①发送”)。这个委托的过程就是 TCP 模块在数据块的前面加上 TCP头部,然后整个传递给 IP 模块,这部分就是网络包的内容。与此同时,TCP 模块还需要指定通信对象的 IP 地址,也就是需要写清楚“将什么内容发给谁”。

收到委托后,IP 模块会将包的内容当作一整块数据,在前面加上包含控制信息的头部。刚才我们讲过,IP 模块会添加 IP 头部和 MAC 头部这两种头部。==IP 头部中包含 IP 协议规定的、根据 IP 地址将包发往目的地所需的控制信息;MAC 头部包含通过以太网的局域网将包传输至最近的路由器所需的控制信息==。关于 IP 头部和 MAC 头部的区别以及其中包含的控制信息的含义,我们将稍后介绍。总之,加上这两个头部之后,一个包就封装好了,这些就是 IP 模块负责的工作。

接下来,封装好的包会被交给网络硬件(图 2.17 中的“②发送”),例如以太网、无线局域网等。网络硬件可能是插在计算机主板上的板卡,也可能是笔记本电脑上的 PCMCIA 卡,或者是计算机主板上集成的芯片,不同形态的硬件名字也不一样,本书将它们统称为网卡 。传递给网卡的网络包是由一连串 0 和 1 组成的数字信息,网卡会将这些数字信息转换为电信号或光信号,并通过网线(或光纤)发送出去,然后这些信号就会到达集线器、路由器等转发设备,再由转发设备一步一步地送达接收方。

包送达对方之后,对方会作出响应。返回的包也会通过转发设备发送回来,然后我们需要接收这个包。接收的过程和发送的过程是相反的,信息先以电信号的形式从网线传输进来,然后由网卡将其转换为数字信息并传递给 IP 模块(图 2.17 中的“③接收”)。接下来,IP 模块会将 MAC 头部和 IP 头部后面的内容,也就是 TCP 头部加上数据块,传递给 TCP 模块。接下来的操作就是我们之前讲过的 TCP 模块负责的部分了

把集成在主板上的网络硬件叫作“网卡”可能听上去有些奇怪,从这个意义上来看应该叫作“网络接口”比较准确。不过,也有接在 USB 接口上的网卡,在计算机的领域中,“接口”这个词有时候会带来更多的歧义。在计算机和网络行业中,有很多术语的用法其实都比较混乱。

==IP 的包收发操作都是相同的,并不会因包本身而有所区别。====因为 IP 模块会将 TCP 头部和数据块看作一整块二进制数据==,在执行收发操作时并不关心其中的内容,也不关心这个包是包含 TCP 头部和数据两者都有呢,还是只有 TCP 头部而没有数据。

生成包含接收方 IP 地址的 IP 头部

IP 头部中还需要填写发送方的 IP 地址,大家可以认为是发送方计算机的 IP 地址 ,实际上“计算机的 IP 地址”这种说法并不准确。一般的客户端计算机上只有一块网卡,因此也就只有一个 IP 地址,这种情况下我们可以认为这个 IP 地址就是计算机的 IP 地址,但如果计算机上有多个网卡,情况就没那么简单了。==IP 地址实际上并不是分配给计算机的,而是分配给网卡的==,因此当计算机上存在多块网卡时,每一块网卡都会有自己的 IP 地 址。很多服务器上都会安装多块网卡,这时一台计算机就有多个 IP 地址,在填写发送方 IP 地址时就需要判断到底应该填写哪个地址。这个判断相当于在多块网卡中判断应该使用哪一块网卡来发送这个包,也就相当于判断应该把包发往哪个路由器,因此只要确定了目标路由器,也就确定了应该使用哪块网卡,也就确定了发送方的 IP 地址。

IP 头部的“接收方 IP 地址”填写通信对象的 IP 地址。 发送方 IP 地址需要判断发送所使用的网卡,并填写该网卡的 IP地址。

image-20220416100809645

如何判断应该把包交给哪块网卡呢?其实和图 2.16 中路由器使用 IP 表判断下一个路由器位置的操作是一样的。因为协议栈的 IP模块与路由器中负责包收发的部分都是根据 IP 协议规则来进行包收发操作的,所以它们也都用相同的方法来判断把包发送给谁。

这个“IP 表”叫作==路由表== ,我们将在第 3 章探索路由器时详细介绍它的用法,这里先简单讲个大概。如图 2.18 所示,我们可以通过 route print命令来显示路由表,下面来边看边讲。首先,我们对套接字中记录的目的地 IP 地址与路由表左侧的 Network Destination 栏进行比较,找到对应的一行。例如,TCP 模块告知的目标 IP 地址为 192.168.1.21,那么就对应图 2.18 中的第 6 行,因为它和 192.168.1 的部分相匹配。如果目标 IP 地址为 10.10.1.166,那么就和 10.10.1 的部分相匹配,所以对应第 3 行。以此类推,我们需要找到与 IP 地址左边部分相匹配的条目 ,找到相应的条目之后,接下来看从右边数第 2 列和第 3 列的内容。右起第 2 列,也就是Interface 列,表示网卡等网络接口,这些网络接口可以将包发送给通信对象。此外,右起第 3 列,即 Gateway 列表示下一个路由器的 IP 地址,将包发给这个 IP 地址,该地址对应的路由器 ==(Gateway(网关)在 TCP/IP 的世界里就是路由器的意思)==就会将包转发到目标地址==(如果 Gateway 和 Interface 列的 IP 地址相同,就表示不需要路由器进行转发,可以直接将包发给接收方的 IP 地址)==。路由表的第 1 行中,目标地址和子网掩码 A都是 0.0.0.0,这表示默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行 。

这样一来,我们就可以判断出应该使用哪块网卡来发送包了,然后就可以在 IP 头部的发送方 IP 地址中填上这块网卡对应的 IP 地址。

接下来还需要填写协议号,它表示包的内容是来自哪个模块的。例如,如果是 TCP 模块委托的内容,则设置为 06(十六进制),如果是 UDP 模块委托的内容,则设置为 17(十六进制),这些值都是按照规则来设置的。在现在我们使用的浏览器中,HTTP 请求消息都是通过 TCP 来传输的,因此这里就会填写表示 TCP 的 06(十六进制)

生成以太网用的 MAC 头部

生成了 IP 头部之后,接下来 IP 模块还需要在 IP 头部的前面加上MAC(Media Access Control) 头部(表 2.3)。IP 头部中的接收方 IP 地址表示网络包的目的地,通过这个地址我们就可以判断要将包发到哪里,但在以太网的世界中,TCP/IP 的这个思路是行不通的。以太网在判断网络包目的地时和 TCP/IP 的方式不同,因此必须采用相匹配的方式才能在以太网中将包发往目的地,而MAC 头部就是干这个用的。

image-20220416100906781

在生成 MAC 头部时,只要设置表 2.3 中的 3 个字段就可以了。方便起见,我们按照从下往上的顺序来对表进行讲解。首先是“以太类型”,这里填写表示 IP 协议的值 0800(十六进制)。接下来是发送方 MAC 地址,这里填写网卡本身的 MAC 地址。MAC 地址是在网卡生产时写入 ROM 里的,只要将这个值读取出来写入 MAC 头部就可以了。

而接收方 MAC 地址就有点复杂了。在这个时间点上,我们还没有把包发送出去,所以先得搞清楚应该把包发给谁,这个只要查一下路由表就知道了。在路由表中找到相匹配的条目,然后把包发给 Gateway 列中的IP 地址就可以了。既然已经知道了包应该发给谁,那么只要将对方的 MAC 地址填上去就好了,但到这里为止根本没有出现对方的 MAC 地址,也就是说我们现在根本不知道对方的 MAC 地址是什么。因此,我们还需要执行根据 IP 地址查询 MAC 地址的操作。

IP 模块根据路由表 Gateway 栏的内容判断应该把包发送给谁。

通过 ARP (ARP:Address Resolution Protocol,地址解析协议)查询目标路由器的 MAC 地址

在以太网中,有一种叫作广播的方法,可以把包发给连接在同一以太网中的所有设备。ARP 就是利用广播对所有设备提问。

image-20220416101228202

如果对方和自己处于同一个子网中,那么通过上面的操作就可以得到对方的 MAC 地址 。然后,我们将这个 MAC 地址写入 MAC 头部,MAC头部就完成了。不过,如果每次发送包都要这样查询一次,网络中就会增加很多 ARP包,因此我们会将查询结果放到一块叫作 ARP 缓存的内存空间中留着以后用。

image-20220416101302670

以太网的基本知识

==以太网是一种为多台计算机能够彼此自由和廉价地相互通信而设计的通信技术。==它的原型如图 2.22(a)所示。从图上不难看出,这种网络的本质其实就是一根网线。图上还有一种叫作收发器的小设备,它的功能只是将不同网线之间的信号连接起来而已。因此,当一台计算机发送信号时,信号就会通过网线流过整个网络,最终到达所有的设备。

这个原型后来变成了图 2.22(b)中的结构。这个结构是将主干网线替换成了一个中继式集线器 ==(集线器)==,将收发器网线替换成了双绞线 。不过,虽然网络的结构有所变化,但信号会发送给所有设备这一基本性质并没有改变。

后来,图 2.22(c)这样的使用交换式集线器 ==(交换机)== 的结构普及开来,现在我们说的以太网指的都是这样的结构。这个结构看上去和(b)很像,但其实里面有一个重要的变化,即信号会发送给所有设备这一性质变了,现在信号只会流到根据 MAC 地址指定的设备,而不会到达其他设备了。当然,根据MAC 地址来传输包这一点并没有变,因此 MAC 头部的设计也得以保留。

image-20220416101900763

尽管以太网经历了数次变迁,但其基本的 3 个性质至今仍未改变,==即将包发送到 MAC 头部的接收方 MAC 地址代表的目的地,用发送方 MAC地址识别发送方,用以太类型识别包的内容==。因此,大家可以认为具备这3 个性质的网络就是以太网 。

将 IP 包转换成电或光信号发送出去

IP 生成的网络包只是存放在内存中的一串数字信息,没有办法直接发送给对方。因此,我们需要将数字信息转换为电或光信号,才能在网线上传输,也就是说,这才是真正的数据发送过程。负责执行这一操作的是网卡。

image-20220416102450648

有人认为在网卡通电之后,ROM 中的 MAC 地址就自动生效了,其实不然,真正生效的是网卡驱动进行初始化时在 MAC模块中设置的那个 MAC 地址 。在操作系统启动并完成初始化操作之后,网卡就可以等待来自 IP 的委托了。

网卡的 ROM 中保存着全世界唯一的 MAC 地址,这是在生产网卡时写入的。

网卡中保存的 MAC 地址会由网卡驱动程序读取并分配给 MAC模块。

网卡驱动从 IP 模块获取包之后,会将其复制到网卡内的缓冲区中,然后向MAC 模块发送发送包的命令。接下来就轮到 MAC 模块进行工作了。首先,MAC 模块会将包从缓冲区中取出,并在开头加上报头==(不能一开始就发送包的数据,而是要在前面加上一段用来测量时钟信号的特殊信号,这就是报头的作用)==和起始帧分界符==(起始帧分界符是一个用来表示包起始位置的标记)==,在末尾加上用于检测错误的帧校验序列==(来检查包传输过程中因噪声导致的波形紊乱、数据错误)==(图 2.24)。

image-20220416104942475

报头是一串像 10101010…这样 1 和 0 交替出现的比特序列,长度为 56比特,它的作用是确定包的读取时机。当这些 1010 的比特序列被转换成电信号后,会形成如图 2.25 这样的波形。接收方在收到信号时,遇到这样的波形就可以判断读取数据的时机。关于这一块内容,我们得先讲讲如何通过电信号来读取数据。

image-20220416105001991

向集线器发送网络包

加上报头、起始帧分界符和 FCS 之后,我们就可以将包通过网线发送出去了(图 2.24)。发送信号的操作分为两种,一种是使用集线器的半双工模式,另一种是使用交换机的全双工模式==(发送和接收同时并行的方式叫作“全双工”,相对地,某一时刻只能进行发送或接收其中一种操作的叫作“半双工”)==。

==网卡的 MAC 模块生成通用信号,然后由 PHY(MAU)模块转换成可在网线中传输的格式,并通过网线发送出去。==

接收返回包

PHY(MAU)模块先开始工作, 然后再轮到 MAC 模块。MAC 模块的工作完成了,接下来网卡会通知计算机收到了一个包。

通知计算机的操作会使用一个叫作中断的机制。在网卡执行接收包的操作的过程中,==计算机并不是一直监控着网卡的活动==,而是去继续执行其他的任务。因此,==如果网卡不通知计算机,计算机是不知道包已经收到了这件事的==。网卡驱动也是在计算机中运行的一个程序,因此它也不知道包到达的状态。在这种情况下,我们==需要一种机制能够打断计算机正在执行的任务,让计算机注意到网卡中发生的事情,这种机制就是中断==。

具体来说,中断的工作过程是这样的。首先,网卡向扩展总线中的中断信号线发送信号,该==信号线通过计算机中的中断控制器连接到 CPU==。当产生中断信号时,CPU 会暂时挂起正在处理的任务,切换到操作系统中的中断处理程序 。然后,==中断处理程序会调用网卡驱动,控制网卡执行相应的接收操作==。

网卡驱动被中断处理程序调用后,会从网卡的缓冲区中取出收到的包,并通过 MAC 头部中的以太类型字段判断协议的类型。

假设 Web 服务器返回了一个网络包,服务器返回的包的以太类型应该是 0800,因此网卡驱动会将其交给 TCP/IP 协议栈来进行处理。

接下来轮到 IP 模块工作,进行==查看接收方 IP 地址==等操作。

接下来包会被交给 TCP 模块。TCP模块会根据 IP 头部中的接收方和发送方 IP 地址,以及 ==TCP 头部中的接收方和发送方端口号==来查找对应的套接字。找到对应的套接字之后,就可以根据套接字中记录的通信状态,执行相应的操作了。例如,如果包的内容是应用程序数据,则返回确认接收的包,并将数据放入缓冲区,等待应用程序来读取;如果是建立或断开连接的控制包,则返回相应的响应控制包,并告知应用程序建立和断开连接的操作状态。

2.6 UDP 协议的收发操作

TCP 的工作方式十分复杂,为什么要设计得如此复杂呢?因为我们需要将数据高效且可靠地发送给对方。为了实现可靠性,我们就需要确认对方是否收到了我们发送的数据,如果没有还需要再发一遍。

适合使用 UDP的情况:

==不需要重发的数据==

==控制用的短数据==:像 DNS 查询等交换控制信息的操作基本上都可以在一个包的大小范围内解决,这种场景中就可以用 UDP 来代替TCP

==音频和视频数据==:音频和视频数据必须在规定的时间内送达,一旦送达晚了,就会错过播放时机,导致声音和图像卡顿

第3章 从网线到网络设备——探索集线器、交换机和路由器

image-20220418161653536

3.1 信号在网线和集线器中传输

image-20220418162146369

每个包都是独立传输的

从计算机发送出来的网络包会通过集线器、路由器等设备被转发,最终到达目的地。==转发设备会根据包头部中的控制信息==,在转发设备内部一个写有转发规则的表中进行查询,以此来判断包的目的地,然后将包朝目的地的方向进行转发。无论包里面装的是应用程序的数据或者是 TCP 协议的控制信息 ,都不会对包的传输操作本身产生影响。因此,所有的包在传输到目的地的过程中都是独立的,相互之间没有任何关联。

“双绞”是为了抑制噪声

局域网网线使用的是双绞线,其中“双绞”的意思就是以两根信号线

为一组缠绕在一起,这种拧麻花一样的设计是为了抑制噪声的影响。

集线器将信号发往所有线路

当信号到达集线器后,会被广播到整个网络中。以太网的基本架构就是将包发到所有的设备,然后由设备根据接收方 MAC 地址来判断应该接收哪些包,而集线器就是这一架构的忠实体现,它就是负责按照以太网的基本架构将信号广播出去。

3.2 交换机的包转发操作

交换机根据地址表进行转发

交换机的设计是将网络包原样转发到目的地。

image-20220418163037026

大家可以认为交换机的每个网线接口后面都是一块网卡。网线接口和后面的电路部分加在一起称为一个端口,也就是说交换机的一个端口就相当于计算机上的一块网卡 。但交换机的工作方式和网卡有一点不同。网卡本身具有 MAC 地址,并通过核对收到的包的接收方 MAC 地址判断是不是发给自己的,如果不是发给自己的则丢弃;相对地,交换机的端口不核对接收方 MAC 地址,而是直接接收所有的包并存放到缓冲区中。因此,和网卡不同,==交换机的端口不具有 MAC 地址==。

换句话说,如果在计算机上安装多块网卡,并开启“混杂模式”让网卡接收所有的网络包,然后再安装一个和交换机具备同样功能的网络包转发软件,那么这台计算机就变成了一台交换机。

将包存入缓冲区后,接下来需要查询一下这个包的接收方 MAC 地址是否已经在 MAC 地址表中有记录了。MAC 地址表主要包含两个信息,一个是设备的 MAC 地址,另一个是该设备连接在交换机的哪个端口上。以 图 3.7 中的地址表为例,MAC 地址和端口是一一对应的,通过这张表就能够判断出收到的包应该转发到哪个端口。举个例子,如果收到的包的接收方 MAC 地址为 00-02-B3-1C-9C-F9,则与图 3.7 的表中的第 3 行匹配,根据端口列的信息,可知这个地址位于 8 号端口上,然后就可以通过交换电路将包发送到相应的端口了。

交换机根据 MAC 地址表查找 MAC 地址,然后将信号发送到相应的端口。

如果接收方 MAC 地址是一个广播地址 ,那么交换机会将包发送到除源端口之外的所有端口。

广播地址(broadcast address)是一种特殊的地址,将广播地址设为接收方地址时,包会发送到网络中所有的设备。MAC 地址中的 FF:FF:FF:FF:FF:FF和 IP 地址中的 255.255.255.255 都是广播地址。

交换机的全双工模式可以同时发送和接收信号。

交换机可同时执行多个转发操作

==交换机只将包转发到具有特定 MAC 地址的设备连接的端口,其他端口都是空闲的。==如图 3.7 中的例子所示,当包从最上面的端口发送到最下面的端口时,其他端口都处于空闲状态,这些端口可以传输其他的包,因此交换机可以同时转发多个包。

==相对地,集线器会将输入的信号广播到所有的端口,如果同时输入多个信号就会发生碰撞==,无法同时传输多路信号,因此从设备整体的转发能力来看,交换机要高于集线器。

3.3 路由器的包转发操作

路由器的基本知识

网络包经过集线器和交换机之后,现在到达了路由器,并在此被转发到下一个路由器。这一步转发的工作原理和交换机类似,也是通过查表判断包转发的目标。不过在具体的操作过程上,路由器和交换机是有区别的。因为==路由器是基于 IP 设计的,而交换机是基于以太网设计的== 。

image-20220418170933656

其中转发模块负责判断包的转发目的地,端口模块负责包的收发操作。换句话说,路由器转发模块和端口模块的关系,就相当于协议栈的 IP 模块和网卡之间的关系。因此,大家可以将==路由器的转发模块想象成 IP 模块,将端口模块想象成网卡。==

路由器的各个端口都具有 MAC 地址和 IP 地址。

路由表中的信息

路由器根据“IP 地址”判断转发目标。

image-20220418171642858

最左侧的目标地址列记录的是接收方的信息。这里可能不是很容易理解,实际上这里的 IP 地址只包含表示子网的网络号部分的比特值,而表示主机号部分的比特值全部为 0。路由器会将接收到的网络包的接收方 IP地址与路由表中的目标地址进行比较,并找到相应的记录。==交换机在地址表中只匹配完全一致的记录,而路由器则会忽略主机号部分,只匹配网络号部分。打个比方,路由器在转发包的时候只看接收方地址属于哪个区,×× 区发往这一边,×× 区发往那一边。==在匹配地址的过程中,路由器需要知道网络号的比特数,因此路由表中还有一列子网掩码。子网掩码的含义和第 1 章的图 1.9(b)中介绍的子网掩码基本相同,通过这个值就可以判断出网络号的比特数。

路由器会忽略主机号,只匹配网络号。

目标地址列中的 IP 地址表示的是子网,但也有一些例外,有时地址本身的子网掩码和路由表中的子网掩码是不一致的,这是路由聚合的结果。

image-20220418171939222

现在有 3 个子网,分别为10.10.1.0/24、10.10.2.0/24、10.10.3.0/24,路由器 B 需要将包发往这 3 个子网。在这种情况下,路由器 B 的路由表中原本应该有对应这 3 个子网的 3条记录,但在这个例子中,无论发往任何一个子网,都是通过路由器 A 来进行转发,因此我们可以在路由表中将这 3 个子网合并成 ==10.10.0.0/16==,这样也可以正确地进行转发,但我们减少了路由表中的记录数量,这就是路由聚合。经过路由聚合,多个子网会被合并成一个子网,子网掩码会发生变化,同时,目标地址列也会改成聚合后的地址。

路由表的子网掩码列只表示在匹配网络包目标地址时需要对比的比特数量。

路由器的端口都具有 MAC 地址,只接收与自身地址匹配的包,遇到不匹配的包则直接丢弃。

查询路由表确定输出端口

完成包接收操作之后,路由器就会丢弃包开头的 MAC 头部。MAC 头部的作用就是将包送达路由器,==其中的接收方 MAC 地址就是路由器端口的 MAC 地址==。因此,当包到达路由器之后,MAC 头部的任务就完成了,于是 MAC 头部就会被丢弃。

接下来,路由器会根据 MAC 头部后方的 IP 头部中的内容进行包的转发操作。转发操作分为几个阶段,首先是查询路由表判断转发目标。关于具体的工作过程,我们还是来看一个实际的例子,如图 3.13 的情况,假设地址为 10.10.1.101 的计算机要向地址为 192.168.1.10 的服务器发送一个包, 这个包先到达图中的路由器。判断转发目标的第一步,就是根据包的接收方 IP 地址查询路由表中的目标地址栏,以找到相匹配的记录。就像前面讲 过的一样,这个匹配并不是匹配全部 32 个比特,而是根据子网掩码列中的值判断网络号的比特数,并匹配相应数量的比特 。例如,图 3.13 的第 3 行,==子网掩码列为 255.255.255.0,就表示需要匹配从左起 24 个比特==。网络包的接收方 IP 地址和路由表中的目标地址左起 24 个比特的内容都是192.168.1,因此两者是匹配的,该行记录就是候选转发目标之一。

按照这样的规则,我们可能会匹配到多条候选记录。在这个例子中, 第 3、4、5 行都可以匹配 。其中,==路由器首先寻找网络号比特数最长的一条记录== 。==网络号比特数越长,说明主机号比特数越短,也就意味着该子网内可分配的主机数量越少==,即子网中可能存在的主机数量越少,这一规则的目的是尽量缩小范围,所以根据这条记录判断的转发目标就会更加准确。我们来看图 3.13 中的例子。

第 3 行 192.168.1.0/255.255.255.0 表示一个子网,第 4 行192.168.1.10/ 255.255.255.255 表示一台服务器。相比服务器所属的子网来说,直接指定服务器本身的地址时范围更小,因此这里应该选择第 4 行作为转发目标。按照最长匹配原则筛选后,如果只剩一条候选记录,则按照这条记录的内容进行转发。

然而,有时候路由表中会存在网络号长度相同的多条记录,例如考虑到路由器或网线的故障而设置的备用路由就属于这种情况。这时,需要根据跃点计数的值来进行判断。==跃点计数越小说明该路由越近,因此应选择跃点计数较小的记录==。

找不到匹配路由时选择默认路由

图 3.13 路由表中的最后一行的作用就相当于把所有目标都配置好了。这一行的子网掩码为 0.0.0.0,关键就在这里,子网掩码 0.0.0.0 的意思是网络包接收方 IP 地址和路由表目标地址的匹配中需要匹配的比特数为 0,换句话说,就是根本不需要匹配。只要将子网掩码设置为 0.0.0.0,那么无论任何地址都能匹配到这一条记录,这样就不会发生不知道要转发到哪里的问题了。

==只要在这一条记录的网关列中填写接入互联网的路由器地址,当匹配不到其他路由时 ,网络包就会被转发到互联网接入路由器。==因此这条记录被称为默认路由,这一行配置的网关地址被称为默认网关。在计算机的TCP/IP 设置窗口中也有一个填写默认网关的框,意思是一样的。计算机上也有一张和路由器一样的路由表,其中默认网关的地址就是我们在设置窗口中填写的地址。

路由表中子网掩码为 0.0.0.0 的记录表示“默认路由”。

这样一来,无论目标地址是表示一个子网还是表示某台设备,都可以用相同的方法查找出转发目标,而且也避免了不知道转发到哪里的问题。

什么是子网掩码?

路由器判断下一个转发目标的方法如下

  • 如果路由表的网关列内容为 IP 地址,则该地址就是下一个转发目标。
  • 如果路由表的网关列内容为空,则 IP 头部中的接收方 IP 地址就是下一个转发目标。

路由器也会使用 ARP 来查询下一个转发目标的 MAC 地址。

路由器与交换机的关系

image-20220418213806177

图 3.16可以发现,准确的说法应该是==将 IP 包装进以太网包的数据部分中==。也就是说,==给包加上 MAC 头部并发送,从本质上说是将 IP 包装进以太网包的数据部分中,委托以太网去传输这些数据。IP 协议本身没有传输包的功能,因此包的实际传输要委托以太网来进行。路由器是基于 IP 设计的,而交换机是基于以太网设计的,因 此 IP 与以太网的关系也就是路由器与交换机的关系。换句话说,路由器将包的传输工作委托给交换机来进行== 。

==IP(路由器)负责将包送达通信对象这一整体过程,而其中将包传输到下一个路由器的过程则是由以太网(交换机)来负责的==。

3.4 路由器的附加功能

通过地址转换有效利用 IP 地址

路由器除了这些基本功能之外,还有一些附加功能。下面介绍两种最重要的功能——==地址转换==和==包过滤==

首先,我们先了解一下地址转换功能出现的背景。所谓地址,就是用来识别每一台设备的标志,因此每台设备都应该有一个唯一不重复的地址。如果不能保证每台设备有唯一不重复的地址,就会从根本上影响网络包的传输,这是一个非常严重的问题。如果任由这样发展下去,不久的将来,一旦固定地址用光,新的设备就无法接入了,互联网也就无法继续发展了。

解决这个问题的关键在于==固定地址的分配方式==。举个例子,假如有 A、 B 两家公司,它们的内网是完全独立的。这种情况下,两家公司的内网之间不会有网络包流动,即使 A 公司的某台服务器和 B 公司的某台客户端具有相同的 IP 地址也没关系,因为它们之间不会进行通信。只要在每家公司自己的范围内,能够明确判断网络包的目的地就可以了,是否和其他公司的内网地址重复无关紧要,只要每个公司的网络是相互独立的,就不会出现问题。

解决地址不足的问题,利用的就是这样的性质,即公司内部设备的地址不一定要和其他公司不重复。这样一来,公司内部设备就不需要分配固定地址了,从而大幅节省了 IP 地址。当然,就算是公司内网,也不是可以随便分配地址的,因此需要设置一定的规则,==规定某些地址是用于内网的,这些地址叫作私有地址,而原来的固定地址则叫作公有地址==。

在内网中可用作私有地址的范围仅限以下这些。

image-20220418220337660

在制定私有地址规则时,这些地址属于公有地址中还没有分配的范围。这个范围中的地址和其他公司重复也没关系,所以对于这些地址不作统一管理,不需要申请,任何人都可以自由使用。当然,如果在公司内部地址有重复就无法传输网络包了,因此必须避免在内网中出现重复的地址。

尽管这样的确能节省一部分地址,但仅凭这一点还无法完全解决问题。公司内网并不是完全独立的,而是需要通过互联网和其他很多公司相连接,所以当内网和互联网之间需要传输包的时候,问题就出现了,因为如果很多地方都出现相同的地址,包就无法正确传输了。于是,当公司内网和互联网连接的时候,需要采用图 3.17 这样的结构,即==将公司内网分成两个部分,一部分是对互联网开放的服务器,另一部分是公司内部设备==。其中==对互联网开放的部分分配公有地址==,可以和互联网直接进行通信,这一部分和之前介绍的内容是一样的。相对地,==内网部分则分配私有地址==,内网中的设备不能和互联网直接收发网络包,而是通过一种特别的机制进行连接,这个机制就叫==地址转换==。

image-20220418220608361

地址转换的基本原理

==地址转换的基本原理是在转发网络包时对 IP 头部中的 IP 地址和端口号 进行改写。==

image-20220418220703233

改写端口号的原因

早期的地址转换机制是只改写地址,不改写端口号的。但是,使用这种方法的前提是私有地址和公有地址必须一一对应,也就是说,有多少台设备要上互联网,就需要多少个公有地址。一个几千人的公司里,有几百人同时访问互联网是很正常的,这样就需要几百个公有地址。

改写端口号正是为了解决这个问题。客户端一方的端口号本来就是从空闲端口中随机选择的,因此改写了也不会有问题。端口号是一个 16 比特的数值,总共可以分配出几万个端口 A,因此如果用公有地址加上端口的组合对应一个私有地址,一个公有地址就可以对应几万个私有地址,这种方法提高了公有地址的利用率。

从互联网访问公司内网

对于从互联网访问公司内网的包,如果在对应表中没有记录就无法正常转发。因为如果对应表中没有记录,就意味着地址转换设备无法判断公有地址与私有地址之间的对应关系。

之所以无法从互联网访问内网,是因为对应表里没有相应的记录,那么我们只要事先手动添加这样的记录就可以了(图 3.19)。一般来说,用于外网访问的服务器可以放在地址转换设备的外面并为它分配一个公有地址,也可以将服务器的私有地址手动添加到地址转换设备中,这样就可以从互联网访问到这台具有私有地址的服务器了。

image-20220418221131782

路由器的包过滤功能

包过滤就是在对包进行转发时,根据 MAC 头部、IP 头部、TCP 头部的内容,按照事先设置好的规则决定是转发这个包,还是丢弃这个包。我们通常说的防火墙设备或软件,大多数都是利用这一机制来防止非法入侵的。

第4章 通过接入网进入互联网内部——探索接入网和网络运营商

image-20220419100219619

4.1 ADSL 接入网的结构和工作方式

image-20220419102301942

所谓接入网,就是指连接互联网与家庭、公司网络的通信线路 。一般家用的接入网方式包括 ADSLB、FTTH、CATV、电话线、ISDN 等,公司则还可能使用专线。

ADSL:Asymmetric Digital Subscriber Line,不对称数字用户线。它是一种利用架设在电线杆上的金属电话线来进行高速通信的技术,它的上行方向(用户到互联网)和下行方向(互联网到用户)的通信速率是不对称的。

FTTH:Fiber To The Home,光纤到户。指的是将光纤接入家庭的意思。

image-20220419102543503

image-20220419102638334

ADSL Modem(Modem:调制解调器)

BAS:Broadband Access Server,宽带接入服务器。它也是一种路由器。

PPP:Point-to-Point Protocol,点到点协议。它是电话线、ISDN 等通信线路所使用的一种协议,集成了用户认证、配置下发、数据压缩、加密等各种功能。

ATM:Asynchronous Transfer Mode,异步传输。它是在以电话线为载体的传统电话技术基础上扩展出来的一种通信方式。它的数据传输是以“信元”为单位来进行的,这和以包为单位传输数据的 TCP/IP 很像,但这种方式并不适用于计算机通信。

DSLAM:DSL Access Multiplexer,数字用户线接入复用设备。它是一种电话局用的多路 ADSL Modem,可以理解为将多个 ADSL Modem 整合在一个外壳里的设备。

PPPoE:Point-to-Point Protocol over Ethernet,以太网的点对点协议。

PPPoA:Point-to-Point Protocol over ATM

互联网接入路由器会在网络包前面加上 MAC 头部、PPPoE 头部、PPP 头 部 总 共 3 种 头 部, 然 后 发 送 给 ADSL Modem(PPPoE 方式下)。

ADSL Modem 将包拆分成信元,并转换成电信号发送给分离器。

==分离器的作用==:ADSL Modem 将信元转换为电信号之后,信号会进入一个叫作分离器的设备,然后 ADSL 信号会和电话的语音信号混合起来一起从电话线传输出去。在信号从用户端发送出去时,电话和 ADSL 信号只是同时流到一条线路上而已,分离器实际上并没有做什么事。分离器的作用其实在相反的方向,也就是信号从电话线传入的时候。这时,分离器需要负责将电话和 ADSL 的信号进行分离(图 4.7)。电话线传入的信号是电话的语音信号和 ADSL 信号混合在一起的,如果这个混合信号直接进入电话机,ADSL 信号就会变成噪音,导致电话难以听清。为了避免这样的问题,就需要通过分离器将传入的信号分离,以确保 ADSL信号不会传入电话机。

image-20220419120016433

DSLAM 具有 ATM 接口,和后方路由器收发数据时使用的是原始网络包拆分后的 ATM 信元形式。

BAS 负责将 ATM 信元还原成网络包并转发到互联网内部。

4.2 光纤接入网(FTTH)

image-20220419103926002

用光纤来代替 ADSL 将用户端接入路由器和运营商的 BAS 连接起来的接入方式就是 ==FTTH==。FTTH 可以分为直连和分路两种方式,这两种方式只是光信号的传输方式有一些区别,实际传输的网络包是相同的。当使用 PPPoE 来传输包时,其工作过程和刚才讲过的 ADSL 类似。具体来说,就是像图4.3 中的⑤一样,由互联网接入路由器在 IP 头部前面加上 MAC 头部、PPPoE 头部和 PPP 头部,然后由光纤收发器或者 ONU 转换成光信号 D,并通过光纤到达 BAS 前面的多路光纤收发器和 OLT,最后被还原成电信号并到达 BAS。

image-20220419104854160

4.3 接入网中使用的 PPP 和隧道

用户发送的网络包会通过 ADSL 和 FTTH 等接入网到达运营商的BAS。

互联网本来就是由很多台路由器相互连接组成的,因此原则上应该是将接入网连接到路由器上。随着接入网发展到 ADSL 和 FTTH,接入网连接的路由器也跟着演进,而这种进化型的路由器就叫作 BAS。

首先是==用户认证和配置下发功能==。ADSL 和 FTTH 接入网中,都需要先输入用户名和密码 ,登录之后才能访问互联网,而 BAS 就是登录操作的窗口。BAS 使用 PPPoE 方式来实现这个功能 。PPPoE 是由传统电话拨号上网上使用的 PPP 协议发展而来的。

PPPoE 是将 PPP 消息装入以太网包进行传输的方式。

BAS 除了作为用户认证的窗口之外,还可以==使用隧道方式来传输网络包==。所谓隧道,就类似于套接字之间建立的 TCP 连接。在 TCP 连接中,我们从一侧的出口(套接字)放入数据,数据就会原封不动地从另一个出口出来,隧道也是如此。也就是说,我们将包含头部在内的整个包从隧道的一头扔进去,这个包就会原封不动地从隧道的另一头出来。

image-20220419111014884

接入网的整体工作过程

互联网接入路由器通过 PPPoE 的发现机制查询 BAS 的 MAC 地址。

BAS 下发的 TCP/IP 参数会被配置到互联网接入路由器的 BAS端的端口上,这样路由器就完成接入互联网的准备了。

BAS 在收到用户路由器发送的网络包之后,会去掉 MAC 头部和PPPoE 头部,然后用隧道机制将包发送给网络运营商的路由器。

除 PPPoE 之外的其他方式

实际的接入网还有其他一些方式。

使用==PPPoA== 方式的 ADSL 接入网(PPPoA 不能用于 FTTH,因为 FTTH 不使用 ATM 信元)。PPPoA 方式不添加 MAC 头部和 PPPoE 头部,而是直接将包装入信元中。

还有一种 ==DHCP== 方式,它不使用 PPP,而是将以太网包直接转换成 ADSL 信号发送给 DSLAM。采用 DHCP 的运营商使用的 ADSL Modem 也和 PPPoE、PPPoA方式不同,这种 ADSL Modem 不使用信元,而是直接将以太网包调制成ADSL 信号,因此没有 ADSL Modem 和路由器无法分离的问题(使用信元的 PPPoE 和 PPPoA 方式中,BAS 需要配备比较昂贵的 ATM 接口,因此不使用信元还可以控制成本)。

4.4 网络运营商的内部

现在网络包已经通过接入网,到达了网络运营商的路由器。这里是互联网的入口,网络包会从这里进入互联网内部

互联网的实体并不是由一个组织运营管理的单一网络,而是由多个运营商网络相互连接组成的(图 4.23)。ADSL、FTTH 等接入网是与用户签约的运营商设备相连的,这些设备称为==POP(POP:Point of Presense,中文一般叫作“接入点”)==,互联网的入口就位于这里。

image-20220419114038710

==网络包通过接入网之后,到达运营商 POP 的路由器。==

image-20220419114144262

==NOC(NOC:Network Operation Center,网络运行中心)== 是运营商的核心设备,从 POP 传来的网络包都会集中到这里,并从这里被转发到离目的地更近的 POP,或者是转发到其他的运营商。这里也需要配备高性能的路由器。

4.5 跨越运营商的网络包

只要让相连的路由器告知路由信息就可以了。只要获得了对方的路由信息,就可以知道对方路由器连接的所有网络,将这些信息写入自己的路由表中,也就可以向那些网络发送包了。

image-20220419141030996

获得对方的路由信息之后,我们也需要将自身的路由信息告知对方。这样一来,对方也可以将发往我们所在子网的包转发过来。这个路由信息交换的过程是由路由器自动完成的,这里使用的机制称为 ==BGP(BGP:Border Gateway Protocol,边界网关协议)==。

==互联网内部使用 BGP 机制在运营商之间交换路由信息。==

根据所告知的路由信息的内容,这种路由交换可分为两类。一类是将互联网中的路由全部告知对方。例如图 4.26 中,如果运营商 D 将互联网上所有路由都告知运营商 E,则运营商 E 不但可以访问运营商 D,还可以访问运营商 D 后面的运营商 B、A 和 C。然后,通过运营商 D 就可以向所有的运营商发送包。像这样,通过运营商 D 来发送网络包的方式称为==转接==。

另一种类型是两个运营商之间仅将与各自网络相关的路由信息告知对方。这样,只有双方之间的网络可以互相收发网络包,这种方式称为非转接,也叫==对等== 。

image-20220419141258200

IX 的必要性

图 4.26 中有一个叫作==IX(IX:Internet eXchange,中文一般叫作“互联网交换中心”)==的东西,我们来说说它是干什么用的。对于两个运营商来说,像图 4.26 中运营商 D 和运营商 C 这样一对一的连接是最基本的一种连接方式,现在也会使用这种方式。但这种方式有个不方便的地方,如果运营商之间只能一对一连接,那么就需要像图 4.27(a)这样将所有的运营商都用通信线路连接起来。现在光日本国内就有数千家运营商,这样连接非常困难。对于这种情况,我们可以采用图 4.27(b)的方式,设置一个中心设备,通过连接到中心设备的方式来减少线路数量,这个中心设备就称为 IX。

image-20220419141440093

运营商如何通过 IX 互相连接

image-20220419141459727

第5章 服务器端的局域网中有什么玄机

image-20220419141737354

5.1 Web 服务器的部署地点

image-20220419142839602

5.2 防火墙的结构和原理

防火墙的基本思路:即==只允许发往特定服务器中的特定应用程序的包通过,然后屏蔽其他的包==。

==包过滤方式的防火墙可根据接收方 IP 地址、发送方 IP 地址、接收方端口号、发送方端口号、控制位等信息来判断是否允许某个包通过。==

image-20220419144019867

5.3 通过将请求平均分配给多台服务器来平衡负载

==性能不足时需要负载均衡==,使用多台服务器来分担负载的方法,这种架构统称为==分布式架构==。

其中对于负载的分担有几种方法,最简单的一种方法就是采用多台 Web 服务器,减少每台服务器的访问量。假设现在我们有 3 台服务器,那么每台服务器的访问量会减少到三分之一,负载也就减轻了。要采用这样的方法,必须有一个机制将客户端发送的请求分配到每台服务器上。

==通过 DNS 服务器来分配:轮询(round-robin)==

image-20220419145228192

==使用负载均衡器分配访问==

用负载均衡器时,首先要用负载均衡器的 IP 地址代替 Web 服务器的实际地址注册到 DNS 服务器上。假设有一个域名 www.lab.glasscom.com,==我们将这个域名对应的 IP 地址设置为负载均衡器的 IP 地址并注册到 DNS 服务器上。于是,客户端会认为负载均衡器就是一台 Web 服务器==,并向其发送请求,然后由负载均衡器来判断将请求转发给哪台 Web 服务器(图 5.4)。

image-20220419145541773

5.4 使用缓存服务器分担负载

除了使用多台功能相同的 Web 服务器分担负载之外,还有另外一种方法,就是将整个系统按功能分成不同的服务器 ,如 Web 服务器、数据库服务器。==缓存服务器就是一种按功能来分担负载的方法==。缓存服务器是一台通过代理机制对数据进行缓存的服务器。代理介于Web 服务器和客户端之间,具有对 Web 服务器访问进行中转的功能。当进行中转时,它可以将 Web 服务器返回的数据保存在磁盘中,并可以代替Web 服务器将磁盘中的数据返回给客户端。这种保存的数据称为缓存,缓存服务器指的也就是这样的功能。

image-20220419152050965

image-20220419152110561

image-20220419152119897

image-20220419152139156

正向代理和反向代理

**正向代理,是"代理服务器"代理了"客户端",去和"目标服务器"进行交互。**通过正向代理服务器访问目标服务器,目标服务器是不知道真正的客户端是谁的。

**反向代理,是"代理服务器"代理了"目标服务器",去和"客户端"进行交互。**通过反向代理服务器访问目标服务器时,客户端是不知道真正的目标服务器是谁的。

正向代理和反向代理的区别

虽然正向代理服务器和反向代理服务器所处的位置都是客户端和真实服务器之间,所做的事情也都是把客户端的请求转发给服务器,再把服务器的响应转发给客户端,但是二者之间还是有一定的差异的。

1、正向代理其实是客户端的代理,帮助客户端访问其无法访问的服务器资源。反向代理则是服务器的代理,帮助服务器做负载均衡,安全防护等。

2、正向代理一般是客户端架设的,比如在自己的机器上安装一个代理软件。而反向代理一般是服务器架设的,比如在自己的机器集群中部署一个反向代理服务器。

3、正向代理中,服务器不知道真正的客户端到底是谁,以为访问自己的就是真实的客户端。而在反向代理中,客户端不知道真正的服务器是谁,以为自己访问的就是真实的服务器。

4、正向代理和反向代理的作用和目的不同。正向代理主要是用来解决访问限制问题。而反向代理则是提供负载均衡、安全防护等作用。二者均能提高访问速度。

5.5 内容分发服务

利用内容分发服务分担负载

image-20220419154118060

作为一个 Web 服务器运营者,如果自己和这些运营商签约并部署缓存服务器,无论是费用还是精力都是吃不消的。为了解决这个问题,一些专门从事相关服务的厂商出现了,他们来部署缓存服务器,并租借给 Web 服务器运营者。这种服务称为==内容分发服务==。

提供这种服务的厂商称为 CDSPB,他们会与主要的供应商签约,并部署很多台缓存服务器 C。另一方面,CDSP 会与 Web 服务器运营者签约,使 得 CDSP 的缓存服务器配合 Web 服务器工作。只要 Web 服务器与缓存服务器建立关联,那么当客户端访问 Web 服务器时,实际上就是在访问 CDSP 的缓存服务器了。

缓存服务器可以缓存多个网站的数据,因此 CDSP 的缓存服务器就可以提供给多个 Web 服务器的运营者共享。这样一来,每个网站运营者的平均成本就降低了,从而减少了网站运营者的负担。而且,和运营商之间的签约工作也由 CDSP 统一负责,网站运营者也节省了精力。

内容分发服务也叫 CDS(Content Delivery Service)。(现在更常用的名称叫 CDN(Content Delivery Network 或 Content Distribution Network)。 CDSP:Content Delivery Service Provider,内容分发服务运营商。

如何找到最近的缓存服务器

image-20220419160656942

image-20220419160717234

image-20220419160828020

第6章 请求到达Web服务器,响应返回浏览器——短短几秒的“漫长旅程”迎来终点

image-20220419160852823

6.1 服务器概览

image-20220419163022036

==从数据收发的角度来看,发起连接的一方是客户端,等待连接的一方是服务器。==

这个区别体现在如何调用 Socket 库上。首先,客户端的数据收发需要经过下面 4 个阶段。

(1)创建套接字(创建套接字阶段)

(2)用管道连接服务器端的套接字(连接阶段)

(3)收发数据(收发阶段)

(4)断开管道并删除套接字(断开阶段)

相对地,==服务器是将阶段(2)改成了等待连接==,具体如下。

(1)创建套接字(创建套接字阶段)

(2-1)将套接字设置为等待连接状态(等待连接阶段)

(2-2)接受连接(接受连接阶段)

(3)收发数据(收发阶段)

(4)断开管道并删除套接字(断开阶段)

image-20220419163033241

image-20220419163113353

要确定某个套接字时,不仅使用服务器端套接字对应的端口号,还同时使用客户端的端口号再加上 IP 地址,总共使用下面 4 种信息来进行判断(图 6.4)

  • 客户端 IP 地址
  • 客户端端口号
  • 服务器 IP 地址
  • 服务器端口号

image-20220419163222034

==使用描述符来指代套接字的原因如下==:

(1)等待连接的套接字中没有客户端 IP 地址和端口号

(2)使用描述符这一种信息比较简单

6.2 服务器的接收操作

image-20220419164734684

==网卡的 MAC 模块将网络包从信号还原为数字信息,校验 FCS并存入缓冲区。==

==网卡驱动会根据 MAC 头部判断协议类型,并将包交给相应的协议栈。==

==协议栈的 IP 模块会检查 IP 头部,(1)判断是不是发给自己的;(2)判断网络包是否经过分片;(3)将包转交给 TCP 模块或 UDP模块。==

==如果收到的是发起连接的包,则 TCP 模块会(1)确认 TCP 头部的控制位 SYN;(2)检查接收方端口号;(3)为相应的等待连接套接字复制一个新的副本;(4)记录发送方 IP 地址和端口号等信息。==

==收到数据包时,TCP 模块会(1)根据收到的包的发送方 IP 地址、发送方端口号、接收方 IP 地址、接收方端口号找到相对应的套接字;(2)将数据块拼合起来并保存在接收缓冲区中;(3)向客户端返回 ACK。==

image-20220419164852113

6.3 Web 服务器程序解释请求消息并作出响应

==将请求的 URI 转换为实际的文件名==

image-20220419170422236

image-20220419170430457

==运行 CGI 程序==

什么是CGI程序?

CGI程序,就是放置在服务器上的一段可执行程序。作为HTTP服务器的时候,客户端可以通过GET或者POST请求来调用这可执行程序。

image-20220419170518733

image-20220419170530258

==Web 服务器的访问控制==

image-20220419170554500

==返回响应消息==

首先,Web 服务器调用 Socket 库的==write==,将响应消息交给协议栈。这时,需要告诉协议栈这个响应消息应该发给谁,但我们并不需要直接告知客户端的 IP 地址等信息,而是只需要给出表示通信使用的套接字的描述符就可以了。套接字中保存了所有的通信状态,其中也包括通信对象的信息,因此只要有描述符就万事大吉了。

接下来,协议栈会将数据拆分成多个网络包,然后加上头部发送出去。这些包中包含接收方客户端的地址,它们将经过交换机和路由器的转发,通过互联网最终到达客户端。==(图6.7)==

6.4 浏览器接收响应消息并显示内容

通过响应的数据类型判断其中的内容

原则上可以根据响应消息开头的 ==Content-Type== 头部字段的值来进行判断。这个值一般是下面这样的字符串。

Content-Type: text/html

其中“/”左边的部分称为“主类型”,表示数据的大分类;右边的“子类型”表示具体的数据类型。在上面的例子中,主类型是 text,子类型是html。主类型和子类型的含义都是事先确定好的 A,表 6.1 列出了其中主要的一些类型。上面例子中的数据类型表示遵循 HTML 规格的 HTML 文档。

image-20220419171852658

此外,当数据类型为文本时,还需要判断编码方式,这时需要用 ==charset== 附加表示文本编码方式的信息,内容如下。

Content-Type: text/html; charset=utf-8

附录 网络包的旅程

image-20220419172026487

image-20220419172045088