2

肝完《浏览器基本原理与实践》,我总结了这些

 2 years ago
source link: https://segmentfault.com/a/1190000040896436
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

肝完《浏览器基本原理与实践》,我总结了这些

作为一名前端er,日常工作打交道最多(之一)的莫过于熟悉而又陌生的浏览器了,熟悉是每天都会基于浏览器的应用层面之上码业务,陌生是很多人可能跟我一样不熟悉其内部运行原理,比如js是怎样运行的呢?精美样式页面是怎样渲染到电脑屏幕的呢?在开放的互联网它又是怎样保证我们个人信息安全的呢?带着种种疑云开始肝李兵老师的《浏览器基本原理与实践》,不得不说,大家之作,通俗易懂,层层拨开云雾见青天,下面就(非常非常)简单总结一下。

Chrome 架构:仅仅打开了 1 个页面,为什么有 4 个进程

线程和进程区别:多线程可以并行处理任务,线程不能单独存在,它是由进程来启动和管理的。一个进程是一个程序的运行实例。

线程和进程的关系:1、进程中任意一线程执行出错,都会导致整个进程的崩溃。2、线程之间共享进程中的数据。3、当一个进程关闭后,操作系统会回收进程所占用的内存。4、进程之间的内容相互隔离。

单进程 浏览器:1、不稳定。单进程中的插件、渲染线程崩溃导致整个浏览器崩溃。2、不流畅。脚本(死循环)或插件会使浏览器卡顿。3、不安全。插件和脚本可以获取到操作系统任意资源。

多进程浏览器:1、解决不稳定。进程相互隔离,一个页面或者插件崩溃时,影响仅仅时当前插件或者页面,不会影响到其他页面。2、解决不流畅。脚本阻塞当前页面渲染进程,不会影响到其他页面。3、解决不安全。采用多进程架构使用沙箱。沙箱看成时操作系统给进程上来一把锁,沙箱的程序可以运行,但是不能在硬盘上写入任何数据,也不能在敏感位置读取任何数据。

多进程架构:分为 浏览器进程、渲染进程、GPU 进程、网络进程、插件进程。

缺点:1、资源占用高。2、体系架构复杂。

面向服务架构:把原来的各种模块重构成独立的服务,每个服务都可以在独立的进程中运行,访问服务必须使用定义好的接口,通过 IPC 通讯,使得系统更内聚、松耦合、易维护和拓展。

TCP 协议:如何保证页面文件能被完整送达浏览器

  • IP 头是 IP 数据包开头的信息,包含 IP 版本、源 IP 地址、目标 IP 地址、生存时间等信息;
  • UDP 头中除了目的端口,还有源端口号等信息;
  • IP 负责把数据包送达目的主机;
  • UDP 负责把数据包送达具体应用;
  • 对于错误的数据包,UDP 不提供重发机制,知识丢弃当前的包,不能保证数据的可靠性,但是传输速度非常块;
  • TCP 头除了包含了目标端口和本机端口号外,还提供了用于排序的序列号,保证了数据完整地传输,它的连接可分为三个阶段:建立连接、传输数据和断开连接;

HTTP 请求流程:为什么很多站点第二次打开速度会很快

  • 浏览器中的 HTTP 请求从发起到结束一共经历如下八个阶段:构建请求、查找缓存、准备 IP 和端口、等待 TCP 队列、建立 TCP 连接、发起 HTTP 请求、服务器处理请求、服务器返回请求和断开连接;
  • 构建请求。浏览器构建请求行,构建好后,准备发起网络请求;
  • 查找缓存。在真正法器请求前浏览器会查询缓存中是否有请求资源副本,有则拦截请求,返回资源副本,否则进入网络请求;
  • 准备 IP 地址和端口。HTTP 网络请求需要和服务器建立 TCP 连接,而建立 TCP 连接需要准备 IP 地址和端口号,浏览器需要请求 DNS 返回域名对应的 IP,同时会缓存域名解析结果,供下次查询使用;
  • 等待 TCP 队列。Chrome 机制,同一个域名同时最多只能建立 6 个 TCP 连接;
  • 建立 TCP 连接。TCP 通过“三次握手”建立连接,传输数据,“四次挥手”断开连接;
  • 发送 HTTP 请求。建立 TCP 连接后,浏览器就可以和服务器进行 HTTP 数据传输了,首先会向服务器发送请求行,然后以请求头形式发送一些其他信息,如果是 POST 请求还会发送请求体;
  • 服务器处理请求。首先服务器会返回响应行,随后,服务器向浏览器发送响应头和响应体。通常服务器返回数据,就要关闭 TCP 连接,如果请求头或者响应头有 Connection:keep-alive TCP 保持打开状态;

导航流程:从输入 URL 到页面展示这中间发生了什么

  • 用户输入 URL 并回车
  • 浏览器进程检查 URL,组装协议,构成完整 URL
  • 浏览器进程通过进程通信(IPC)把 URL 请求发送给网络进程
  • 网络进程接收到 URL 请求后检查本地缓存是否缓存了该请求资源,如果有则将该资源返回给浏览器进程
  • 如果没有,网络进程向 web 服务器发起 http 请求(网络请求),请求流程如下:

    • 进行 DNS 解析,获取服务器 IP 地址,端口
    • 利用 IP 地址和服务器建立 tcp 连接
    • 构建请求头信息
    • 发送请求头信息
    • 服务器响应后,网络进程接收响应头和响应信息,并解析响应内容
  • 网络进程解析响应流程:

    • 检查状态码,如果是 301/302,则需要重定向,从 Location 自动读取地址,重新进行第 4 步,如果是 200,则继续处理请求
    • 200 响应处理:检查响应类型 Content-Type,如果是字节流类型,则将该请求提交给下载管理器,该导航流程结束,不再进行后续渲染。如果是 html 则通知浏览器进程准备渲染进程进行渲染
  • 准备渲染进程

    • 浏览器进程检查当前 URL 是否和之前打开的渲染进程根域名是否相同,如果相同,则复用原来的进程,如果不同,则开启新的渲染进程
  • 传输数据、更新状态

    • 渲染进程准备好后,浏览器向渲染进程发起“提交文档”的消息,渲染进程接收到消息和网络进程建立传输数据的“管道”
    • 渲染进程接收完数据后,向浏览器发送“确认提交”
    • 浏览器进程接收到确认消息后 engine 浏览器界面状态:安全、地址 URL、前进后退的历史状态、更新 web 页面

渲染流程(上):HTML、CSS 和 JavaScript 是如何变成页面的

  • 浏览器不能直接理解 HTML 数据,需要将其转化为 DOM 树结构;
  • 生成 DOM 树后,根据 CSS 样式表,计算出 DOM 树所有节点样式;
  • 创建布局树:遍历 DOM 树所有可见节点,把这些节点加到布局中,不可见节点忽略,如 head 标签下所有内容,display: none 元素;

渲染流程(下):HTML、CSS 和 JavaScript 是如何变成页面的

  • 分层:层叠上下文属性的元素(比如定位属性元素、透明属性元素、CSS 滤镜属性元素)提升为单独的一层,需要裁剪的地方(比如出现滚动条)也会被创建为图层;
  • 图层绘制:完成图层树构建后,渲染引擎会对图层树每一层进行绘制,把一个图层拆分成小的绘制指令,再把指令按照顺序组成一个带绘制列表;
  • 有些情况图层很大,一次绘制所有图层内容,开销太大,合成线程会将图层划分为图块(256x256 或者 512x512);
  • 合成线程将图块提交给栅格线程进行栅格化,将图块转换为位图。栅格化过程都会使用 GPU 加速,生成的位图保存周期 GPU 内存中;
  • 一旦所有图块都被栅格化,合成线程会生成一个绘制图块命令(DrawQuad),然会将命令提交给浏览器进程,viz 组件接收到该指令,将页面内容绘制到内存中,显示在屏幕上;
  • 重排:通过 JavaScript 或者 CSS 修改元素几何位置属性,会触发重新布局,解析后面一系列子阶段;重绘:不引起布局变换,直接进入绘制及其以后子阶段;合成:跳过布局和绘制阶段,执行的后续操作,发生在合成线程,非主线程;

变量提升:javascript 代码是按顺序执行的吗

  • JavaScript 代码在执行之前需要先编译,在编译阶段,变量和函数会被存放到变量环境中,变量默认值会被设置为 undefined;
  • 在代码执行阶段,JavaScript 引擎会从变量环境中查找自定义的变量和函数;
  • 如果在编译阶段,窜爱两个相同的函数,那么最终放在变量环境中的是最后定义的那个,后定义的覆盖先定义的;

调用栈:为什么 JavaScript 代码会出现栈溢出

  • 每调用一个函数,JavaScript 引擎会为其创建执行上下文压入调用栈,然后,JavaScript 引擎开始执行函数代码。
  • 如果一个函数 A 调用另外一个函数 B,那么 JavaScript 引擎会为 B 函数创建执行上下文,并将 B 函数的执行上下文压入栈顶。
  • 当前函数执行完毕后,JavaScript 引擎会将该函数的执行上下文弹出栈。
  • 当分配的调用栈空间被占满时,会引发“堆栈溢出”问题。

块级作用域:var 缺陷以及为什么要引入 let 和 const

  • let、const 申明的变量不会被提升。在 javascript 引擎编译后,会保存在词法环境中。
  • 块级作用域在代码执行时,将 let、const 变量存放在词法环境的一个单独的区域。词法环境内部维护一个小型的栈结构,作用域内部变量压入栈顶。作用域执行完,从栈顶弹出。

作用域链和闭包:代码中出现相同的变量,JavaScript 引擎如何选择

  • 使用一个变量,JavaScript 引擎会在当前的执行上下文中查找变量,如果没有找到,会继续在 outer(执行环境指向外部执行上下文的引用)所指向的执行上下文中查找;
  • JavaScript 执行过程,作用域链是由词法作用域决定,而词法作用域是由代码中函数声明的位置决定;
  • 根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使外部函数已经执行结束了,但是内部函数引用外部函数的变量依旧保存在内存中,把这些变量的集合称为闭包;

this:从 JavaScript 执行上下文视角讲 this

当执行 new CreateObj 的时候,JavaScript 引擎做了四件事:

  • 首先创建一个控对象 tempObj;
  • 接着调用 CreateObj.call 方法,并将 tempObj 作为 call 方法的参数,这样当 createObj 的执行上下文创建时,它的 this 就指向 tempObj 对象;
  • 然后执行 CreateObj 函数,此时的 CreateObj 函数执行上下文中的 this 指向 tempObj 对象;
  • 最后返回 tempObj 对象。

this 的使用分为:

  • 当函数最为对象的方法调用时,函数中的 this 就是该对象;
  • 当函数被正常调用时,在严格模式下,this 值是 undefined,非严格模式下 this 指向的是全局对象 window;
  • 嵌套函数中的 this 不会继承外层函数的 this 值;
  • 箭头函数没有自己的执行上下文,this 是外层函数的 this。

栈空间和堆空间:数据是如何存储的

动态语言:在使用时需要检查数据类型的语言。
弱类型语言:支持隐式转换的语言。

JavaScript 中的 8 种数据类型,它们可以分为两大类——原始类型和引用类型。
原始类型数据存放在栈中,引用类型数据存放在堆中。堆中的数据是通过引用与变量关系联系起来的。

从内存视角了解闭包:词法扫描内部函数,引用了外部函数变量,堆空间创建一个“closure”对象,保存变量。

垃圾回收:垃圾数据如何自动回收

  • 栈中数据回收:执行状态指针 ESP 在执行栈中移动,移过某执行上下文,就会被销毁;
  • 堆中数据回收:V8 引擎采用标记-清除算法;
  • V8 把堆分为两个区域——新生代和老生代,分别使用副、主垃圾回收器;
  • 副垃圾回收器负责新生代垃圾回收,小对象(1 ~ 8M)会被分配到该区域处理;
  • 新生代采用 scavenge 算法处理:将新生代空间分为两半,一半空闲,一半存对象,对对象区域做标记,存活对象复制排列到空闲区域,没有内存碎片,完成后,清理对象区域,角色反转;
  • 新生代区域两次垃圾回收还存活的对象晋升至老生代区域;
  • 主垃圾回收器负责老生区垃圾回收,大对象,存活时间长;
  • 新生代区域采用标记-清除算法回收垃圾:从根元素开始,递归,可到达的元素活动元素,否则是垃圾数据;
  • 为了不造成卡顿,标记过程被切分为一个个子标记,交替进行。

编译器和解析器:V8 如何执行一段 JavaScript 代码的

  • 计算机语言可以分为两种:编译型和解释型语言。编译型语言经过编译器编译后保留机器能读懂的二进制文件,比如 C/C++,go 语言。解释型语言是在程序运行时通过解释器对程序进行动态解释和执行,比如 Python,JavaScript 语言。
  • 编译型语言的编译过程:编译器首先将代码进行词法分析、语法分析,生成抽象语法树(AST),然后优化代码,最后生成处理器能够理解的机器码;
  • 解释型语言解释过程:解释器会对代码进行词法分析、语法分析,并生产抽象语法树(AST),不过它会再基于抽象语法树生成字节码,最后根据字节码执行程序;
  • AST 的生成:第一阶段是分词(词法分析),将一行行源码拆解成一个个 token(语法上不可再分、最小单个字符)。第二阶段是解析(语法分析),将上一步生成的 token 数据,根据语法规则转为 AST,这一阶段会检查语法错误;
  • 字节码存在的意义:直接将 AST 转化为机器码,执行效率是非常高,但是消耗大量内存,从而先转化为字节码解决内存问题;
  • 解释器 ignition 在解释执行字节码,同时会手机代码信息,发现某一部分代码是热点代码(HotSpot),编译器把热点的字节码转化为机器码,并保存起来,下次使用;
  • 字节码配合解释器和编译器的计数实现称为即时编译(JIT)。

消息队列和事件循环:页面是怎么活起来的

  • 每个渲染进程都有一个主线程,主线程会处理 DOM,计算样式,处理布局,JavaScript 任务以及各种输入事件;
  • 维护一个消息队列,新任务(比如 IO 线程)添加到消息队列尾部,主线程循环地从消息队列头部读取任务,执行任务;
  • 解决处理优先级高的任务:消息队列的中的任务称为宏任务,每个宏任务中都会包含一个微任务队列,在执行宏任务的过程中,如果 DOM 有变化,将该变化添加到微任务队列中;
  • 解决单个任务执行时长过久:JavaScript 通过回调功能来规避。

webapi:setTimeout 是怎么实现的

  • JavaScript 调用 setTimeout 设置回调函数的时候,渲染进程会创建一个回调任务,延时执行队列存放定时器任务;
  • 当定时器任务到期,就会从延时队列中取出并执行;
  • 如果当前任务执行时间过久,会影响延时到期定时器任务的执行;
  • 如果 setTimeout 存在嵌套调用(5 次以上),判断该函数方法被阻塞,那么系统会设置最短时间间隔为 4 秒;
  • 未激活的页面,setTimeout 执行最小间隔是 1000 毫秒,目的是为了降低加载损耗;
  • 延时执行时间最大值是 24.8 天,因为延时值是以 32 个 bit 存储的;
  • setTimeout 设置的回调函数中的 this 指向全局 window。

webpai:XMLHttpRequest 是怎么实现的

  • XMLHttpRequest onreadystatechange 处理流程:未初始化 -> OPENED -> HEADERS_RECEIVED -> LOADING -> DONE;
  • 渲染进程会将请求发送给网络进程,然后网络进程负责资源下载,等网络进程接收到数据后,利用 IPC 通知渲染进程;
  • 渲染进程接收到消息之后,会将 xhr 回调函数封装成任务并添加到消息队列中,等主线程循环系统执行到该任务的时候,会根据相关状态来调用回调函数。

宏任务和微任务:不是所有的任务都是一个待遇

  • 消息队列中的任务为宏任务。渲染进程内部会维护多个消息队列,比如延时执行队列和普通消息队列,主线程采用 for 循环,不断地从这些任务队列中取出任务并执行;
  • 微任务是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前;
  • V8 在执行 javascript 脚本时,会为其创建一个全局执行上下文,同时会创建一个微任务队列;
  • 执行微任务过程中产生的微任务不会推迟到下个宏任务中执行,而是在当前宏任务中继续执行;

使用 Promise 告别回调函数

  • 使用 Promise 解决了回调地狱问题,消灭嵌套和多次处理;
  • 模拟实现 Promise
function Bromise(executor) {
  var _onResolve = null
  this.then = function (onResolve) {
    _onResolve = onResolve
  }
  function resolve(value) {
    setTimeout(() => {
      _onResolve(value)
    }, 0)
  }
  executor(resolve, null)
}

async await 使用同步方式写异步代码

  • 生成器函数是一个带星号函数,而且是可以暂停执行和回复执行的;
  • 生成器函数内部执行一段代码,遇到 yield 关键字,javascript 引擎返回关键字后面的内容给外部,并且暂停该函数的执行;
  • 外部函数可以同步 next 方法恢复函数的执行;
  • 协程是一种比线程更加轻量级的存在,协程可以看成是跑在线程上的任务,一个线程可以存在多个协程,但是同时只能执行一个协程,如果 A 协程启动 B 协程,A 为 B 的父协程;
  • 协程不被操作协同内核所管理,而完全由程序所控制,这样性能提升;
  • await xxx 会创建一个 Promise 对象,将 xxx 任务提交给微任务队列;
  • 暂停当前协程的执行,将主线程的控制权力转交给父协程执行,同时将 Promise 对象返回给父协程,继续执行父协程;
  • 父协程执行结束之前会检查微任务队列,微任务队列中有 resolve(xxx) 等待执行,触发 then 的回调函数;
  • 回调函数被激活后,会将主线程的控制权交给协程,继续执行后续语句,完成后将控制权还给父协程。

页面性能分析:利用 chrome 做 web 性能分析

  • Chrome 开发者工具(简称 DevTools)是一组网页制作和调试的工具,内嵌于 Google Chrome 浏览器中。它一共包含了 10 个功能面板,包括了 Elements、Console、Sources、NetWork、Performance、Memory、Application、Security、Audits 和 Layers。

DOM 树:JavaScript 是如何影响 DOM 树构建的

  • HTML 解析器(HTMLParse)负责将 HTML 字节流转换为 DOM 结构;
  • HTML 解析器并不是等整个文档加载完成之后再解析,而是网络进程加载流多少数据,便解析多少数据;
  • 字节流转换成 DOM 三个阶段:1、字节流转换为 Token;2、维护一个 Token 栈,遇到 StartTag Token 入栈,遇到 EndTag Token 出栈;3、为每个 Token 创建一个 DOM 节点;
  • JavaScript 文件和 CSS 样式表文件都会阻塞 DOM 解析;

渲染流水线:CSS 如何影响首次加载时的白屏时间?

  • DOM 构建结束之后,css 文件还未下载完成,渲染流水线空闲,因为下一步是合成布局树,合成布局树需要 CSSOM 和 DOM,这里需要等待 CSS 加载结束并解析成 CSSOM;
  • CSSOM 两个作用:提供给 JavaScript 操作样式表能力,为布局树的合成提供基础样式信息;
  • 在执行 JavaScript 脚本之前,如果页面中包含了外部 CSS 文件的引用,或者通过 style 标签内置了 CSS 内容,那么渲染引擎还需要将这些内容转化为 CSSOM,因为 JavaScript 有修改 CSSOM 的能力,所以在执行 JavaScript 之前,还需要依赖 CSSOM。也就是说 CSS 在部分情况下也会阻塞 DOM 的生成。

分层和合成机制:为什么 CSS 动画比 JavaScript 高效

  • 显示器固定刷新频率是 60HZ,即每秒更新 60 张图片,图片来自显卡的前缓冲区;
  • 显卡的职责是合成新的图像,保存在后缓冲区,然后后缓冲区和前缓冲区互换,显卡更新频率和显示前刷新频率不一致,就会造成视觉上的卡顿;
  • 渲染流水线生成的每一副图片称为一帧,生成一帧的方式有重排、重绘和合成三种;
  • 重排会根据 CSSOM 和 DOM 计算布局树,重绘没有重新布局阶段;
  • 生成布局树之后,渲染引擎根据布局树特点转化为层树,每一层解析出绘制列表;
  • 栅格线程根据绘制列表中的指令生成图片,每一层对应一张图片,合成线程将这些图片合成一张图片,发送到后缓存区;
  • 合成线程会将每个图层分割成大小固定的图块,优先绘制靠近视口的图块;

页面性能:如何系统优化页面

  • 加载阶段:减少关键资源个数,降低关键资源大小,降低关键资源的 RTT 次数;
  • 交互阶段:减少 JavaScript 脚本执行时间,避免强制同步布局:操作 DOM 的同时获取布局样式会引发,避免布局抖动:多次执行强制布局和抖动,合理利用 CSS 合成动画:标记 will-change,避免频繁的垃圾回收;
  • CSS 实现一些变形、渐变、动画等特效,这是由 CSS 触发的,并且是在合成线程中执行,这个过程称为合成,它不会触发重排或者重绘;

虚拟 DOM:虚拟 DOM 和真实 DOM 有何不同

  • 当有数据更新时, React 会生产一个新的虚拟 DOM,然会拿新的虚拟 DOM 和之前的虚拟 DOM 进行比较,这个过程找出变化的节点,然后将变化的节点应用到 DOM 上;
  • 最开始的时候,比较两个 DOM 的过程是在一个递归函数里执行的,其核心算法是 reconciliation。通常情况,这个比较过程执行很快,不过虚拟 DOM 比较复杂时,执行比较函数可能占据主线程比较久的时间,这样会导致其他任务的等待,造成页面卡顿。React 团队重写了 reconciliation 算法,称为 Fiber reconciler,之前老的算法称为 Stack reconciler;

PWA:解决 web 应用哪些问题

  • PWA(Progressive Web App),渐进式 Web 应用。一个渐进式过渡方案,让普通站点过渡到 Web 应用,降低站点改造代价,逐渐支持新技术,而不是一步到位;
  • PWA 引入 ServiceWorker 来试着解决离线存储和消息推送问题,引入 mainfest.json 来解决一级入口问题;
  • 暗转了 ServiceWorker 模块之后,WebApp 请求资源时,会先通过 ServiceWorker,让它判断是返回 Serviceworker 缓存的资源还是重新去网络请求资源,一切的控制权交给 ServiceWorker 来处理;
  • 在目前的 Chrome 架构中,Service Worker 是运行在浏览器进程中的,因为浏览器进程生命周期是最长的,所以在浏览器的生命周期内,能够为所有的页面提供服务;

WebComponent:像搭积木一样构建 web 应用

  • CSS 的全局属性会阻碍组件化,DOM 也是阻碍组件化的一个因素,因为页面中只有一个 DOM,任何地方都可以直接读取和修改 DOM;
  • WebComponent 提供了对局部试图封装能力,可以让 DOM、CSSOM 和 JavaScript 运行在局部环境中;
  • template 创建模版,查找模版内容,创建影子 DOM,模版添加到影子 DOM 上;
  • 影子 DOM 可以隔离全局 CSS 和 DOM,但是 JavaScript 是不会被隔离的;

HTTP1:HTTP1 性能优化

  • HTTP/0.9 基于 TCP 协议,三次握手建立连接,发送一个 GET 请求行(没有请求头和请求体),服务器接收请求之后,读取对应 HTML 文件,数据以 ASCII 字符流返回,传输完成断开连接;
  • HTTP/1.0 增加请求头和响应头来进行协商,在发起请求时通过请求头告诉服务器它期待返回什么类型问题、什么形式压缩、什么语言以及文件编码。引入来状态吗,Cache 机制等;
  • HTTP/1.1 改进持久化连接,解决建立 TCP 连接、传输数据和断开连接带来的大量开销,支持在一个 TCP 连接上可以传输多个 HTTP 请求,目前浏览器对于一个域名同时允许建立 6 个 TCP 持久连接;
  • HTTP/1.1 引入 Chunk transfer 支持动态生成内容:服务器将数据分割成若干任意大小的数据块,每个数据块发送时附上上个数据块的长度,最后使用一个零长度的块作为发送数据完成的标志。在 HTTP/1.1 需要在响应头中设置完整的数据大小,如 Content-Length。

HTTP2:如何提升网络速度

  • HTTP/1.1 主要问题:TCP 慢启动;同时开启多条 TCP 连接,会竞争固定宽带;对头阻塞问题;
  • HTTP/2 在一个域名下只使用一个 TCP 长连接和消除对头阻塞问题;
  • 多路复用的实现:HTTP/2 添加了二进制分帧层,将发送或响应数据经过二进制分帧处理,转化为一个个带有请求 ID 编号的帧,服务器或者浏览器接收到响应帧后,根据相同 ID 帧合并为一条完整信息;
  • 设置请求优先级:发送请求可以设置请求优先级,服务器可以优先处理;
  • 服务器推送:请求一个 HTML 页面,服务器可以知道引用了哪些 JavaScript 和 CSS 文件,附带一起发送给浏览器;
  • 头部压缩:对请求头和响应头进行压缩;

HTTP3:甩掉 TCP、TCL 包袱,构建高效网络

  • 虽然 HTTP/2 解决了应用层面的对头阻塞问题,不过和 HTTP/1.1 一样,HTTP/2 依然是基于 TCP 协议,而 TCP 最初是为了单连接而设计;
  • TCP 可以看成是计算机之间的一个虚拟管道,数据从一端发送到另一端会被拆分为一个个按照顺序排列的数据包,如果在传输过程中,有一个数据因为网络故障或者其他原因丢失,那么整个连接会处于暂停状态,只有等到该数据重新传输;
  • 由于 TCP 协议僵化,也不可能使用新的协议,HTTP/3 选择了一个折衷的方法,基于现有的 UDP 协议,实现类似 TC 片多路复用,传输可靠等功能,称为 QULC 协议;
  • QULC 实现类似 TCP 流量控制,传输可靠功能;集成 TLS 加密功能;实现多路复用功能;

同源策略:为什么 XMLHttpRequst 不能跨域请求

  • 协议、域名和端口号相同的 URL 是同源的;
  • 同源策略会隔离不同源的 DOM、页面数据和网络通信;
  • 页面可以引用第三方资源,不过暴露出诸如 XSS 问题,引入内容安全策略 CSP 限制;
  • 默认 XMLHttpRequest 和 Fetch 不能跨站请求资源,引入跨域资源共享(CORS)进行跨域访问控制;

跨站脚本攻击 XSS:为什么 cookie 中有 httpOnly 属性

  • XSS 跨站脚本,往 HTML 文件中注入恶意代码,对用户实施攻击;
  • XSS 攻击主要有存储型 XSS 攻击、反射型 XSS 攻击和 DOM 的 XSS 攻击;
  • 阻止 XSS 攻击:服务器对脚本进行过滤或转码,利用 CSP 策略,使用 HttpOnly;

CSRF 攻击:陌生连接不要随便点

  • CSRF 跨站请求伪造,利用用户的登录状态,通过第三方站点攻击;
  • 避免 CSRF 攻击:利用 SameSite(三种模式:Strict、Lax、None) 让浏览器禁止第三方站点发起请求携带关键 Cookie;验证请求的来源站点,请求头中的 Referer 和 Origin 属性;利用 CSRF Token;

沙盒:页面和系统之间的隔离墙

  • 浏览器被划分为浏览器内核和渲染内核两个核心模块,其中浏览器内核石油网络进程、浏览器主进程和 GPU 进程组成的,渲染内核就是渲染进程;
  • 浏览器中的安全沙箱是利用操作系统提供的安全技术,让渲染进程在执行过程中无法访问或者修改操作系统中的数据,在渲染进程需要访问系统资源的时候,需要通过浏览器内核来实现,然后将访问的结果通过 IPC 转发给渲染进程;
  • 站点隔离(Site Isolation)将同一站点(包含相同根域名和相同协议的地址)中相互关联的页面放到同一个渲染进程中执行;
  • 实现站点隔离,就可以将恶意的 iframe 隔离在恶意进程内部,使得它无法继续访问其他 iframe 进程的内容,因此无法攻击其他站点;

HTTPS:让数据传输更安全

  • 在 TCP 和 HTTP 之间插入一个安全层,所有经过安全层的数据都会被加密或者解密;
  • 对称加密:浏览器发送加密套件列表和一个随机数 client-random,服务器会从加密套件中选取一个加密套件,然后生成一个随机数 service-random,返回给浏览器。这样浏览器和服务器都有相同 client-random 和 service-random,再用相同的方法将两者混合生成一个密钥 master secret,双方就可以进行数据加密传输了;
  • 对称加密缺点:client-random 和 service-random 的过程都是明文,黑客可以拿到协商的加密套件和双方随机数,生成密钥,数据可以被破解;
  • 非对称加密:浏览器发送加密套件列表给服务器,服务器选择一个加密套件,返回加密套件和公钥,浏览器用公钥加密数据,服务器用私钥解密;
  • 非对称加密缺点:加密效率太低,不能保证服务器发送给浏览器的数据安全,黑客可以获取公钥;
  • 对称加密结合非对称加密:浏览器发送对称加密套件列表、非对称加密列表和随机数 client-random 给服务器,服务器生成随机数 service-random,选择加密套件和公钥返回给浏览器,浏览器利用 client-random 和 service-random 计算出 pre-master,然后利用公钥给 pre-master 加密,向服务器发送加密后的数据,服务器用私钥解密出 pre-master 数据,结合 client-random 和 service-random 生成对称密钥,使用对称密钥传输加密数据;
  • 引入数字证书是为了证明“我就是我”,防止 DNS 被劫持,伪造服务器;
  • 证书的作用:一个是向浏览器证明服务器的身份,另一个是包含服务器公钥;
  • 数字签名过程:CA 使用 Hash 函数技术明文信息,得出信息摘要,然后 CA 使用私钥对信息摘要进行加密,加密后的秘文就是数字签名;
  • 验证数字签名:读取证书明文信息,使用相同 Hash 函数计算得到信息摘要 A,再利用 CA 的公钥解密得到 B,对比 A 和 B,如果一致,则确认证书合法;

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK