本文的目标是以“输入 URL 后发生了什么”这个经典面试题为引子,写一篇既可以涵盖面试中大部分网络试题,又可以将“输入 URL 后发生什么”讲得有深度的文章。之前写过一篇相似的文章,但实在过于简单。另外,HTTPS 逐渐普及,文章中没有这部分过程也说不过去。不想修改原来的文章,就从新写一篇吧。文中以我所在的项目“兴趣部落”的官网 https://buluo.qq.com/index.html 为例子。html
解析完要访问的目标服务器是啥了,接下来浏览器就会用 HTTP 协议生成请求消息去 web服务器请求资源,消息格式以下:web
请求信息主要包括:面试
对应的,响应消息也有 3 个部分组成:浏览器
用图表示:缓存
生成 HTTP 消息后,浏览器委托操做系统将消息发送给 web服务器。而经过 web服务器的名称是无法找到服务器在哪的,比如知道一我的的名字无法找到他家在哪同样,网络中的地址是用 IP 地址表示的,因此要想跟服务器通讯,得先找到它的 IP 地址,使用 DNS(Domain Name System,域名服务系统) 服务器能够将 web服务器名称转换成 IP 地址。那这个过程是怎样的呢?安全
操做系统有一个 Socket 库,这个库中的程序主要是让应用程序调用操做系统的网络功能,而在这些功能中,浏览器须要调取操做系统的 DNS 解析功能。DNS 解析器生成一条表示“告诉我 https://buluo.qq.com/index.html 的 IP 地址”的消息,而后委托操做系统的协议栈发送 UDP 消息到 DNS 服务器。那这条消息是如何发送到 DNS 服务器又是如何将 IP 地址返回的呢?服务器
首先介绍下操做系统中 DNS 解析器发送给 DNS 服务器的消息内容,消息中包含 1)域名:buluo.qq.com;2)Class: IN,表明当前的网络是因特网,DNS 设计之初还考虑了其余网络,虽然如今只有互联网,但这个字段仍是保留了下来;3)记录类型:A,表示域名对应的是 IP 地址,由于 DNS 还能解析其余地址,好比类型为 MX 时 DNS 服务器会查询邮件服务器地址。DNS 服务器中维护一张表,表的每一项包含上面三个字段还有服务器地址,当域名、Class、记录类型所有匹配时,DNS 服务器返回地址,在例子中会返回兴趣部落首页的 IP 地址。网络
但这个时候问题来了,世界上有不可胜数的服务器,将这些全部的服务器信息都保存在一个 DNS 的表中确定是不现实的,因此确定有不少台 DNS 服务器一块儿配合完成这个域名解析过程的,那具体过程是什么样的呢?并发
首先,DNS 服务器中的全部信息都是按照域名来划分层次的,这个层次是用 .
来分隔的,越靠右层次越高,好比 “buluo.qq.com” 中 “com” 层次最高,“qq” 次之,“buluo” 最后,其中每一层都被称为“域”,好比 “com 域”下是 “qq” 域,再下是 “buluo” 域,域的层次划分是为了更好地分配给不一样国家、公司和组织等,典型的例子像南京市政府的官网:“www.nanjing.gov.cn”,“cn” 表明中国这个国家的域,“gov” 表明这个国家下的政府组织,“nanjing” 表明南京市政府。域有层次之分,那 DNS 服务器呢?规定将管理下级域的 DNS 服务器的 IP地址注册到上级的 DNS 服务器中,好比管理 “buluo.qq.com” 这个域的 DNS 服务器的 IP地址须要注册到 “qq.com” 域的 DNS 服务器中,以此类推,一直到“根域”,就是 “cn”、“com” 这类域的上一层次,根域中就保存了 “cn”、“com” 等域名的 DNS 服务器信息。此外,还须要将根域的 DNS 服务器信息保存在全部的 DNS 服务器中,这样只要找到一台 DNS 服务器就能够顺藤摸瓜找到下层任何一个 DNS 服务器。知道了域的层次划分以及 DNS 服务器的分布,下面就正式介绍如何寻找到相应的 DNS 服务器并获取 IP 地址。dom
首先,客户端会访问最近的一台 DNS 服务器,但因为这台 DNS 服务器上没有 “buluo.qq.com” 这个域名的对应的信息,因此就向根域 DNS 服务器发请求询问,但根域中也没有,但断定这个域名是属于 “com” 域的,因此就返回其管理的 “com” 域的 DNS 服务器的 IP 地址,意思是“虽然我不知道,但你能够去某某处问问,他应该知道”。而后 最近的那个 DNS 服务器又向 “com” 域的 DNS 服务器发请求,同理,也不知道,而后返回 “qq.com” 域的 DNS 服务器,而后这台最近的 DNS 服务器又向 “qq.com” 域 DNS 服务器发请求,仍然没有,直到最后,向 “buluo.qq.com” 这个域下的 DNS 服务器发请求才拿到 IP 地址。接着,这台最近的 DNS 服务器将得到的 “buluo.qq.com” 的 IP 地址返回给客户端,客户端再拿着这个 IP 地址去请求资源。以上的过程用图表示以下:
以上就是经过 DNS 服务获取目标服务器 IP 地址的过程,能够说是很是耗时,为了优化性能,DNS 服务器会对中间的查询结果作个缓存,为了保存缓存的实时性,每隔一段时间就会将缓存设为过时。
如今客户端拿到了目标服务器的 IP 地址,下面就要与其链接并发送消息了,这个过程一样不是浏览器作的,而是委托协议栈来完成的,具体过程是:
在“委托协议栈发送消息”部分简单地提了下客户端和服务端利用套接字进行链接,那这个链接具体是什么样的呢?
首先什么是套接字?套接字其实就是个放在内存的备忘录,协议栈在发送数据时先看一眼备忘录,了解这个数据是发到哪一个端口,当数据发送出去后,这个备忘录还得记录什么时间收到响应、何时断开等控制信息,协议栈须要根据这些信息来决定下一步作什么。
客户端和服务端的链接是经过套接字链接的,那“链接”又是什么意思呢?链接其实是客户端和服务端互相交换控制信息的过程,控制信息主要包含两种,一种是上面提到的套接字里要来帮助协议栈进行下一步操做的信息,另外一种是客户端和服务端通讯时交换的控制信息,这种控制信息就是咱们俗称的 TCP 头部。 那链接的过程是怎样的呢?
这个链接过程就是咱们平时常常听到的三次握手。
整个过程用图表示以下:
上面的过程是最简单的 HTTP 三次握手,但如今愈来愈多的网站使用了 HTTPS 协议,那与 HTTP 链接有什么不一样呢?
先介绍一下什么是 HTTPS。HTTPS 正如其名字,HTTP 表明其并非本身建立一个新的协议,而是创建在 HTTP 的基础之上,S 表明其是安全的,如何保证安全?利用 SSL/TLS。SSL(Secure Sockets Layer,安全套接层)是网景设计的安全传输协议,经历了 1.0、2.0 和 3.0 版本,但由于 1.0 有严重安全缺陷,因此从未公布。后来 IETF 将 SSL 标准化,称为 TLS(Transport Layer Security, 传输层安全协议) ,TLS 1.0 与 SSL 3.0 差异很小。TLS 经历了 1.0、1.1 到如今最新的 1.2。在 HTTPS 通讯中具体使用哪种还要看客户端和服务端的支持程度。那 SSL/TLS 在网络模型中属于哪一层呢?直接上图:
在客户端和服务端经过 HTTPS 链接的过程当中,除了正常的 HTTP 链接中的事情,还有身份验证和加密信息两件事,下面看看具体过程(更详细内容能够查看标准:RFC5246)。
Client Hello:此次握手是客户端向服务端发起加密通讯请求,请求中包含如下关键信息:
Client Key Exchange Message:这一步很是关键,客户端会生成 premaster secret(预主密钥),为何叫 premaster secret?由于后面客户端和服务端会根据 premaster secret 和前面过程当中两个随机数共同生成一个 master secret(主密钥,48字节),后面通讯的安全全靠这个 master secret。前两个随机数客户端和服务端都知道了,这个步骤最主要的就是协商一个 premaster secret,这个过程叫作“密钥交换”,这里介绍两个方法:
用图表示一下就是:
整个握手过程总结一下就是:
以上就是握手的整个通讯细节,但细心的同窗可能会发现少了一个重要步骤,客户端收到服务器发来的证书时是如何断定对方就是本身想要找的服务器呢?这时候就要验证证书的有效性,证书就像现实中的身份证,能够确认某个网站的确是我要访问的网站。那怎么验证证书的有效性呢?首先,数字证书和身份证同样由权威机构签发,不一样的是身份证只能由政府签发,而数字证书由 CA(Certification Authorities,数字证书认证机构)签发,Mac 用户能够经过“文件-应用程序-实用工具-钥匙串访问”来查看根 CA,根 CA 能够签发其余 CA,因此一个网站的签发者不是根 CA 也不要紧,只要这个 CA 的签发者是根 CA 也行。了解了 CA,下面看一下证书包含什么,先看图:
证书中包含:网站的基本信息、网站的公钥、CA 的名字等信息(详细请看 X.509),而后 CA 根据这几个内容生成摘要(digest),再对摘要用 CA 的私钥加密,加密后的结果即数字签名,最后将数字签名也放入到证书中。那么当系统收到一个证书后,先用公钥解密,解得开说明对方是由权威 CA 签发的,而后再根据证书的信息生成摘要,跟解密出来的摘要对比。
创建链接以后,客户端和服务端即可以开始进行数据传输。一样,浏览器委托协议栈来帮忙收发消息,协议栈收到消息后不会当即发送出去,而是先放入到缓存区中,由于向协议栈发送的数据长度由浏览器控制,若是协议栈一收到数据就发送出去,那么可能会发送大量小包,致使网络效率下降,因此协议栈通常会等数据量积累到必定程度再发送出去,那这个程度具体是啥样?
首先,在以太网中,一个包的MTU(Maximum Transmission Unit,最大传输单元)是 1500 字节,除去 TCP、IP 头部的 40字节,MSS(Maximum Segment Size,最大分段大小)就是 1460 字节,但由于加密须要,头部可能会增长,相对的 MSS 就会减小。当缓存区内的数据接近 MSS 时再发送,能够避免发送小包。可是若是数据量原本就很小,或者应用程序发送数据的频率很小,那协议栈就不得不等很长时间,因此协议栈内部还有一个定时器,必定时间以后就会将包发送出去。若是数据较小,那就几个拼个车,放在一个包里发出去,若是数据很大,就要进行拆分。大概是下面这样:
本地一切就绪以后,协议栈就会将消息发送出去,这时还没完,客户端还要确保服务器收到了消息。咱们一直都说 TCP 是面向链接的协议,由于它能够纠正丢包错误、链接失败提示等等,使得传输更加可靠。那具体又是怎么样的呢?
首先 TCP 模块在拆分数据时会先算好每一块数据至关于从头开始是第几个字节,而后将这个数字写入到 TCP 头部的“序号”字段中,经过这个字段,接收方就能知道包有没有丢失,好比一个消息长度为 4380(1460 * 3),那么这条消息就被拆分到三个数据块中,三个数据块的 TCP 头部的“序号”依次是 0、1460 和 2920,因此接收方先收到一个序号为 0 的包,再收到一个序号为 2920 的包,可是没收到序号为 1460 的包,说明这个包丢失了,现实中的序号为了安全不会从 0 开始,而是以一个随机数做为初始值。若是确认没有遗漏,那么接收方会将到目前为止收到的数据长度加起来,写入 TCP 的 “ACK 号”中发送给对方,注意 “ACK 号”与 ACK 标记位不是一回事,前者是数字,后者就是一个比特的标记位,可是 “ACK 号”只有在 ACK 标记位为 1 是才有效。
当数据发送完毕后,一方(多是客户端,多是服务端)就会发起断开链接过程。这个过程也是你们很熟悉的,即四次挥手。下面以客户端发起断开请求为例:
以上就是输入 URL 后大概发生的一些事情,可是从面试角度看,仍然还有不少部分没有涉及。后续还会继续更新这篇文章,添加一些重要内容,这里先挖个坑:
好,坑就挖这么多,再多怕本身不想填,等填完再继续挖。