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

1.1 生成HTTP请求消息

1.1.1 探索之旅从输入网址开始

我们的探索之旅从在浏览器中输入网址开始某些情况下,浏览器的工作是从点击网页中的一个链接开始,大家可以认为这种情况与将链接中所包含的网址输入到浏览器的地址栏中是一样的。——译者注,在介绍浏览器的工作方式之前,让我们先来介绍一下网址。网址,准确来说应该叫URLURL:Uniform Resource Locator,统一资源定位符。,如果我说它就是以http://开头的那一串东西,恐怕大家一下子就明白了,但实际上除了“http:”,网址还可以以其他一些文字开头,例如“ftp:”“file:”“mailto:”如果没有正确配置电子邮件软件,则即使在地址栏中输入“mailto:”也是无法正常工作的。等。

之所以有各种各样的URL,是因为尽管我们通常是使用浏览器来访问Web服务器的,但实际上浏览器并不只有这一个功能,它也可以用来在FTPFTP:File Transfer Protocol,文件传送协议。这是一种在上传、下载文件时使用的协议。使用FTP协议来传送文件的程序也被叫作FTP。服务器上下载和上传文件,同时也具备电子邮件客户端的功能。可以说,浏览器是一个具备多种客户端功能的综合性客户端软件,因此它需要一些东西来判断应该使用其中哪种功能来访问相应的数据,而各种不同的URL就是用来干这个的,比如访问Web服务器时用“http:”,而访问FTP服务器时用“ftp:”。

图1.1列举了现在互联网中常见的几种URL,根据访问目标的不同,URL的写法也会不同。例如在访问Web服务器和FTP服务器时,URL中会包含服务器的域名域名:就是像www.glasscom.com这样以句点(.)分隔的名称。关于域名,1.2.2节和1.3.2节有详细说明。和要访问的文件的路径名等,而发邮件的URL则包含收件人的邮件地址。此外,根据需要,URL中还会包含用户名、密码、服务器端口号端口号:1.4.3节和第6章的6.1.3节有详细说明,这里请大家理解为一个用来识别要连接的服务器程序的编号。不同的服务器程序会使用不同的编号,例如Web是80,邮件是25等。等信息。

图1.1 URL的各种格式

尽管URL有各种不同的写法,但它们有一个共同点,那就是URL开头的文字,即“http:”“ftp:”“file:”“mailto:”这部分文字都表示浏览器应当使用的访问方法。比如当访问Web服务器时应该使用HTTPHTTP:Hypertext Transfer Protocol,超文本传送协议。协议,而访问FTP服务器时则应该使用FTP协议。因此,我们可以把这部分理解为访问时使用的协议协议:通信操作的规则定义称为协议(protocol)。类型像“file:”这样的URL在访问时是不使用网络的,因此说URL的开头部分表示的是协议类型并不完全准确,也许理解为“访问方法”会更好一些。。尽管后面部分的写法各不相同,但开头部分的内容决定了后面部分的写法,因此并不会造成混乱。

1.1.2 浏览器先要解析URL

浏览器要做的第一步工作就是对URL进行解析,从而生成发送给Web服务器的请求消息。刚才我们已经讲过,URL的格式会随着协议的不同而不同,因此下面我们以访问Web服务器的情况为例来进行讲解。

根据HTTP的规格,URL包含图1.2(a)中的这几种元素。当对URL进行解析时,首先需要按照图1.2(a)的格式将其中的各个元素拆分出来,例如图1.2(b)中的URL会拆分成图1.2(c)的样子。然后,通过拆分出来的这些元素,我们就能够明白URL代表的含义。例如,我们来看拆分结果图1.2(c),其中包含Web服务器名称www.lab.glasscom.com,以及文件的路径名/dir1/file1.html,因此我们就能够明白,图1.2(b)中的URL表示要访问www.lab.glasscom.com这个Web服务器上路径名为/dir/file1.html的文件,也就是位于/dir/目录目录(directory)这个词的意思相当于Windows中的文件夹(folder)。下的file1.html这个文件(图1.3)。

图1.2 Web浏览器解析URL的过程

图1.3 路径名为/dir/file1.html的文件

1.1.3 省略文件名的情况

图1.2(b)是一个以“http:”开头的典型URL,但有时候我们也会见到一些不太一样的URL,例如下面这个URL是以“/”来结尾的。

      (a)http://www.lab.glasscom.com/dir/

我们可以这样理解,以“/”结尾代表/dir/后面本来应该有的文件名被省略了。根据URL的规则,文件名可以像前面这样省略。

不过,没有文件名,服务器怎么知道要访问哪个文件呢?其实,我们会在服务器上事先设置好文件名省略时要访问的默认文件名。这个设置根据服务器不同而不同,大多数情况下是index.html或者default.htm之类的文件名。因此,像前面这样省略文件名时,服务器就会访问/dir/index.html或者/dir/default.htm。

还有一些URL是像下面这样只有Web服务器的域名的,这也是一种省略了文件名的形式。

      (b)http://www.lab.glasscom.com/

这个URL也是以“/”结尾的,也就是说它表示访问一个名叫“/”的目录“/”目录表示目录层级中最顶层的“根目录”。也许单独一个“/”表示根目录的写法看上去很奇怪,其实只要明白目录的规则就很容易理解了。我们写目录名时会像“dir/”一样在末尾加上一个“/”,这一点大家应该没什么疑问,那么如果目录本身没有名字的话会怎么样呢?在上面的例子中,就相当于“dir/”去掉了其中的dir,这样一来就只剩下一个“/”了,这就是表示根目录的“/”。对于目录层级最顶层的那个目录,我们出于方便的考虑把它叫作根目录,其实它本身并没有名字,因此我们仅用“/”来表示它。。而且,由于省略了文件名,所以结果就是访问/index.html或者/default.htm这样的文件了。

那么,下面这个URL又是什么意思呢?

      (c)http://www.lab.glasscom.com

这次连结尾的“/”都省略了。像这样连目录名都省略时,真不知道到底在请求哪个文件了,实在有些过分。不过,这种写法也是允许的。当没有路径名时,就代表访问根目录下事先设置的默认文件最早的时候这个文件被叫作“主页”(home page),意思就是当省略文件名时访问的那个默认的页面。随着Web的普及,这个词的意义似乎并没有被正确理解,现在不光是默认页面,似乎随便什么网页都可以被叫作主页了。,也就是/index. html或者/default.htm这些文件,这样就不会发生混乱了。

不过,下面这个例子就更诡异了。

      (d)http://www.lab.glasscom.com/whatisthis

前面这个例子中,由于末尾没有“/”,所以whatisthis应该理解为文件名才对。但实际上,很多人并没有正确理解省略文件名的规则,经常会把目录末尾的“/”也给省略了。因此,或许我们不应该总是将whatisthis作为文件名来处理。一般来说,这种情况会按照下面的惯例进行处理:如果Web服务器上存在名为whatisthis的文件,则将whatisthis作为文件名来处理;如果存在名为whatisthis的目录,则将whatisthis作为目录名来处理我们无法创建两个名字相同的文件和目录,因此不可能既有一个名为whatisthis的文件,同时又有一个名为whatisthis的目录。只要查询一下磁盘中的文件和目录,就可以知道whatisthis究竟是一个文件还是一个目录了,并不会产生歧义。

浏览器的第一步工作就是对URL进行解析。

1.1.4 HTTP的基本思路

解析完URL之后,我们就知道应该要访问的目标在哪里了。接下来,浏览器会使用HTTP协议来访问Web服务器,不过在介绍这一环节之前,我们先来讲一讲HTTP协议到底是怎么回事。

图1.4 HTTP的基本思路

HTTP协议定义了客户端和服务器之间交互的消息内容和步骤,其基本思路非常简单。首先,客户端会向服务器发送请求消息(图1.4)。请求消息中包含的内容是“对什么”和“进行怎样的操作”两个部分。其中相当于“对什么”的部分称为URIURI:Uniform Resource Identifier,统一资源标识符。。一般来说,URI的内容是一个存放网页数据的文件名或者是一个CGI程序CGI程序:对Web服务器程序调用其他程序的规则所做的定义就是CGI,而按照CGI规范来工作的程序就称为CGI程序。的文件名,例如“/dir1/file1.html”“/dir1/program1.cgi”等实际上,这个文件在Web服务器上未必是真实存在的,因为Web服务器可以通过重写规则对虚拟的URI进行映射。——译者注。不过,URI不仅限于此,也可以直接使用“http:”开头的URL5.4.3节有详细说明。来作为URI。换句话说就是,这里可以写各种访问目标,而这些访问目标统称为URI。

相当于接下来“进行怎样的操作”的部分称为方法也叫HTTP谓词,或者HTTP动词。——译者注。方法表示需要让Web服务器完成怎样的工作,其中典型的例子包括读取URI表示的数据、将客户端输入的数据发送给URI表示的程序等。表1.1列举了主要的方法,通过这张表大家应该能够理解通过方法可以执行怎样的操作。

表1.1 HTTP的主要方法

○:在该版本的规格中定义的项目。

△:并非正式规格,而是在规格书附录(Appendix)中定义的附加功能。

上述1.0版本和1.1版本的描述分别基于RFC1945和RFC2616。

除了图1.4中的内容之外,HTTP消息中还有一些用来表示附加信息的头字段。客户端向Web服务器发送数据时,会先发送头字段,然后再发送数据。不过,头字段属于可有可无的附加信息,因此我们留到后面再讲。

收到请求消息之后,Web服务器会对其中的内容进行解析,通过URI和方法来判断“对什么”“进行怎样的操作”,并根据这些要求来完成自己的工作,然后将结果存放在响应消息中。在响应消息的开头有一个状态码,它用来表示操作的执行结果是成功还是发生了错误。当我们访问Web服务器时,遇到找不到的文件就会显示出404 Not Found的错误信息,其实这就是状态码。状态码后面就是头字段和网页数据。响应消息会被发送回客户端,客户端收到之后,浏览器会从消息中读出所需的数据并显示在屏幕上。到这里,HTTP的整个工作就完成了。

现在大家应该已经了解了HTTP的全貌,下面我们再补充一些关于HTTP方法的知识。表1.1列出的方法中,最常用的一个就是GET方法了。一般当我们访问Web服务器获取网页数据时,使用的就是GET方法。所谓一般的访问过程大概就是这样的:首先,在请求消息中写上GET方法,然后在URI中写上存放网页数据的文件名“/dir1/file1.html”,这就表示我们需要获取/dir1/file1.html文件中的数据。当Web服务器收到消息后,会打开/dir1/file1.html文件并读取出里面的数据,然后将读出的数据存放到响应消息中,并返回给客户端。最后,客户端浏览器会收到这些数据并显示在屏幕上。

还有一个经常使用的方法就是POST。我们在表单表单:网页中的文本框、复选框等能够输入数据的部分。中填写数据并将其发送给Web服务器时就会使用这个方法。当我们在网上商城填写收货地址和姓名,或者是在网上填写问卷时,都会遇到带有输入框的网页,而这些可以输入信息的部分就是表单。使用POST方法时,URI会指向Web服务器中运行的一个应用程序用于处理购物订单数据或者问卷数据的程序。的文件名,典型的例子包括“index.cgi”“index. php”等。然后,在请求消息中,除了方法和URI之外,还要加上传递给应用程序和脚本的数据。这里的数据也就是用户在输入框里填写的信息。当服务器收到消息后,Web服务器会将请求消息中的数据发送给URI指定的应用程序。最后,Web服务器从应用程序接收输出的结果,会将它存放到响应消息中并返回给客户端。

前面两个方法属于HTTP的典型用法,除此之外的其他方法在互联网上几乎见不到使用的例子。因此,只要理解了这两个方法,就能够应付大部分情况了,但如果可以,还是推荐大家看一看表1.1中所有方法的说明,思考一下它们的含义,以便理解HTTP协议具备的所有功能。如果只有GET和POST方法,我们就只能从Web服务器中获取网页数据,以及将网页输入框中的信息发送给Web服务器,而有了PUT和DELETE方法,就能够从客户端修改或者删除Web服务器上的文件。有了这些功能,我们甚至可以将Web服务器当成文件服务器来用。当然,出于安全上的原因,或者是支持GET和POST之外的方法的客户端没有广泛普及之类的原因,一般我们并不会碰到这样的用法如果能够规避安全问题,例如将访问限制在公司内部网络,那么这种用法还是有效的。(实际上,PUT、DELETE等方法现在常用于RESTful API的设计中,在手机App和后端服务器交互时就会经常用到。——译者注),但大家应该能够看出,HTTP协议其实蕴藏着很多的可能性。

1.1.5 生成HTTP请求消息

理解了HTTP的基本知识之后,让我们回到对浏览器本身的探索中来。

对URL进行解析之后,浏览器确定了Web服务器和文件名,接下来就是根据这些信息来生成HTTP请求消息了。实际上,HTTP消息在格式上是有严格规定的,因此浏览器会按照规定的格式来生成请求消息(图1.5)。

首先,请求消息的第一行称为请求行。这里的重点是最开头的方法,方法可以告诉Web服务器它应该进行怎样的操作。不过这里必须先解决一个问题,那就是方法有很多种,我们必须先判断应该选用其中的哪一种。

解决这个问题的关键在于浏览器的工作状态。这次探索之旅是从在浏览器顶部的地址栏中输入网址开始的,但浏览器并非只有在这一种场景下才会向Web服务器发送请求消息。比如点击网页中的超级链接在HTML文档中写上<a href=“……”>标签,其中“……”部分为URL,这就是一个超级链接。,或者在表单中填写信息后点击“提交”按钮,这些场景都会触发浏览器的工作,而选用哪种方法也是根据场景来确定的。

图1.5 HTTP消息的格式

浏览器和Web服务器根据此格式来生成消息。

① 准确来说,消息体的格式会通过消息头中的Content-Type字段来定义(MIME类型),关于MIME类型在本书的6.4节有详细介绍。——译者注

我们的场景是在地址栏中输入网址并显示网页,因此这里应该使用GET方法。点击超级链接的场景中也是使用GET方法。如果是表单,在HTML源代码中会在表单的属性中指定使用哪种方法来发送请求,可能是GET也可能是POST(图1.6)GET方法能够发送的数据只有几百个字节,如果表单中的数据超过这一长度,则必须使用POST方法来发送。

图1.6 表单中对方法的区分

写好方法之后,加一个空格,然后写URI。URI部分的格式如下,一般是文件和程序的路径名。

      /<目录名>/…/<文件名>

前面已经讲过,路径名一般来说已经包含在URL中了,因此只要从URL中提取出来原封不动地写上去就好了。

第一行的末尾需要写上HTTP的版本号,这是为了表示该消息是基于哪个版本的HTTP规格编写的。到此为止,第一行就结束了。

第二行开始为消息头。尽管通过第一行我们就可以大致理解请求的内容,但有些情况下还需要一些额外的详细信息,而消息头的功能就是用来存放这些信息。消息头的规格中定义了很多项目,如日期、客户端支持的数据类型、语言、压缩格式、客户端和服务器的软件名称和版本、数据有效期和最后更新时间等。这些项目表示的都是非常细节的信息,因此要想准确理解这些信息的意思,就需要对HTTP协议有非常深入的了解。表1.2中列举了主要的头字段供大家参考,但不必全部弄明白。消息头中的内容随着浏览器类型、版本号、设置等的不同而不同,大多数情况下消息头的长度为几行到十几行不等。

写完消息头之后,还需要添加一个完全没有内容的空行,然后写上需要发送的数据。这一部分称为消息体,也就是消息的主体。不过,在使用GET方法的情况下,仅凭方法和URI, Web服务器就能够判断需要进行怎样的操作,因此消息体中不需要填写任何数据。消息体结束之后,整个消息也就结束了。

表1.2 HTTP中主要的头字段

(续)

○:在规格中定义的项目。

△:并非正式规格,而是在规格书附录(Appendix)中定义的附加功能。

① 这里的Challenge指的是Challenge-Response身份验证模型中的一环。简单来说,Challenge相当于“天王盖地虎”,Response相当于“宝塔镇河妖”。——译者注

当使用POST方法时,需要将表单中填写的信息写在消息体中。到此为止,请求消息的生成操作就全部完成了。

1.1.6 发送请求后会收到响应

当我们将上述请求消息发送出去之后,Web服务器会返回响应消息。关于响应消息我们将在第6章详细介绍,这里先粗略地了解一下。响应消息的格式以及基本思路和请求消息是相同的(图1.5(b)),差别只在第一行上。在响应消息中,第一行的内容为状态码和响应短语,用来表示请求的执行结果是成功还是出错。状态码和响应短语表示的内容一致,但它们的用途不同。状态码是一个数字,它主要用来向程序告知执行的结果(表1.3);相对地,响应短语则是一段文字,用来向人们告知执行的结果。

表1.3 HTTP状态码概要

返回响应消息之后,浏览器会将数据提取出来并显示在屏幕上,我们就能够看到网页的样子了。如果网页的内容只有文字,那么到这里就全部处理完毕了,但如果网页中还包括图片等资源,则还有下文。

当网页中包含图片时,会在网页中的相应位置嵌入表示图片文件的标签标签:编写网页所使用的HTML语言中规定的控制信息。例如,当需要在网页中插入图片时,需要在相应位置嵌入形如<img src=“image1.jpg”>的标签。的控制信息。浏览器会在显示文字时搜索相应的标签,当遇到图片相关的标签时,会在屏幕上留出用来显示图片的空间,然后再次访问Web服务器,按照标签中指定的文件名向Web服务器请求获取相应的图片并显示在预留的空间中。这个步骤和获取网页文件时一样,只要在URI部分写上图片的文件名并生成和发送请求消息就可以了。

由于每条请求消息中只能写1个URI,所以每次只能获取1个文件,如果需要获取多个文件,必须对每个文件单独发送1条请求。比如1个网页中包含3张图片,那么获取网页加上获取图片,一共需要向Web服务器发送4条请求。

判断所需的文件,然后获取这些文件并显示在屏幕上,这一系列工作的整体指挥也是浏览器的任务之一,而Web服务器却毫不知情。Web服务器完全不关心这4条请求获取的文件到底是1个网页上的还是不同网页上的,它的任务就是对每一条单独的请求返回1条响应而已。

到这里,我们已经介绍了浏览器与Web服务器进行交互的整个过程。作为参考,图1.7展示了浏览器与Web服务器之间交互消息的一个实例。在这个例子中,我们需要获取一张名为sample1.htm的网页,网页中包含一张名为picture.jpg的图片,图中展示了这个过程中产生的消息。

1条请求消息中只能写1个URI。如果需要获取多个文件,必须对每个文件单独发送1条请求。

图1.7 HTTP消息示例

图1.7 (续)

图1.7 (续)