zero-blog icon indicating copy to clipboard operation
zero-blog copied to clipboard

用户输入 url 并按下回车(一)

Open PerseveranceZ opened this issue 6 years ago • 0 comments

1. 用户输入 url 并按下回车

一般的 URL(统一资源定位符) 可能会包含如下:

  • 协议 scheme 如 httphttps,等。
  • 服务器名称(域名/子域名)或 IP地址
  • 路径文件

以下根据 scheme 为 http/https,起始请求文件为 html ,且不考虑部署 CDN 的情况下进行分析。

2. DNS lookup

DNS 域名解析,优先级如下:

本地 hosts -> DNS 代理服务器 -> DNS 服务器

查找方式:

  • 递归式,发出封包后只等待正确响应解析结果
  • 迭代式,发出封包后等待下一个查询地址,然后发送封包到返回的查询地址,直至返回正确解析结果。

解析结果即域名对应真实 IP 地址

3. 建立 TCP/IP 连接,三次握手,开始请求数据帧。

三次握手

拿到域名对应 IP 后,浏览器准备请求 html 资源文件,如果在本地有相关缓存,根据缓存字段来进行强缓存还是协商缓存(后续会有图片提到)。

一般来说前端项目的 html 资源文件都进行 cache-control: no-cache/no-store ,每次都向服务器请求最新,这时候就发起与目标服务器的链接,假设我们是 http请求,开始三次握手(SYN 包),服务器处理请求返回 html 字节流,4次挥手发送 FIN 包,由此不难看出 TCP 效率很慢,而 TCP 却有很多优点:

  • 面向链接:使用 TCP 传输数据前,必须先建立 TCP 链接,传输完成后在释放链接。
  • 面向字节流:流入流出的字符序列。
  • 全双工通信:建立 TCP 链接后,通信双方都能发送数据。
  • 可靠:通过 TCP 链接传送的数据,不丢失,无差错,不重复且按序到达。

为什么需要三次握手

防止服务器端因接受了早已失效的连接请求报文,从而一直等待客户端请求,最终形成死锁,浪费资源。

为什么需要四次挥手?

为了保证通信双方都能通知对方当前需要释放资源,断开连接。

为什么客户端关闭需要等待2MSL时间?

确保服务器关闭连接,释放资源前不会存在失效的请求报文。

其他相关原理(发送窗口,接受窗口,慢启动,加法增大,sshrefresh,拥挤窗口等) https://www.jianshu.com/p/65605622234b

4. 请求 HTML,进行词法分析

浏览器将返回的 html 文件数据帧,传入浏览器自带的 HTML 解释器,会有如下转换过程:

字节流 -> 字符流 -> 词法分析生成 token -> 语法分析构建 DOM 节点 -> DOM 树,并挂载 DOM 事件机制。

解释期间,以 webkit 为例:

  • 当遇到 script 或者 link 变迁,或者图片等需要请求远端资源的标签,便又发出建立 TCP/IP 连接请求,效率是比较低的,占用服务器资源较大。

  • 会构造 frame,后构造 document,前者是有 Frame 类实例化,后者是由 HTMLDocument 实例化来,他的父级也是 Document 类,里面还有 XMLDocument 类,来解析 XML 老的写法,frame 包含 document

在构建 token 的期间,使用到的是 HTMLTokenizer 这个类,这个中有个 nextToken 方法,并内置了状态机,我们输入一段字符串,只要循环调用此方法,便会返回字符串中所有包含的 token,每次调用 nextToken 方法都会检查当前状态并做相应处理。

token 的词语类别大致分为

  • DOCTYPE
  • StartTag
  • EndTag
  • Comment
  • Character
  • EndOfFile 等

生成的 token 又会 经过 XSSauditor 类经过一层处理,防止部分不合法字符的注入。

token 生成完,浏览器开始构建 DOM 节点。

多次 TCP/IP 的建立,自然不难就看出 http 2.0 和 http 1 的差距,下面 是 http 2.0 和 http 1 的差别:

  • 多路复用

多路复用 HTTP2虽然只有一条TCP连接,但是在逻辑上分成了很多stream。 HTTP2把要传输的信息分割成一个个二进制帧,首部信息会被封装到HEADER Frame,相应的request body就放到DATA Frame,一个帧你可以看成路上的一辆车,只要给这些车编号,让1号车都走1号门出,2号车都走2号门出,就把不同的http请求或者响应区分开来了。但是,这里要求同一个请求或者响应的帧必须是有有序的,要保证FIFO的,但是不同的请求或者响应帧可以互相穿插。这就是HTTP2的多路复用,充分利用了网络带宽,提高了并发度。

  • 单一长连接

单一长连接在HTTP/2中,客户端向某个域名的服务器请求页面的过程中,只会创建一条TCP连接,即使这页面可能包含上百个资源。 单一的连接应该是HTTP2的主要优势,单一的连接能减少TCP握手带来的时延 。HTTP2中用一条单一的长连接,避免了创建多个TCP连接带来的网络开销,提高了吞吐量。

  • 头部压缩和二进制格式

http1.x一直都是plain text,对此我只能想到一个优点,便于阅读和debug。但是,现在很多都走https,SSL也把plain text变成了二进制,那这个优点也没了。于是HTTP2搞了个HPACK压缩来压缩头部,减少报文大小(调试这样的协议将需要curl这样的工具,要进一步地分析网络数据流需要类似Wireshark的http2解析器)。

  • 服务端推动Sever Push

这个功能通常被称作“缓存推送”。主要的思想是:当一个客户端请求资源X,而服务器知道它很可能也需要资源Z的情况下,服务器可以在客户端发送请求前,主动将资源Z推送给客户端。这个功能帮助客户端将Z放进缓存以备将来之需。

由此可以看出 http 2.0 更适合静态资源的请求与传输。

5. 开始构建 DOM 树

当我们拿到经过 xss 处理的 token 后,便可以开始构建 DOM 树,这时候会借助一个工具栈来进行,会把解析出来的 token 按照解析顺序放入栈中,入栈的 token 节点会先实例化成 DOM 节点,这时候会根据 token 类型,实例化一些基类,如 webkit 中 DOM 节点均继承 Node 基类,DOM 节点均有 Node 类中的 ContainerNode 类来制作,他会根据 token 类型(元素,属性,文本,文档等),来赋予节点对应的属性和方法,其中元素节点较为复杂,他们还会继承 HTMLElement 一个基类之外,还会根据特殊 token 来实例化特殊的类,如 HTMLImageElementHTMLInputElement 等。

DOM 节点构造完成之后,DOM 节点之间的索引随着 DOM 节点的构造也一并关联起来,(通过栈),当传入对应 EndTagtoken 便执行出栈操作,直到最后的 html 闭合标签 token 传入,整个 DOM 树便构建完成,可以看出构建 DOM 树也是一个深度优先的过程。

这里也可以看出,平级节点不可能同时出现在构造栈中

以下是全部节点类型:

值得注意的是,DocumentFragment 也是一种文档类型,我们可以再常用的优化手段中看到他,目的是减少 DOM 操作次数

PerseveranceZ avatar Jun 12 '18 01:06 PerseveranceZ