在咱们点击一个网址,到它可以呈如今浏览器中,展现在咱们面前,这个过程当中,电脑里,网络上,究竟发生了什么事情。php
那咱们就开始了,故事其实并非从在浏览器的地址栏输入一个网址,或者咱们抓着鼠标点击一个连接开始,事情的开端要追溯到服务器启动监听服务的时候,在某个未知的时刻,一台机房里普普统统的刀片服务器,加上电,启动了操做系统,随着操做系统的就绪,服务器启动了 http 服务进程,这个 http 服务的守护进程,(daemon),多是 apache,也多是 nginx,无论怎么说,这个 http 服务进程开始定位到服务器上的 www 文件夹,通常是位于 /var/www ,而后启动了一些附属的模块,例如 php,或者,使用 fastcgi 方式链接到 php 的 fpm 管理进程,而后,向操做系统申请了一个 tcp 链接,而后绑定在了 80 端口,调用了 accept 函数,开始了默默的监听,监听着可能来自位于地球任何一个地方的请求,随时准备作出响应。css
这个时候,典型的状况下,机房里面应该还有一个数据库服务器,或许,还有一台缓存服务器,若是对于流量巨大的网站,那么动态脚本的解释器可能还有单独的物理机器来跑,若是是中小的站点,那么,上述的各色服务,甚至均可能在一台物理机上,这些服务监听之间的关系。无论怎么说,他们作好了准备,静候差遣。html
当协议或主机名不合法时,浏览器会将地址栏中输入的文字传给默认的搜索引擎。大部分状况下,在把文字传递给搜索引擎的时候,URL会带有特定的一串字符,用来告诉搜索引擎此次搜索来自这个特定浏览器。mysql
这里主机名是 google.com ,因此没有非ASCII的字符;若是有的话,浏览器会对主机名部分使用 Punycode 编码nginx
浏览器检查自带的“预加载 HSTS(HTTP严格传输安全)”列表,这个列表里包含了那些请求浏览器只使用HTTPS进行链接的网站算法
要想发送 ARP(地址解析协议)广播,咱们须要有一个目标 IP 地址,同时还须要知道用于发送 ARP 广播的接口的 MAC 地址。sql
首先查询 ARP 缓存,若是缓存命中,咱们返回结果:目标 IP = MAC
若是缓存没有命中:chrome
查看路由表,看看目标 IP 地址是否是在本地路由表中的某个子网内。是的话,使用跟那个子网相连的接口,不然使用与默认网关相连的接口。数据库
咱们发送一个二层( OSI 模型 中的数据链路层)ARP 请求:apache
1
2
3
4
|
Sender MAC: interface:mac:address:here
Sender IP: interface.ip.goes.here
Target MAC: FF:FF:FF:FF:FF:FF (Broadcast)
Target IP: target.ip.goes.here
|
根据链接主机和路由器的硬件类型不一样,能够分为如下几种状况:
若是咱们链接到一个集线器,集线器会把 ARP 请求向全部其它端口广播,若是路由器也“链接”在其中,它会返回一个 ARP Reply 。
交换机:
若是咱们链接到了一个交换机,交换机会检查本地 CAM/MAC 表,看看哪一个端口有咱们要找的那个 MAC 地址,若是没有找到,交换机会向全部其它端口广播这个 ARP 请求。
1
2
3
4
|
Sender MAC: target:mac:address:here
Sender IP: target.ip.goes.here
Target MAC: interface:mac:address:here
Target IP: interface.ip.goes.here
|
如今咱们有了 DNS 服务器或者默认网关的 IP 地址,咱们能够继续 DNS 请求了:
当浏览器获得了目标服务器的 IP 地址,以及 URL 中给出来端口号(http 协议默认端口号是 80, https 默认端口号是 443),它会调用系统库函数 socket ,请求一个 TCP流套接字,对应的参数是 AF_INET/AF_INET6 和 SOCK_STREAM 。
到了如今,TCP 封包已经准备好了,能够进行tcp三次握手(在地址解析完毕后首先进行的是三次握手,就是客户端和服务器端只发送SYN包,创建三次链接后,在对HTTP的引用进行响应)
最终封包会到达管理本地子网的路由器。在那里出发,它会继续通过自治区域(autonomous system, 缩写 AS)的边界路由器,其余自治区域,最终到达目标服务器。一路上通过的这些路由器会从IP数据报头部里提取出目标地址,并将封包正确地路由到下一个目的地。IP数据报头部 time to live (TTL) 域的值每通过一个路由器就减1,若是封包的TTL变为0,或者路由器因为网络拥堵等缘由封包队列满了,那么这个包会被路由器丢弃。
客户端发送一个 Client hello 消息到服务器端,消息中同时包含了它的 Transport Layer Security (TLS) 版本,可用的加密算法和压缩算法。
服务器端向客户端返回一个 Server hello 消息,消息中包含了服务器端的TLS版本,服务器选择了哪一个加密和压缩算法,以及服务器的公开证书,证书中包含了公钥。客户端会使用这个公钥加密接下来的握手过程,直到协商生成一个新的对称密钥
HTTPD(HTTP Daemon)在服务器端处理请求/响应。最多见的 HTTPD 有 Linux 上经常使用的 Apache 和 nginx,以及 Windows 上的 IIS。
请求进入处理函数以后,若是客户端所请求须要浏览的内容是一个动态的内容,那么处理函数会相应的从数据源里面取出数据,这个地方通常会有一个缓存,例如 memcached 来减少 db 的压力,若是引入了 orm 框架的话,那么处理函数直接向 orm 框架索要数据就能够了,由 orm 框架来决定是使用内存里面的缓存仍是从 db 去取数据,通常缓存都会有一个过时的时间,而 orm 框架也会在取到数据回来以后,把数据存一份在内存缓存中的。
orm 框架负责把面向对象的请求翻译成标准的 sql 语句,而后送到后端的 db 去执行,db 这里以 mysql 为例的话,那么一条 sql 进来以后,db 自己也是有缓存的,不过 db 的缓存通常是用 sql 语言 hash 来存取的,也就是说,想要缓存可以命中,除了查询的字段和方法要同样之外,查询的参数也要彻底如出一辙才可以使用 db 自己的查询缓存,sql 通过查询缓存器,而后就会到达查询分析器,在这里,db 会根据被搜索的数据表的索引创建状况,和 sql 语言自己的特色,来决定使用哪个字段的索引,值得一提的是,即便一个数据表同时在多个字段创建了索引,可是对于一条 sql 语句来讲,仍是只能使用一个索引,因此这里就须要分析使用哪一个索引效率最高了,通常来讲,sql 优化在这个点上也是很重要的一个方面。
sql 由 db 返回结果集后,再由 orm 框架把结果转换成模型对象,而后由 orm 框架进行一些逻辑处理,把准备好的数据,送到视图层的渲染引擎去渲染,渲染引擎负责模板的管理,字段的友好显示,也包括负责一些多国语言之类的任务。对于一条请求在 mvc 中的生命周期,在视图层把页面准备好后,再从动态脚本解释器送回到 http 服务器,由 http 服务器把这些正文加上一个响应头,封装成一个标准的 http 响应包,再经过 tcp ip 协议,送回到客户机浏览器。
当浏览获得一个正确的 200 响应以后,接下来面临的一个问题就是多国语言的编码解析了,响应头是一个 ascii 的标准字符集的文本,这个还好办,可是响应的正文本质上就是一个字节流,对于这一坨字节流,浏览器要怎么去处理呢,首先浏览器会去看响应头里面指定的 encoding 域,若是有了这个东西,那么就按照指定的 encoding 去解析字符,若是没有的话,那么浏览器会使用一些比较智能的方式,去猜想和判断这一坨字节流应该使用什么字符集去解码。相关的笔记能够看这里,浏览器对编码的肯定
解决了字符集的问题,接下来就是构建 dom 树了,在 html 语言嵌套正常并且规范的状况下,这种 xml 标记的语言是比较容易的可以构建出一棵 dom 树出来的,固然,对于互联网上大量的不规范的页面,不一样的浏览器应该有本身不一样的容错去处理。构建出来的 dom 本质上仍是一棵抽象的逻辑树,构建 dom 树的过程当中,若是遇到了由 script 标签包起来的 js 动态脚本代码,那么会把代码送到 js 引擎里面去跑,若是遇到了 style 标签包围起来的 css 代码,也会保存下来,用于稍后的渲染。若是遇到了 img 等引用外部文件的标签,那么浏览器会根据指定的 url 再次发起一个新的 http 请求,去把这个文件拉取回来,值得一提的是,对于同一个域名下的下载过程来讲,浏览器通常容许的并发请求是有限的,一般控制在两个左右,因此若是有不少的图片的话,通常出于优化的目的,都会把这些图片使用一台静态文件的服务器来保存起来,负责响应,从而减小主服务器的压力。
dom 树构造好了以后,就是根据 dom 树和 css 样式表来构造 render 树了,这个才是真正的用于渲染到页面上的一个一个的矩形框的树,对于 render 树上每个框,须要肯定他的 x y 坐标,尺寸,边框,字体,形态,等等诸多方面的东西,render 树一旦构建完成,整个页面也就准备好了,能够上菜了。
须要说明的是,下载页面,构建 dom 树,构建 render 树这三个步骤,实际上并非严格的前后顺序的,为了加快速度,提升效率,让用户不要等那么久,如今通常都并行的往前推动的,现代的浏览器都是一边下载,下载到了一点数据就开始构建 dom 树,也一边开始构建 render 树,构建了一点就显示一点出来,这样用户看起来就不用等待那么久了。
当服务器提供了资源以后(HTML,CSS,JS,图片等),浏览器会执行下面的操做:
浏览器的功能是从服务器上取回你想要的资源,而后展现在浏览器窗口当中。资源一般是 HTML 文件,也多是 PDF,图片,或者其余类型的内容。资源的位置经过用户提供的 URI(Uniform Resource Identifier) 来肯定。
浏览器解释和展现 HTML 文件的方法,在 HTML 和 CSS 的标准中有详细介绍。这些标准由 Web 标准组织 W3C(World Wide Web Consortium) 维护。
不一样浏览器的用户界面大都十分接近,有不少共同的 UI 元素:
组成浏览器的组件有:
浏览器渲染引擎从网络层取得请求的文档,通常状况下文档会分红8kB大小的分块传输。
HTML 解析器的主要工做是对 HTML 文档进行解析,生成解析树。
解析树是以 DOM 元素以及属性为节点的树。DOM是文档对象模型(Document Object Model)的缩写,它是 HTML 文档的对象表示,同时也是 HTML 元素面向外部(如Javascript)的接口。树的根部是”Document”对象。整个 DOM 和 HTML 文档几乎是一对一的关系。
HTML不能使用常见的自顶向下或自底向上方法来进行分析。主要缘由有如下几点:
因为不能使用经常使用的解析技术,浏览器创造了专门用于解析 HTML 的解析器。解析算法在 HTML5 标准规范中有详细介绍,算法主要包含了两个阶段:标记化(tokenization)和树的构建。
浏览器开始加载网页的外部资源(CSS,图像,Javascript 文件等)。
此时浏览器把文档标记为“可交互的”(interactive),浏览器开始解析处于“推迟”模式的脚本(defer属性的脚本文件),也就是那些须要在文档解析完毕以后再执行的脚本。以后文档的状态会变为“完成”(complete,DOMContentLoaded事件被响应),浏览器会进行“加载”事件(onload事件被响应)。
注意解析 HTML 网页时永远不会出现“语法错误”,浏览器会修复全部错误,而后继续解析。
执行同步 Javascript 代码。