认识Web浏览器
——《网络是怎样连接的》读后笔记(一)
基础引入
例如一串网址“https://www.baidu.com”。
- www是Web服务器上的一种命名,World Wide Web 是 Web 的提出者最早开发的浏览器兼 HTML 编辑器的名字。(不是协议的名字)
- 在中国,个人可以申请“
.cn
”的域名,但“.com.cn
”“.net.cn
”等域名则是不开放给个人注册的。中国使用的是三个字母的命名,例如“.com.cn
”“.net.cn
”。 - 应用程序并不是自己去控制网络,而是委托操作系统来控制网络。
生成HTTP请求信息
从用户在浏览器中输入网址(URL)开始。接下来,浏览器的工作会从对用户输入的网址进行解析开始。浏览器如何解析网址就是我们的第一个看点。然后,浏览器会根据网址的含义来生成请求消息。 浏览器通过请求消息将用户需要哪些数据告知服务器,而请求消息实际的样子就是我们的第二个看点。只要理解了具体的消息长什么样,我们也就能够理解访问 Web 服务器时使用的 HTTP 协议的原理了。
输入网址
- 网址:URL(Uniform Resource Locator,统一资源定位符)
注:URL除了会以http:
开头,还有以"ftp:
" “file:
” “mailto:
”
因为浏览器是一个具备多种客户端功能的综合性客户端软件,因此它需要一些东西来判断应该使用其中哪种功能来访问相应的数据,而各种不同的 URL就是用来干这个的, 此时:
访问 Web 服务器时用“http:”,而访问FTP服务器时用“ftp:”。
重点:
尽管 URL 有各种不同的写法,但它们有一个共同点,那就是 URL 开头的文字,这部分文字都表示浏览器应当使用的访问方法。比如当访问 Web 服务器时应该使用HTTP 协议,而访问 FTP 服务器时则应该使用FTP 协议。因此,我们可以把这部分理解为访问时使用的协议 类型 。尽管后面部分的写法各不相同,但开头部分的内容决定了后面部分的写法,因此并不会造成混乱。
解析URL(以访问Web服务器为例)
由上图为例:
在解析时会按照(a)的格式将URL中各个元素拆分出来,然后提取信息。
如(c)图URL知道,要访问www.lab.glasscom.com
这个 Web 服务器上路径名为 /dir/file1.html
的文 件,也就是位于 /dir/
目录(文件夹) 下的 file1.html
这个文件。
注:像file:
这种URL在访问时不需要网络,故可以说URL的开头部分是表面其中的访问方法。
省略文件名的情况:
(1)http://www.lab.glasscom.com/dir/
这种以“/”结尾即代表/dir/后面本来应该有的文件名被省略了。
没有文件名,服务器怎么知道要访问哪个文件呢?
我们会在服务器上事先设置好文件名省略时 要访问的默认文件名。这个设置根据服务器不同而不同,大多数情况下是 index.html 或者 default.htm 之类的 文件名。因此,像前面这样省略文件名时,服务器就会访问/dir/index.html
或者/dir/default.htm
。
(2)http://www.lab.glasscom.com/
像这种连目录都省略的。结果就是访问 /index.html 或者 /default.htm 这样的文件。且,“/”目录是目录中最顶层的“根目录”
(3)http://www.lab.glasscom.com
像这种没有目录名的就代表访问根目录下事先设置的默认文件 ,也就是 /index.html
或者 /default.htm
这些文件
(4)http://www.lab.glasscom.com/whatisthis
如果 Web 服务器上存在名为 whatisthis 的 文件,则将 whatisthis 作为文件名来处理;如果存在名为 whatisthis 的目录,则将 whatisthis 作为目录名来处理。
HTTP协议
图解:
上图1中客户端会向服务器发送请求消息,其包括“对什么”和“进行什么操作”,对于“对什么”就是URI
(Uniform Resource Identifier,统一资源标识符,其内容是是一个存放网页数据的文件名或者是一个 CGI (就是对 Web 服务器程序调用其他程序的规则所做的定义)程序的文件名,例如“
/dir1/file1.html
”“/dir1/program1.cgi
”等 。不过,URI 不仅限于此,也可以直接使 用“http:”开头的 URL 来作为 URI。换句话说就是,这里可以写各种访问目标,而这些访问目标统称为 URI。
)
“进行什么操作”就是方法,或者叫HTTP谓词(例如读取 URI 表示的数据、将客户端输入的数据发送给 URI 表示的程序)
补充:对于这些方法:
(1)GET 方法 :一般的访问过程大概就是这样的:首先,在请求消息中写上 GET 方法,然后在 URI 中写上存放网 页数据的文件名“/dir1/file1.htm
l”,这就表示我们需要获取/dir1/file1.html
文件中的数据。当 Web 服务器收到 消息后,会打开/dir1/file1.html
文件并读取出里面的数据,然后将读出的数据存放到响应消息中,并返回给 客户端。最后,客户端浏览器会收到这些数据并显示在屏幕上。
(2)POST方法:使用 POST 方法时,URI 会指向 Web 服务器中运行的一个应用程序 的文件名,典型的例子包括“index.cgi
”“index.php
”等。在表单中填写数据并将其发送给 Web 服务器时就会使用这个方法。
Web在对URI和方法的解析后了解自己的工作,运行并将结果存放在响应中,且响应的开头会有一个状态码,(例如遇到找不到的文件就 会显示出 404 Not Found 的错误信息),然后响应消息发送到客户端,浏览器再读出所需数据并显示在屏幕。
生成HTTP请求消息
HTTP消息在生成时有严格规定:
解释:(a)请求行的重点是”方法“,通过当时浏览器的工作状态判断选用的方法。例如
我们的场景是在地址栏中输入网址并显示网页,因此这里应该使用 GET 方法。点击超级链接的场景中也是 使用 GET 方法。如果是表单,在
HTML 源代码中会在表单的属性中指定使用哪种方法来发送请求,可能是 GET 也可能是 POST(图 1.6)
其中的写法是/< 目录名 >/…/< 文件名 >
第二行开始叫做“消息头”,对于消息头的规格定义了很多项目,如日期、客户端支持的数据类型、语言、压缩格式、客户端和服务器的软件名称和版本、数据有效期和最后更新时间等。这些都有表可查。
最后是“消息体”,注意:
在使用 GET 方法的情况下,仅凭方法和 URI,Web 服务器就能够判断需要进行怎样的操作,因此消息体中不需要填写任何数据。
当使用 POST 方法时,需要将表单中填写的信息写在消息体中。
发送请求后接受响应
与请求消息的区别在于第一行。
第一行的内容为状态码和响应短语,用来表示请求的执行结果是成功还是出错。 状态码和响应短语表示的内容一致,但它们的用途不同。状态码是一个数字,它主要用来向程序告知执行的 结果(表 1.3);相对地,响应短语则是一段文字,用来向人们告知执行的结果。
此时注意:
1 条请求消息中只能写 1 个 URI。如果需要获取多个文件,必须对每个文件单独发送 1 条请求。
解析器与DNS服务器的交互
IP地址介绍
- 为了将生成的消息发送到网络,需要委托操作系统来实现。而且前提是提供其通信对象的IP地址。
- 在了解了TCP/IP协议(由一些小的子网,通过路由器连接起来组成一个大的网络。这里的子网可以理解为用集线器连接起来的几台计算机 ,我们将它看作一个单位,称为子网。将子网通过路由器连接起来,就形成了一个网络)后,
- 在网络中的所有设备会被分配一个地址——相当于现实中某条路上的“×× 号 ×× 室”
- 其中“号”对应的号码是分配给整个子网的,而“室”对应的号码是分配给子网中的计算机的,这就是网络中的地址。
“号”对应的号码称为网络号,“室”对应的号码称为主机号,这个地址的整体称为IP地址。
在TCP/IP中消息IP的发送:
发送者发出的消息首先经过子网中的集线器 (这里的数据以包的形式传递) ,转发到距离发 送者最近的路由器上(图 1.8 ①)。接下来,路由器会根据消息的目的地判断下一个路由器的位置,然后将 消息发送到下一个路由器,即消息再次经过子网内的集线器被转发到下一个路由器(图 1.8 ②)。前面的过 程不断重复,最终消息就被传送到了目的地。
- 实际IP地址的解释:
即一串32bit的数字,按照 8 比特(1字节)为一组分成4组,分别用十进制 表示然后再用圆点隔开。这就是我们平常经常见到的 IP地址格式,
在 IP 地址的规则中,网络号和主机号连起来总共是 32 比特,但这两部分的具体结构是不固定的。
在组建网络时,用户可以自行决定它们之间的分配关系
,因此,我们还需要另外的附加 信息来表示 IP 地址的内部结构。
这里的附加信息就是“子网掩码“
它是一串与 IP 地址长度相同的32bit数字,其左边一半都是1,右边一半都是 0。其中,子网掩码为 1 的部分表示网络号,子网掩码为 0 的部分表示主机号。
将子网掩码按照和 IP 地址一样的方式以每 8 比特为单位用圆点分组后写在 IP 地址的右侧,这就 是图 1.9(b)的方法。
这种写法太长,我们也可以把 1 的部分的比特数用十进制表示并写在 IP 地址的右 侧,如图 1.9(c) 所示。这两种方式只是写法上的区别,含义是完全一样的。
注:主机号部分的比特全部为 0 或者全部为 1 时代表两种特殊的含义。主机号部分全部为 0 代表整个子网而不是子网中的某台设备(图 1.9(d))。此外,主机号部分全部为 1 代表向子网上所有设备发送包, 即广播(图 1.9(e))。
IP 地址的主机号
全 0:表示整个子网
全 1:表示向子网上所有设备发送包,即“广播”
域名和 IP 地址并用的理由
由上文,我们知道TCP/IP网络是通过IP地址来确定通信对象,
现在我们使用的方案是让人来使用名称,让路由器来使用 IP 地址。为了填补两者之间的障碍,需要 有一个机制能够通过名称来查询 IP 地址,或者通过 IP 地址来查询名称,这样就能够在人和机器双方都不做 出牺牲的前提下完美地解决问题。这个机制就是 DNS 。
Domain Name System,域名服务系统。将服务器名称和 IP 地址进行关联是 DNS 最常见的用法,但 DNS 的功能并不仅限于此,它还 可以将邮件地址和邮件服务器进行关联,以及为各种信息关联相应的名称。
Socket库提供查询IP地址的功能
Socket 库是用于调用网络功能的程序组件集合。
查询 IP 地址的方法非常简单,只要询问最近的 DNS 服务器“www.lab.glasscom.com 的 IP 地址是什么”就可以 了,DNS 服务器会回答说“该服务器的 IP 地址为 xxx.xxx.xxx.xxx”。这一步非常简单,那么浏览器是如何向 DNS 服务器发出查询的呢?
因此介绍DNS
使用DBS时,就是向DNS服务器发送查询消息,接受服务器返回的响应消息,因此在我们计算机上一定有与之相应的DNS客户端,我们称为DNS解析器。——通过DNS查询IP地址称为域名解析。
解析器是一段程序,包含在操作系统的Socket库中。
库就是一堆通用程序组件的集合,其应用程序都需要使用其中的组件。
库的好处:
首先,使用现成的组件搭建应用程序可以节省编程工作量;其次,多个程序使用相 同的组件可以实现程序的标准化。除此之外还有很多其他的好处,因此使用库来进行软件开发的思路已经非常普及。
库的种类和数量也非常之多。
Socket 库也是一种库,其中包含的程序组件可以让其他的应用程序调用操作系统的网络功能,而解析器就是这个库中的其中一种程序组件。
Socket库中包含很多用于发送和接受数据的程序组件。
发出查询
通过解析器向DNS服务器发出查询。
至于解析器的用法——在Socket库中直接调用。
- 运行该程序,IP地址就写入了浏览器指定的内存地址中
- 浏览器向Web服务器发送消息(从该内存地址中取出IP地址,并将它与 HTTP 请求消息一起交给操作系统)
解析器
即调用解析器时,解析器内部的工作
图解:
- 程序的控制流程转移到解析器
一般来说,应用程序编写的操作内容是从上往下按顺序执行的,当到达需要调用解析器的部分时,对应的那 一行程序就会被执行,应用程序本身的工作就会暂停(图 1.12 ①)。然后,Socket 库中的解析器开始运行 (图 1.12 ②),完成应用程序委托的操作。像这样,由于调用了其他程序,原本运行的程序进入暂停状 态,而被调用的程序开始运行,这就是“控制流程转移”
- 解析器生成发生过DNS服务器的查询消息
与浏览器生成要发 送给 Web 服务器的 HTTP 请求消息的过程类似,
解析器会根据 DNS 的规格,生成一条表示“请告诉我www.lab.glasscom.com 的 IP 地址”的数据,
并将它发送给 DNS 服务器(图 1.12 ③)。
发送消息这个操作并不是由解析器自身来执行,而是要委托给操作系统内部的协议栈 (操作系统内部的网络控制软件,也叫“协议驱动”“TCP/IP 驱动”等。)来执行。
因为和浏览器一样,解析器本身也不具备使用网络收发数据的功能。
但两者在消息形式还是有所差别:
HTTP 消息是用文本编写的,但 DNS 消息是使用二进制数据编写的。
解析器调用协议栈后,控制流程会再次转移,协议栈会执行发送消息的操作,然后通过网卡将消息发送给 DNS 服务器(图 1.12 ④⑤)。
- DNS服务器收到后,按照查询内容进行查询。
DNS服务器
DNS服务器的基本工作
简而言之:接收来自客户端的查询消息,然后根据消息内容返回响应。
查询消息包括三种信息:
(a)域名
服务器、邮件服务器(邮件地址中 @ 后面的部分)的名称
(b)Class
在最早设计 DNS 方案时,DNS 在互联网以外的其他网络中的应用也被考虑到了,而 Class 就是用来识 别网络的信息。不过,如今除了互联网并没有其他的网络了,因此 Class 的值永远是代表互联网的 IN
(c)记录类型
表示域名对应何种类型的记录。例如,当类型为 A 时,表示域名对应的是 IP 地址;当类型为 MX 时,表示域名对应的是邮件服务器。对于不同的记录类型,服务器向客户端返回的信息也会不同.
图解:
对记录类型的解释:
A类型:Address,且只要是作为 A记录在DNS服务器上注册的,都可以作为 Web 服务器的域名 。
MX类型:Mail eXchange,查询邮件服务器
PTR 类型: 根据 IP 地址反查域名
CNAME 类型:查询域名相关别名
NS 类型: 查询 DNS 服务器 IP 地址
SOA 类型:查询域名属性信息
在 DNS服务器上,IP 地址是保存在 A 记录中的,而邮件服务器则是保存在 MX 记录中。
综上所述,
DNS 服务器的基本工作就是根据需要查询的域名和记录类型查找相关的记录,并向客户端返回 响应消息。
DNS 服务器会从域名与 IP地址的对照表中查找相应的记录,并返回 IP 地址。
域名的解释
DNS服务器中的所有信息是按照域名以分层次的结构来保存的。(层次结构可以更好的管理大量信息)
细节:
- DNS的域名是用句点来分隔,越靠右的位置代表其层级越高。
- 一个层级的部分叫做域。
- 这种层级结构的信息会注册到DNS服务器中,每个域都是作为一个整体来处理的。
- DNS服务器与域之间关系不全是一对一,可以说一对多(即一台DNS服务器中有多个域的信息)但绝不可能是多对一(因为域是不可以拆开的)
- 因此DNS服务器也有层级关系,某一层级的域存放在相应层级的DNS服务器中。
寻找相应的 DNS 服务器并获取 IP 地址
关键在于:如何找到与之对应的要访问的 Web 服务器的信息归哪一台 DNS 服务器管。
寻找方法:
首先,将负责管理下级域的 DNS 服务器的 IP 地址注册到它们的上级 DNS 服务器中,然后上级 DNS 服务器的 IP 地址再注 册到更上一级的 DNS 服务器中,以此类推。
注:1.顶级域(例如com,cn)的上面还有一级域,称为根域。
(但一般可以省略,如果要明确表示根域,可以在域名的最后加上一个句点)
2. 将根域的 DNS 服务器信息保存在互联网中所有的 DNS 服务器中。
(这样一来,任何 DNS 服务器就都可以找到并访问根域 DNS 服务器了。)
分配给根域 DNS 服务器的 IP 地址在全世界仅有 13 个 ,而且这些地址几乎不发生变 化,因此将这些地址保存在所有的 DNS 服务器中也并不是一件难事。实际上,根域 DNS 服务器的相关信息 已经包含在 DNS 服务器程序的配置文件中了,因此只要安装了 DNS 服务器程序,这些信息也就被自动配置好了。
图解——找到目标DNS服务器
通过缓存加快DNS服务器的响应
在现实互联网中,为了节省效率,一台服务器可以管理多个域的信息,有可能上图的上下级域都共享着同一台DNS服务器
因为 DNS 服务器有一个缓存功能,可以记住之前查询过的域名。
1.如果要查询的域名和相关信息已经在缓存中,那么就可以直接返回响应,接下来的查询可以从缓存的位置开始向下进行。相比每次都从根域找起来说,缓存可以减少查询所需的时间。
2.当要查询的域名不存在时,“不存在”这一响应结果也会被缓存。这样,当下次查询这个不存在的域名 时,也可以快速响应。
3.缓存:指的是将使用过的数据存放在离使用该数据的地方较近的高速存储装置中,以便提高后续访问速度的技术。这一技术有很多应用,如 CPU 和内存之间的缓存、磁盘和内存之间的缓存等,在网络中缓存也是一种用来提高访问速度的普遍性技术。
委托协议栈发送消息(数字信息)
数据收发
- 收发数字信息的过程和向DNS服务器查询IP地址的操作都适用于任何网络应用程序。
- 需要按照指定顺序调用Socket库中的多个程序组件
- 图解:
- 因此可以把数据通道类比成管道:
(1)建立管道:
理论上关键在两端的数据出入口——套接字,再将套接字连接形成管道。
实际中服务器创建套接字,等待客户端连接套接字形成管道,客户端也创建套接字,继而延申出管道最终连接在服务器的套接字上。
(2)操作结束
管道断开,可以由客户端或服务器任意一方发起,断开后删除套接字。
(3)整个阶段都是由操作系统的协议栈完成
创建套接字(创建套接字阶段)
将管道连接到服务器端的套接字上(连接阶段)
收发数据(通信阶段)
断开管道并删除套接字(断开阶段)
创建套接字
1.即要调用 Socket 库中的 socket 程序组件。
2.当有多个套接字时,应用程序是通过“描述符”这一类似号码牌的东西来识别套接字的。
连接管道
应用程序通过调用 Socket 库中的名为 connect 的程序组件来完成这一操作。这里的要点是当调用 connect 时,需要指定描述 符、服务器 IP 地址和端口号这 3 个参数。
三个参数:
第 1 个参数,即描述符,
-. 就是在创建套接字的时候由协议栈返回的那个描述符。
-. connect 会将应用程序指定的描述符告知协议栈,然后协议栈根据这个描述符来判断到底使用哪一个套接字去和服务器端的套接字进行 连接,并执行连接的操作 。
-. 当调用 Socket 库中的程序组件时,应用程序所指定的参数会通过 Socket 库的程序组件传递给协议栈,并由协议栈来实际执行相应的操作。
第 2 个参数,即服务器 IP 地址,
-. 通过 DNS 服务器查询得到的我们要访问的服务器的 IP 地址。
第 3 个参数,即端口号
-. 如果连接操作的对象是某个具体的套接字,必须要识别到具体的套接字。
-. 端口号就是这样一种方式。当同时指定 IP 地址和端口号时,就可以明 确识别出某台具体的计算机上的某个具体的套接字。
服务器上所使用的端口号是根据应用的种类事先规定好的,仅此而已。比如 Web 是 80 号端口,电子邮件是 25 号端口。
传递消息
发送消息通过 Socket 库委托协议栈使用 write 这个程序组件。
接收消息通过 Socket 库中的 read 程序组件委托协议栈来完成的。
- write
应用程序需要在内存中准备好要发送的数据。根据用户输入的网址生成的 HTTP 请求消息。当调用 write 时,指定描述符和发送数据(图 1.18 ③),协议栈再将数据发送到服务器。
由于套接字中已经保存了已连接的通信对象的相关信息,所以只要通过描述符指定套接字, 就可以识别出通信对象,并向其发送数据。
发送数据会通过网络到达我们要访问的服务器。 服务器执行接收操作,解析收到的数据内容并执行相应的操作,向客户端返回响应消息 。
- read
调用 read 时需要指定用于存放接收到的响应消息的内存地址,这一内存地址称为接收缓冲区。
当服务器返回响应消息时,read 负责将接收到的响应消息存放到接收缓冲区中。
注:由于接收缓冲区是一块位于应用程序内部的内存空间,因此当消息被存放到接收缓冲区中时,就相当于已经转交给了应用程序。
断开管道
即调用 Socket 库的 close 程序组件进入 断开阶段(图 1.18 ④)。
连接在套接字之间的管道会被断开,套接字本身也会被删除。
断开的过程:
Web 使用的 HTTP 协议规定,当 Web 服务器发送完响应消息之后,应该主动执行断开操作 ,因此 Web 服务器会首先调用 close 来断开连接。断开操作传达到客户端之后,客户端的套接字也会进入断开阶段。接下来,当浏览器调用 read 执行接收数据操作时,read 会告知浏览器收发数据操作已结束,连接已经断开。浏览器得知后,也会调用 close 进入断开阶段。