网络是怎样连接的
上QQ阅读APP看书,第一时间看更新

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

1.2.1 IP地址的基本知识

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

互联网和公司内部的局域网都是基于TCP/IP的思路来设计的,所以我们先来了解TCP/IP的基本思路。TCP/IP的结构如图1.8所示,就是由一些小的子网,通过路由器路由器:一种对包进行转发的设备,在第3章有详细介绍。连接起来组成一个大的网络。这里的子网可以理解为用集线器集线器:一种对包进行转发的设备,分为中继式集线器和交换式集线器两种,在第3章有详细介绍。连接起来的几台计算机当计算机数量较少时,可以用一台集线器连接起来;当计算机数量较多时,一台集线器可能无法连接这么多计算机,可以增加集线器数量并将集线器相互连接起来,这时,凡是通过集线器连接起来的所有设备都属于同一个子网。,我们将它看作一个单位,称为子网。将子网通过路由器连接起来,就形成了一个网络一些家用路由器中已经内置了集线器功能,因此大家可以理解为这种路由器内部同时包含路由器和集线器两种设备,它们在里面已经连接起来了。

在网络中,所有的设备都会被分配一个地址。这个地址就相当于现实中某条路上的“××号××室”。其中“号”对应的号码是分配给整个子网的,而“室”对应的号码是分配给子网中的计算机的,这就是网络中的地址。“号”对应的号码称为网络号,“室”对应的号码称为主机号,这个地址的整体称为IP地址IP地址和现实中的地址含义是相同的,因此就像“××号××室”不能有两户人家的号码相同一样,也不能有两台设备使用相同的IP地址。现实中其实存在因为疏漏两台设备被分配了相同的IP地址的情况,但这种情况下网络会发生故障,无法正常工作。。通过IP地址我们可以判断出访问对象服务器的位置,从而将消息发送到服务器。消息传送的具体过程在后面的章节有详细讲解,不过现在我们先简单了解一下。发送者发出的消息首先经过子网中的集线器数据是以包的形式传送的。,转发到距离发送者最近的路由器上(图1.8①)。接下来,路由器会根据消息的目的地判断下一个路由器的位置,然后将消息发送到下一个路由器,即消息再次经过子网内的集线器被转发到下一个路由器(图1.8②)。前面的过程不断重复,最终消息就被传送到了目的地。

图1.8 IP的基本思路

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

图1.9 IP地址的表示方法

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

图1.10 IP地址的结构

子网掩码表示网络号与主机号之间的边界。在本例中,这个边界与字节的边界是正好吻合的,也就是正好划分在句点的位置上,实际上也可以划分在字节的中间位置。

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

IP地址的主机号

全0:表示整个子网

全1:表示向子网上所有设备发送包,即“广播”

1.2.2 域名和IP地址并用的理由

TCP/IP网络是通过IP地址来确定通信对象的,因此不知道IP地址就无法将消息发送给对方,这和我们打电话的时候必须要知道对方的电话号码是一个道理。因此,在委托操作系统发送消息时,必须要先查询好对方的IP地址。

可能你会问“既然如此,那么在网址中不写服务器的名字,直接写IP地址不就好了吗?”实际上,如果用IP地址来代替服务器名称也是能够正常工作的如果Web服务器使用了虚拟主机功能,有可能无法通过IP地址来访问。。然而,就像你很难记住电话号码一样,要记住一串由数字组成的IP地址也非常困难。因此,相比IP地址来说,网址中还是使用服务器名称比较好也有人说域名也很难记啊,不过在设计TCP/IP架构的当时,在技术上还无法实现我们今天的搜索引擎,因此用名称来代替地址本身是有价值的。

那么又有人问了:“既然如此,那干脆不要用IP地址,而是用名称来确定通信对象不就好了吗?互联网中使用的是最新的网络技术,和电话那种老古董可不一样,这样的功能应该还是做得到的吧?”这样的想法其实并不奇怪实际上真的存在以名称来确定通信对象的网络,Windows网络的原型PC-Networks就是其中的一个例子。

不过从运行效率上来看,这并不能算是一个好主意。互联网中存在无数的路由器,它们之间相互配合,根据IP地址来判断应该把数据传送到什么地方。那么如果我们不用IP地址而是改用名称会怎么样呢?IP地址的长度为32比特,也就是4字节,相对地,域名最短也要几十个字节,最长甚至可以达到255字节。换句话说,使用IP地址只需要处理4字节的数字,而域名则需要处理几十个到255个字节的字符,这增加了路由器的负担,传送数据也会花费更长的时间域名并不仅是长,而且其长度是不固定的。处理长度不固定的数据比处理长度固定的数据要复杂,这也是造成效率低下的重要原因之一。。可能有人会说:“那使用高性能路由器不就能解决这个问题了吗?”然而,路由器的速度是有极限的,而互联网内部流动的数据量已然让路由器疲于应付了,因此我们不应该再采用效率更低的设计。随着技术的发展,路由器的性能也会不断提升,但与此同时,数据量也在以更快的速度增长,在可预见的未来,这样的趋势应该不会发生变化。出于这样的原因,使用名称本身来确定通信对象并不是一个聪明的设计。

于是,现在我们使用的方案是让人来使用名称,让路由器来使用IP地址。为了填补两者之间的障碍,需要有一个机制能够通过名称来查询IP地址,或者通过IP地址来查询名称,这样就能够在人和机器双方都不做出牺牲的前提下完美地解决问题。这个机制就是DNSDNS:Domain Name System,域名服务系统。将服务器名称和IP地址进行关联是DNS最常见的用法,但DNS的功能并不仅限于此,它还可以将邮件地址和邮件服务器进行关联,以及为各种信息关联相应的名称。

1.2.3 Socket库提供查询IP地址的功能

查询IP地址的方法非常简单,只要询问最近的DNS服务器“www.lab.glasscom.com的IP地址是什么”就可以了,DNS服务器会回答说“该服务器的IP地址为xxx.xxx.xxx.xxx”。这一步非常简单,很多读者也都很熟悉,那么浏览器是如何向DNS服务器发出查询的呢?让我们把向Web服务器发送请求消息的事情放一放,先来探索一下DNS。

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

解析器实际上是一段程序,它包含在操作系统的Socket库中,在介绍解析器之前,我们先来简单了解一下Socket库。首先,库到底是什么东西呢?库就是一堆通用程序组件的集合,其他的应用程序都需要使用其中的组件。库有很多好处。首先,使用现成的组件搭建应用程序可以节省编程工作量;其次,多个程序使用相同的组件可以实现程序的标准化。除此之外还有很多其他的好处,因此使用库来进行软件开发的思路已经非常普及,库的种类和数量也非常之多。Socket库也是一种库,其中包含的程序组件可以让其他的应用程序调用操作系统的网络功能Socket库是在加州大学伯克利分校开发的UNIX系操作系统BSD中开发的C语言库,互联网中所使用的大多数功能都是基于Socket库来开发的。因此,BSD之外的其他操作系统以及C语言之外的其他编程语言也参照Socket库开发了相应的网络库。可以说,Socket库是网络开发中的一种标准库。,而解析器就是这个库中的其中一种程序组件。

Socket库中包含很多用于发送和接收数据的程序组件,这些功能我们暂且放一放,先来集中精力探索一下解析器。

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

1.2.4 通过解析器向DNS服务器发出查询

解析器的用法非常简单。Socket库中的程序都是标准组件,只要从应用程序中进行调用就可以了。具体来说,在编写浏览器等应用程序的时候,只要像图1.11这样写上解析器的程序名称“gethostbyname”以及Web服务器的域名“www.lab.glasscom.com”就可以了,这样就完成了对解析器的调用实际上,除此之外还需要编写一些用于分配保存IP地址的内存空间的语句,并在程序开头使用#include命令将其包含进来。

图1.11 解析器的调用方法

在应用程序中编写上图中的一行代码后就能够调用解析器完成向DNS服务器查询IP地址的操作。

调用解析器后,解析器会向DNS服务器发送查询消息,然后DNS服务器会返回响应消息。响应消息中包含查询到的IP地址,解析器会取出IP地址,并将其写入浏览器指定的内存地址中。只要运行图1.11中的这一行程序,就可以完成前面所有这些工作,我们也就完成了IP地址的查询。接下来,浏览器在向Web服务器发送消息时,只要从该内存地址取出IP地址,并将它与HTTP请求消息一起交给操作系统就可以了。

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

1.2.5 解析器的内部原理

下面来看一看当应用程序调用解析器时,解析器内部是怎样工作的(图1.12)。网络应用程序(在我们的场景中就是指浏览器)调用解析器时,程序的控制流程就会转移到解析器的内部。“控制流程转移”这个说法对于没有编程经验的人来说可能不容易理解,所以这里简单解释一下。

图1.12 调用解析器时计算机内部的工作流程

通过让多个程序按顺序执行操作,数据就被发送出去了。

一般来说,应用程序编写的操作内容是从上往下按顺序执行的,当到达需要调用解析器的部分时,对应的那一行程序就会被执行,应用程序本身的工作就会暂停(图1.12①)。然后,Socket库中的解析器开始运行(图1.12②),完成应用程序委托的操作。像这样,由于调用了其他程序,原本运行的程序进入暂停状态,而被调用的程序开始运行,这就是“控制流程转移”在图1.12中,我们假设gethostbyname这个程序实现了解析器的全部功能,实际上,实现解析器的功能需要多个程序相互配合,可能还会从gethostbyname程序中调用其他的程序。但如果继续深挖下去的话会变得复杂难懂,因此在这里我们假设gethostbyname实现了解析器的全部功能。

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

当DNS服务器收到查询消息后,它会根据消息中的查询内容进行查询。这个查询的过程有点复杂,我们稍后会进行讲解,这里先不关心具体的方法。

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

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

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

顺带一提,向DNS服务器发送消息时,我们当然也需要知道DNS服务器的IP地址。只不过这个IP地址是作为TCP/IP的一个设置项目事先设置好的,不需要再去查询了。不同的操作系统中TCP/IP的设置方法也有差异,Windows中的设置如图1.13所示,解析器会根据这里设置的DNS服务器IP地址来发送消息。

图1.13 DNS服务器地址的设置