1.1 万维网的原理
如今,互联网跟我们的生活密不可分。购物、金融活动、社交和娱乐,都依赖互联网。随着IoT(Internet of Things,物联网)的发展,越来越多的设备具备了上网能力,让人类可以远程控制和访问它们。这种远程控制和访问之所以可行,主要依赖几项技术,其中就包括HTTP(Hypertext Transfer Protocol,超文本传输协议)。HTTP是访问远程Web应用和资源的关键技术。尽管很多人都会使用浏览器上网,但真正理解这项技术的原理,以及为什么HTTP是万维网的核心,为什么其下一个版本(HTTP/2)如此引人瞩目的人就很少了。
1.1.1 因特网与万维网
对普通人来说,因特网(Internet)和万维网(World Wide Web或Web)是同义词,而搞清楚它们的区别是很重要的。
因特网是使用IP(Internet Protocol,因特网协议)连接在一起实现消息传递的计算机构成的网络。因特网上有很多服务,包括万维网,以及电子邮件、文件共享、因特网电话等。因此万维网(注意,是World Wide Web,简称Web)只是因特网上的一种服务形式,但其却是人们最常看到的形式,因为人们经常会通过Web应用(如Gmail、Hotmail和Yahoo!)来收发邮件。我们日常所说的上网,既可以理解为上万维网,也可以理解为上因特网。
HTTP是万维网浏览器或网页浏览器请求网页的协议。Tim Berners-Lee当初发明万维网时,一共创造了三项核心技术,除了传输数据的HTTP,还有用于标识唯一资源的URL(Uniform Resource Locator,统一资源定位符)和用于构造网页的HTML(Hypertext Markup Language,超文本标记语言)。因特网上的其他服务同样也使用自己的传输协议及标准,这些协议和标准定义了它们工作的方式,以及底层消息通过因特网传输的方式,例如用于电子邮件服务的SMTP、IMAP和POP。说到HTTP,其实主要是说万维网。但随着越来越多的服务甚至一些没有传统Web前端界面的服务开始使用HTTP,这个界限变得越来越模糊。因此要想清楚地给万维网下个定义也变得越来越不容易。我们刚才提到的这些服务(比如REST或SOAP)可以通过网页使用,也可以不通过网页使用(比如手机应用)。而IoT设备的原理简单来说,就是通过HTTP调用暴露服务,从而让其他设备(计算机、手机应用,乃至其他IoT设备)使用。比如,你可以在自己的手机应用中通过HTTP向家里的灯发出开或关的指令。
尽管因特网由众多服务构成,但其中很多服务所占份额越来越少,而万维网应用的份额却持续扩大。一些早期接触互联网的人肯定记得BBS或IRC,这些服务如今基本已退出历史舞台,被网页论坛、社交网站及聊天应用取代了。
如此看来,虽然万维网跟因特网经常被人们错误地混为一谈,但万维网(至少为它而发明的HTTP)的持续发展,很可能意味着用不了多久,其真的就可以代表因特网了。
1.1.2 打开网页时会发生什么
现在,让我们回到HTTP最初的设计目标:请求网页。当我们在浏览器中打开一个网站时,会发生很多事情,无论这个浏览器是在台式电脑、笔记本电脑、平板电脑、手机,还是在其他各种各样能上网的设备上。为理解本书内容,必须首先理解浏览器上网的工作原理。
假设我们打开浏览器访问www.google.com。在接下来的几秒钟内,会发生下面这些事情,如图1.1所示。
图1.1 浏览器请求网页时的典型交互过程
1. 浏览器根据DNS(Domain Name System,域名系统)服务器返回的真实地址请求网页,DNS主要负责把对人类友好的www.google.com转换为对机器友好的IP地址。
可以把IP地址想象成电话号码,把DNS想象成电话号码簿。IP地址有两种格式,老一点的是IPv4地址(比如26.58.192.4,人类还能用),新一点的是IPv6地址(比如2607:f8b0:4005:801:0:0:0:2004,这绝对只有机器才能用)。与电话区号会随着号源用尽而更换一样,IPv6也需要满足当下和未来联网设备激增的需求。
需要注意的是,由于因特网是全球性的,所以大公司通常会把服务器部署在世界各地。我们在向DNS查询IP地址时,通常会得到一个距离你最近的服务器的IP地址,以便你能快速访问。同样访问www.google.com,在美国的人和在欧洲的人很可能会得到不同的IP地址,这是很正常的。
怎么没有IPv5
IPv4(Internet Protocol version 4)被IPv6(version 6)取代,那有没有IPv5呢?另外,你听说过IPv1~IPv3吗?
IP包的前4位表示版本,理论上限是15个版本。在被广泛使用的IPv4以前,出现过0~3共4个实验性版本。但是,直到第4版,这几个版本一直没有被标准化(参见:https://tools.ietf.org/html/rfc760,这个协议后来被升级和替换了,见https://tools.ietf.org/html/rfc791)。此后,第5个版本被指定为Internet Stream Protocol,其主要在实时音频和视频流应用中使用,与VoIP(Voice over IP,IP语音)后来的发展类似。可是,这个版本一直没启用,原因之一就是存在与第4版一样的地址限制。后来第6版出来的时候,第5版的工作被叫停,于是IPv6就成了IPv4的后续版本。据说一开始由于人们以为6也被占用了,所以IPv6最早被称为第7版(参见https://archive.is/QqU73#selection-417.1-417.15)。版本7、8、9同样也被占用,而且以后也不会再使用了。假如IPv6还有后续版本,很可能会是IPv10或更高的版本。到时候,无疑还会导致与今天类似的疑惑。
2. 浏览器请求计算机建立对这个IP地址的标准Web端口(80)[1]或标准安全Web端口(443)的TCP(Transmission Control Protocol,传输控制协议)连接[2]。
IP用于直接通过因特网传输数据(这也是Internet Protocol这个名字的含义),而TCP增加了稳定性与重传机制以确保连接可靠(“嘿,你拿到了吗?”“没有啊,可以再发一次吗?”)。
因为这两种技术经常一起使用,通常也被称为TCP/IP,它们是因特网上运行的主要协议。
服务器可以提供多种服务(比如电子邮件、FTP、HTTP和HTTPS),而端口可以让不同服务共享同一个IP地址,这非常像公司的电话总机和分机。
3. 当浏览器连接到Web服务器之后会请求网站。这一步就要用到HTTP了,具体细节我们在下一节讨论。现在,只需知道浏览器会使用HTTP向Google的服务器请求Google主页就行了。
注意 此时此刻,你的浏览器会自动将简短的网址(www.google.com)扩展为语法上更为准确的URL地址(http://www.google.com)。而包含端口在内的完整URL应该是http://www.google.com:80,只不过在使用标准端口(80用于HTTP,443用于HTTPS)的情况下,浏览器会隐藏端口号。如果使用的是非标准端口,端口就会被显示出来。比如在某些环境特别是开发环境下,HTTP可以使用8080端口,HTTPS则可以使用8443端口。
如果使用的是HTTPS(1.4节将更详细地介绍HTTPS),还会采取额外的步骤进行安全验证,以确保连接安全。
4. Google服务器会根据请求的URL响应相关内容。一般来说,初始响应中包含HTML格式的网页文本。HTML是一种标准化、结构化的基于文本的格式,用于组织页面中的内容。通常要使用HTML标签将网页内容划分为很多区块,同时为了让用户看到丰富的媒体形式,还会引用其他资源(CSS样式表、JavaScript代码、图片、字体文件,等等)。
除了返回HTML页面,服务器还有可能返回一条指向不同位置的指令。比如,Google网站只提供HTTPS访问,因此在访问http://www.google.com时,响应通常是一条特殊的HTTP指令(通常是301或302响应码),将浏览器重定向到新的地址https://www.google.com。这种响应又会导致重复之前的部分或全部步骤,具体取决于新地址的服务器与端口变化多大,比如是不是仍然是同一台服务器上的不同端口(如重定向到HTTPS),当然还有可能会重定向到相同服务器与端口下的不同页面。
同样,如果这中间出了什么差错,你的浏览器又会收到一个HTTP响应码,比如最广为人知的错误响应码404 Not Found。
5. Web浏览器负责处理返回的响应。假设返回的响应是HTML,则浏览器就会解析HTML中的代码,并在内存中构建DOM(Document Object Model,文档对象模型),一种页面的内部表现形式。处理期间,浏览器可能会发现正常显示页面还需要其他资源(比如CSS、JavaScript和图片)。
6. Web浏览器请求自己需要的额外资源。Google的网页非常精简,在写作本书时,只包含16个别的资源。其中每个资源都需要以与上述1~5步类似的方式去请求,当然也包含这一步。因为在这些资源中也有可能引用其他资源。普通网页可没有Google网页那么精简,通常需要75个资源[3],而且往往会分布在多个域名下,而请求这其中每一个资源,都必须重复1~6步。这是导致上网慢的一个重要因素,因而催生了HTTP/2。HTTP/2的主要目的是使得请求多资源时更有效率,本书后续几章将陆续介绍。
7. 浏览器在获取了足够的关键资源后,开始在屏幕上渲染页面。但是,选择在屏幕上渲染的时机又是一个挑战,远没有听起来那么简单。如果浏览器等到所有资源都下载完毕才渲染,那么用户就要等很久才能看到网页,上网的体验会很差。相反,如果浏览器过早渲染页面,那么用户又会看到,伴随着更多资源下载,页面结构也会跳来跳去。假如你正在阅读一篇文章,而页面突然抖动了一下,你不生气才怪。充分理解Web技术,特别是HTTP和HTML/CSS/JavaScript,就会知道如何减少加载过程中的这种页面抖动。然而,有太多网站并没有优化好页面以应对这个问题。
8. 在页面刚刚显示在屏幕上之后,浏览器会在后台继续下载其他资源,并在处理完它们之后更新页面。这些资源包括不那么重要的图片和广告追踪脚本。因此,我们经常会看到网页刚显示时并没有图片(特别是在网速比较慢的情况下),而过了一会儿图片才慢慢下载并显示出来。
9. 当页面完全被加载后,浏览器会停止显示加载图标(在多数浏览器上都位于地址栏旁边),然后触发OnLoad JavaScript事件。根据这个事件,JavaScript就知道可以执行某些操作了。
10. 此时,页面已经完全加载了,但浏览器并不会停止发送请求。网页只包含静态内容的时代早就过去了。如今的很多网页其实已经是功能齐全的应用了,因此接下来浏览器还会与因特网上的各种浏览器打交道,发送或加载更多内容。这些内容有的来自用户输入,比如你在Google主页的搜索框中填写了一个关键字,但没有按“搜索”按钮就立即看到了搜索建议;有的来自应用驱动的操作,比如你的Facebook或者Twitter动态,不需要你点击“刷新”按钮就能自动刷新。这些操作通常在后面悄悄发生,你看不到它们,特别是广告分析脚本,它会跟踪你在网站上的操作,并将该信息发送给站长或者广告服务商。
如你所见,当你请求一个URL时会发生很多事情,而且这些事情通常发生在眨眼之间。上面这些步骤中的每一步都可以用一整本书来讲述,并且在不同场景下还有不同。本书主要关注(并且会深挖)第3~8步(通过HTTP加载网站)。后面有些章节也会简单介绍第2步(HTTP所使用的底层连接)的内容。