Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

浏览器输入URL发生了什么 #3

Open
impeiran opened this issue Mar 27, 2020 · 0 comments
Open

浏览器输入URL发生了什么 #3

impeiran opened this issue Mar 27, 2020 · 0 comments

Comments

@impeiran
Copy link
Owner

比较考验理念知识积累,后续笔者会随着经验增加再依次补充。

分析协议

首先第一步,如果URL是File://协议开头,浏览器首先会查找机子上的本地文件。如果是FTP就会按照其对应规则建立连接。如果是http/https则会按照下面步骤进行。

域名查找解析

如果地址是直接使用ip时,则跳过域名解析阶段。

地址是使用域名型的,就会进行此步骤,涉及到的概念:

  • 根 DNS 服务器: 返回顶级域DNS服务器的IP地址
  • 顶级域DNS服务器:返回权威DNS服务器的IP地址
  • 权威DNS服务器: 返回相应主机的IP地址

整个查找解析是一个递归过程:首先从

1 客户端/浏览器缓存中查找 ->
2 本地的hosts文件查找 ->
3 本地DNS解析器(此时可能命中缓存) ->
4 本地DNS服务器 ->

当上述这个过程都未命中时,根据本地DNS服务器设置的转发器进行查询。若未用转发模式,则迭代查找过程如下:

5 根域名服务器 ->
6 顶级域名服务器 ->
7 权威域名服务器

缓存与优化

  • DNS存在着多级缓存,从离浏览器的距离排序的话,有以下几种: 浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。
  • 拓展httpDNS,服务商可自定义匹配IP的规则,适合做分布式缓存与负载均衡。
  • 在域名和 IP 的映射过程中,给了应用基于域名做负载均衡的机会,可以是简单的负载均衡,也可以根据地址和运营商做全局的负载均衡。

建立tcp链接

如果是http(s)的链接,还是基于tcp的,需要建立起链接。涉及到TCP的三次握手,主要作用是为了确认双方的接收能力和发送能力是否都正确,指定自己的初始化序列号为后面的可靠性传送做准备。

涉及概念:

  • SYN(SYNchronization):在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1. 因此, SYN置1就表示这是一个连接请求或连接接受报文。
  • ACK:此标志表示应答域有效,就是说前面所说的TCP应答号将会包含在TCP数据包中;有两个取值:0和1,为1的时候表示应答域有效,反之为0。TCP协议规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1。
  • FIN:即完,终结的意思, 用来释放一个连接。当 FIN = 1 时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。
  • seq:序号,本报文段发送的数据的第一个字节的序号。
  • ack:期望收到对方下一个报文段的第一个数据字节的序号。

一开始:客户端处于Closed状态,服务端处于Listen状态。

第一次握手

发起方A向B发起连接请求报文段,tcp首部的控制位SYN设为1,序号seq=x(某个开始的传输字节数)。此时SYN=1时不携带数据,但要消耗一个序号位。此时发起方进入SYN-SENT(同步已发送)阶段。

第二次握手

接收方B收到请求连接报文,若同意建立连接,则发送报文段:设控制位ACK=1SYN继续为1,确认序号ack设为x+1,同时为自己的序号位seq=y。此时该报文依然不携带数据,TCP的服务器进程进入SYN-RCVN(同步收到状态)。

第三次握手

发送方A接收到确认报文后,还要再发送一次确认:控制位ACK=1,序号seq仍然为x+1,确认号ack为y+1。A发送后进入ESTABLISHED(已建立连接)状态,B收到确认后也进入ESTABLISH状态。

PS:如果不携带数据就不消耗序号位,即上次序号位为a且不带数据,下一次仍可继续发送序号位为a的报文。

延伸:为什么需要第三次握手,而不是两次握手?

因为是为了防止A第一次握手时发送的报文,被某些因素导致延误了,而此时A因为超时,重新建立了连接,当延误的报文到了之后,B继续确认进行第二次握手,重复建立链接。

有了第三次握手,则会确保第一次延误时不会进行多次重复的连接。

TSL/SSL握手过程(若有)

TLS以SSL3.0为基准,后又制定了TLS1.0、TLS1.1和TLS1.2。当前主流的版本是SSL3.0和TLS1.0。

整个握手过程采用非对称加密与对称加密混合的方式。因为全部使用非对称加密的方式,算法实现会很耗时间,整个传输过程就会变得效率低。全部采用对称加密的方式,一开始若密钥被监听到,则就不安全。

所以正常数据传输时采用对称加密,而对称加密的密钥,采用安全性更好的非对称加密进行传输。

第一次握手 - Client Hello

客户端首先要告诉服务端,自己可以支持哪些加密算法。于是客户端把本地支持的加密套件(cipher suites),以及产生一个随机数(Client Random),两者一并传输给服务端。而且,客户端也要保存这个随机数。

第二次握手 - Server Hello

服务端收到信息后,保存第一次的随机数。然后把证书(包含公钥、数字签名、过期信息、其余网站信息),以及生成并保存第二个随机数(Server Random),并确定使用的加密方法,三个信息一并传输回给客户端(server hello done)。

若此次发送的证书信息不齐全时,中间还会发送一个Server Key Exchange信息,补充回给客户端。

第三次 - Client Key Exchange

接着,客户端收到证书及信息后。首先验证证书的完整性、正确性、以及证书上的域名与服务端域名是否一致。

拓展数字签名:证书的数字签名由CA相关组织私钥加密而成,而浏览器一般内置了知名的机构的信息与公钥,使用机构提供的公钥解密签名:得到哈希摘要算法,以及一段摘要。接着再对证书上的服务端公钥进行哈希摘要并验证。确保中间过程的服务端公钥没被篡改。

验证完证书的正确性后,客户端会使用一些加密算法(例如:RSA, Diffie-Hellman)产生一个48个字节的Key,这个Key叫PreMaster Secret。该Key将会连同前两个随机数,使用共同协商的加密套件,加密生成“对话密钥(Master Key / Session Key)”。

但客户端只用接收到的公钥加密PreMaster Key,把该加密结果发送给服务端。

此处握手可拓展DH算法生成PreMaster Key,那么就不再需要传递Key,只传递需要的参数即可生成。

第四次 - Server Finish

服务端收到信息后,使用自己的私钥解密,获得PreMaster Key,同样的,因为服务端也有存之前的两个随机数,所以此处也可以根据约定的加密套件生成同样的Master Key

为了验证之前搭建的加密通道是否成功,服务端会用该Master Key加密一段Finish信息给客户端。如果客户端能对Finish信息进行正常加解密且消息正确的被验证,则说明加密通道已经建立成功,可以正常传输数据。

参考资料:
http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html
https://blog.csdn.net/u010285974/article/details/85320788

浏览器渲染

此处权作通过HTTP请求,浏览器已得到HTML数据:

  1. 对HTML文本词法分析和语法分析,自上而下加载,遇到<script src=""><style href="">此类标签,若资源指向外链,则会额外发起请求加载。

    script标签:若指明defer,则是并行加载,但会延迟到整个页面解析完再执行。async也是会并行加载,加载完后再执行脚本。

  2. 渲染进程将标签内容转换为DOM树

  3. 渲染引擎将CSS样式表转化为浏览器可以理解的styleSheets,计算出DOM节点的样式。

    • 首先进行属性值标准化,例如:将color: blue转化为color: rgb(0,0,255)
    • 其次处理样式的继承与层叠 => 从右往左开始匹配
  4. 创建renderTree布局树,计算元素的布局信息。

    结合dom tree和样式规则,生成一颗只包含可见元素的tree。即display: none的tree并不会出现在此模型上。

  5. 对布局树进行分层,并生成分层树。

    此处考虑到页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-index 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)

  6. 为每个图层生成绘制列表,并将其提交到合成线程。合成线程将图层分图块,并栅格化将图块转换成位图。

    栅格化可以理解为按照图层整合出顶层视角上的显示图块。合成线程优先考虑合成视口及其附近的位图。

  7. 合成线程发送绘制图块命令给浏览器进程。浏览器进程根据指令生成页面,并显示到显示器上。

参考:https://mp.weixin.qq.com/s/nMlZWZO6foRUPFK34ouPhg

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant