浏览器原理与网络

293 阅读21分钟

1. 并行处理(思想)

把程序拆分成多个任务,多路同时处理

2. 线程

一个线程可以处理一个任务,多线程可以并行处理多任务。线程无法单独存在,由进程启动和销毁。

3. 进程

 一个进程是一个程序的运行化实例。是一种运行环境,操作系统会为这种运行环境分配内存,该内存用来存放代码,运行中的数据,文件和执行任务的线程。

  • 进程中任一线程出错,都会导致整个进程的崩溃
  • 线程间共享进程的数据,但进程间数据是不共享的
  • 进程关闭,操纵系统会回收内存
  • 进程间内容隔离,需要通信的话使用IPC机制

4. 浏览器的多进程架构

  • 浏览器开始是单进程架构的,各个功能模块是放在线程中运行的,不稳定,不流畅
  • 浏览器多进程架构:浏览器主进程,GPU进程,网络进程,多个页面渲染进程,多个插件进程

5. TCP/IP分层模型

  • 四层模型:物理层,网络层(IP:负责把数据包送达目的主机),传输层(TCP(面向连接,可靠),UDP(快,不可靠),负责把数据包通过端口号送达具体应用),应用层(HTTP,WebSocket等)
  • TCP面向连接:建立连接(三次握手)——传输数据——断开连接(四次挥手)
  • TCP具有“慢启动”特点,避免网络阻塞,带宽够用时,无法占用全贷款;带宽不够用时,又会动态减慢数据传输的速度
  • HTTP是基于TCP的,请求方和应答方都必须依据HTTP规范构建和解析HTTP报文

6.http发展各阶段特点

- HTTP/0.9
  • 仅用于传输HTML超文本,用于学术交流,数据以ASSIC字节流返回
  • 只有一个请求行,并没有请求头和请求体
  • 服务器也没有返回头信息
- HTTP/1.0
  • 需要支持传输js,css,图片,视频等多类型文件,文件格式不仅仅局限于 ASCII 编码,还有很多其他类型编码

  • 引入了请求头和响应头,它们都是以为 Key-Value 形式保存的,可以支持更加深入的交流

  • 为了满足多类型文件,浏览器端需要解决(确定)以下几个问题:不同类型的文件到底是哪种,浏览器需要针对不同文件不同处理?文件过大,服务器压缩后浏览器需要知道解压算法?支持全球传播支持多语言?不同文件不同的编码类型?这些通过请求头参数传给服务器,服务器根据请求头要求准备返回数据,即按要求返回,部分服务器不支持的部分,服务器会通过响应头告诉浏览器我不支持那个我使用了另一种方式,浏览器最终依据响应头来处理多类型文件数据。主要涉及“压缩方式”,“编码方式”,“文件类型”三个头信息来支持多文件类型解析。例如, 

    //请求头中
    accept: text/html
    accept-encoding: gzip, deflate, br
    accept-Charset: ISO-8859-1,utf-8
    accept-language: zh-CN,zh
    //响应头中
    content-encoding: br
    content-type: text/html; charset=UTF-8
    
  • 此外,引入了状态码(响应行),cache机制,用户代理(请求头user-agent)等字段

  • 每进行一次 HTTP 通信,都需要经历建立 TCP 连接、传输 HTTP 数据(请求发送,请求响应)和断开 TCP 连接三个阶段,适用于单页面各类型文件不多且引用不多的情况

  • 一个域名对于一个主机,一个主机对应一个ip

- HTTP/1.1
  • 为了适应单页面多资源多引用的情况,避免反复建立Tcp连接增加了持久连接的方法,建立一次tcp连接可以进行多次http请求,只要浏览器或者服务器没有明确断开连接,那么该 TCP 连接会一直连接。目前浏览器中对于同一个域名,默认允许同时建立 6 个 TCP连接。1HTTP/1.1默认为持久连接(长连接),关闭持久连接(短连接),可在请求头加

    Connection:close;

  • 持续连接可能导致一个tcp连接中的http请求阻塞,通过“管线化”处理阻塞问题,HTTP/1.1 中的管线化是指将多个 HTTP 请求整批提交给服务器的技术,但服务器依然要按顺序进行响应。但这项技术没能得到很好实现。

  • 提供虚拟主机的支持,一个公用ip可对应多个虚拟主机,一个虚拟主机对应一个域名。请求头中增加了Host 字段(当前域名地址而不是IP地址),用来表示当前的域名地址,这样服务器就可以根据不同的 Host 值做不同的处理。

  • 抛开响应头中contnet-length的无法动态估算局限,使用块传输机制对动态生成的内容提供了完美支持。

  • 提供了客户端cookie和安全机制

  • 但还存在一个问题:对带宽的利用率不理想。原因:TCP慢启动;同时开启了多条 TCP 连接,那么这些连接会竞争固定的带宽;HTTP/1.1 队头阻塞的问题。

- HTTP/2
  • 建立在https基础之上,但https的TLS握手时可选的

  • 主要采用多路复用技术使****一个域名只使用一个 TCP 长连接(减少慢启动次数和多个TCP连接竞争的问题),消除队头阻塞问题实现资源的并行传输。

    每个请求都有一个对应的 ID,浏览器端就可以随时将请求发送给服务器;服务器端接收到这些请求后,会根据自己的喜好来决定优先返回哪些内容,比如服务器可能早就缓存好了 index.html 和 bar.js 的响应头信息,那么当接收到请求的时候就可以立即把 index.html 和 bar.js 的响应头信息返回给浏览器,然后再将 index.html 和 bar.js 的响应体数据返回给浏览器。之所以可以随意发送,是因为每份数据都有对应的 ID,浏览器接收到之后,会筛选出相同 ID 的内容,将其拼接为完整的 HTTP 响应数据;HTTP/2 使用了多路复用技术,可以将请求分成一帧一帧的数据去传输,这样带来了一个额外的好处,就是当收到一个优先级高的请求时,比如接收到 JavaScript 或者 CSS 关键资源的请求,服务器可以暂停之前的请求来优先处理关键资源的请求。

  • 多路复用的实现:

    HTTP/2 添加了一个二进制分帧层;首先,浏览器准备好请求数据,包括了请求行、请求头等信息,如果是 POST 方法,那么还要有请求体。这些数据经过二进制分帧层处理之后,会被转换为一个个带有请求 ID 编号的帧,通过协议栈将这些帧发送给服务器。服务器接收到所有帧之后,会将所有相同 ID 的帧合并为一条完整的请求信息。然后服务器处理该条请求,并将处理的响应行、响应头和响应体分别发送至二进制分帧层。同样,二进制分帧层会将这些响应数据转换为一个个带有请求 ID 编号的帧,经过协议栈发送给浏览器。浏览器接收到响应帧之后,会根据 ID 编号将帧的数据提交给对应的请求。通过引入二进制分帧层,就实现了 HTTP 的多路复用技术。

  • 提供了请求优先级,可以在发送请求时,标上该请求的优先级,高优先级优先处理。实际应用中式如何实现的呢?

  • 服务器(主动)推送给浏览器:当用户请求一个 HTML 页面之后,服务器知道该 HTML 页面会引用几个重要的 JavaScript 文件和 CSS 文件,那么在接收到 HTML 请求之后,附带将要使用的 CSS 文件和 JavaScript 文件一并发送给浏览器,这样当浏览器解析完 HTML 文件之后,就能直接拿到需要的 CSS 文件和 JavaScript 文件,这对首次打开页面的速度起到了至关重要的作用。

  • 请求头和响应头压缩:在浏览器发送请求的时候,基本上都是发送 HTTP 请求头,很少有请求体的发送,只有POST请求才会发送请求体。

  • 在 HTTP/1.1 时代,为了提升并行下载效率,浏览器为每个域名维护了 6 个 TCP 连接;而采用 HTTP/2 之后,浏览器只需要为每个域名维护 1 个 TCP 持久连接,同时还解决了 HTTP/1.1 队头阻塞的问题。HTTP/2相对于HTTP/1做出了这么多的优化,实际项目中如何推动运维使用HTTP/2呢?又有哪些坑呢?

  • (1)TCP内部队头阻塞:HTTP/2受影响严重,有测试数据表明,当系统达到了 2% 的丢包率时,HTTP/1.1 的传输效率反而比 HTTP/2 表现得更好。

  • (2)TCP建立连接延迟:网络延迟又称为 RTT(Round Trip Time)。我们把从浏览器发送一个数据包到服务器,再从服务器返回数据包到浏览器的整个往返时间称为 RTT(如下图)。RTT 是反映网络性能的一个重要指标。而TCP(三次握手,1.5个RTT)和TLS握手(版本不一,握手次数不定,但1~2个RTT)需要多个RTT。
  • (3)TCP协议僵化:我们知道互联网是由多个网络互联的网状结构,为了能够保障互联网的正常工作,我们需要在互联网的各处搭建各种设备,这些设备就被称为中间设备。这些中间设备有很多种类型,并且每种设备都有自己的目的,这些设备包括了路由器、防火墙、NAT、交换机等。它们通常依赖一些很少升级的软件,这些软件使用了大量的 TCP 特性,这些功能被设置之后就很少更新了。所以,如果我们在客户端升级了 TCP 协议,但是当新协议的数据包经过这些中间设备时,它们可能不理解包的内容,于是这些数据就会被丢弃掉。这就是中间设备僵化,它是阻碍 TCP 更新的一大障碍。
  • (4)操作系统也是导致 TCP 协议僵化的另外一个原因。因为 TCP 协议都是通过操作系统内核来实现的,应用程序只能使用不能修改。通常操作系统的更新都滞后于软件的更新,因此要想自由地更新内核中的 TCP 协议也是非常困难的。
- HTTP/3
  • 使用quic协议模拟结合tcp和udp的优点:为了避免TCP中间设备僵化和操作系统的一些限制,HTTP/3全新设计支持了基于 UDP 实现了类似于 TCP 的多路数据流、传输可靠性等功能,我们把这套功能称为 QUIC 协议。HTTP2与HTTP3协议栈区别如下图:

    不仅单单结合TCP和UDP的有点,还进一步进行了升级,如下,多路复用实现了在同一物理连接上可以有多个独立的逻辑数据流。

  • 但QUIC同样在推广使用和设备支持上存在很大问题,期待后续发展。

7. HTTP请求流程

  • HTTP是一种允许浏览器向服务器获取资源的协议,可以获取各种不同文件类型,所以应用最广。
  • 请求流程
  1. 构建请求:构建请求行信息,准备发起网络请求
  2. 查找浏览器缓存:在本地保存资源副本,以供下次请求时直接使用,查找浏览器缓存失败,则接下来进入网络请求过程。
  3. DNS解析:查找浏览器DNS数据缓存(在浏览器本地把IP与域名关联起来),查找失败则请求DNS服务器,获取IP地址和端口(默认80)用来建立TCP连接
  4. 等待TCP队列:因为浏览器同一域名同一时间最多可以建立6个TCP连接,同一域名同一时间超过6个就要进入等待
  5. 建立TCP连接:三次握手
  6. 发送HTTP请求:请求行(请求方式,请求URI,协议版本),请求头header(其他浏览器基本信息和Cookie等),请求体body(POST方式下的数据等);
  7. 返回HTTP请求:响应行(协议版本,状态码),响应头header(其他服务器的基本信息和数据类型,服务器要在客户端保存的Cookie等信息),响应体body(实际内容)
  8. 重定向:如果返回状态码为301的话,则需要重定向,重定向的地址在响应头的Location字段
  9. 断开TCP连接:一旦服务器向客户端返回了数据,就要断开TCP连接,除非浏览器或者服务器在头信息中加入了Connection:Keep-Alive则保持连接状态。这样就可以继续通过该TCP连接发送请求,省去了重新建立连接的时间,提高了效率。

8. 浏览器缓存:

  • 缓存控制:浏览器通过响应头的该字段判断是否缓存该资源。
  1. 控制是否缓存的开关有两个(Cache-Control和Pragma(包含Pragma和Expires两个字段))。
  2. 三者的决定性作用优先级为:Pragma>Cache-Control>Expires。其中Cache-Control,Pragma字段在请求头中也有,是拿的响应头这两个字段值,是给客户端看的,这两个字段如果没过期则直接返回客户端缓存资源,都不向服务器端发送请求的。
  3. 通过devtools的NetWork面板Disable Cache开关设置客户端为no-cache的话会跳过浏览器端缓存直接向服务器端发送请求。而服务端的缓存控制一般都可以使用nginx反向代理进行配置。所以浏览器缓存控制完服务器端控制,设置了这么个时间关卡,告诉客户端没过期别给我发请求!!!如果过期了再找我!!!但是前端标识过期了找我后我还要校验我服务器端有没有更新呀,就要接着校验了!!!blog.csdn.net/u012375924/…
  • 缓存校验:
  1. 响应头:返回Last-Modified(最后一次更新时间,判断是否过期)和eTag(文件内容的算法标记,类似md5,判断文件内容是否更新)两个字段同时控制;
  2. 请求头对应用If-Modified-Since/If-Unmodified-Since带上Last-Modified这个字段的值返回与服务器端记录的最后一次更改时间做比较,如果相同则为304,如果不相同则响应头更新Last-Modified字段,接着继续校验文件内容是否变化;
  3. 即使更新时间改变也有可能内容没有修改,此时就额外需要eTag进行验证,请求头对应用If-None-Match /If-Match带上eTag这个字段的值返回与服务器端记录的最新内容标记做比较,如果相同则为304,如果不相同则200并响应头更新eTag字段。
  • **合理使用缓存:**设置cache-control,expires,以及E-tag都是不错的,不过要注意一个问题,就是文件更新后,要避免缓存而带来的影响。其中一个解决方案是在文件名字后面加一个版本号(时间戳等)。

9. referer及Referrer Policy

  • referrer:用以指定该请求说从哪个页面跳转来的,可以分析用户来源等信息

  • Referer Policy:有些网站直接将 sessionid 或是 token 放在地址栏里传递的,会原样不动地当作 Referrer 报头的内容传递给第三方网站,所以该Referer Policy策略用于过滤referer报头内容;常用选项有no-referrer-when-downgrade(默认安全策略)

  • 策略控制:服务端控制的话可以在Nginx中配置,客户端控制的话可以在html元标签中添加属性,如

    <meta name="referrer"
    content="no-referrer-when-downgrade">
    

10. 服务端Cookie

服务器验证完用户的登录信息后会把SessionId放在响应头的Set-Cookie字段,客户端在解析响应头遇到该字段则会保存cookie到本地,客户端下次再发生请求时则直接读取保存的cookie信息并放在响应头的Cookie字段发送给服务器而免于二次登录。服务器端解析请求头发现Cookie字段会获取并以此查询后台验证登录状态。

11. 从输入URL到页面展示

  • 从用户回车(不监听beforeunload)发送URL请求(搜索内容还是URL地址判断处理)到获得响应体准备解析这一过程称为浏览器导航
  • 还要判断响应头返回的数据类型Content-Type字段,来准备不同的响应。如果是字节流(下载类型),则会将该请求提交给浏览器的下载管理器进行文件下载,导航流程到此结束;如果是html类型,则接着准备渲染进程。
  • 用户回车后,可以监听beforeunload事件,取消导航,如果没有取消,则进入导航且页面状态图标会进入loadig态但当前页面内容还并没有改变,等到渲染进程“提交文档”,浏览器进程“确认文档被提交”时,浏览器进程才开始移除旧的文档,更新浏览器进程中的页面状态,包括安全状态,地址栏的URL,前进后退的历史状态,更新web页面。
  • 渲染进程渲染完页面后通知浏览器主进程,主进程则停止标签页的加载动画。

12. 渲染流程

每个子阶段都是输入内容——处理——输出内容

  • 构建DOM树:HTML文件——HTML解析器解析——DOM树(浏览器console.log(document)展示的即为DOM树结构,可以通过JS操作修改)
  • 样式计算:CSS文件——渲染引擎——styleSheets和ComputedStyle结构
  1. 浏览器console.log(document.styleSheets)展示的即为样式数组也可以进行查询和修改等样式操作
  2. 转换样式属性值,使其标准化
  3. 计算DOM树中每个节点的具体样式:继承规则,层叠规则
  • 布局:DOM树和ComputedStyle­——渲染引擎——只包含可见元素的布局树
  1. 创建布局树:通过DOM树和ComputedStyle创建布局树
  2. 布局计算:计算DOM树中可见元素的几何位置
  • 分层:为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree),开发者工具layer标签。
  1. 并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。
  2. 创建新图层的条件:拥有层叠上下文属性的元素,如position,opacity,fliter等;需要剪裁的地方,如使用overflow:hidden以及滚动条。
  • 绘制:生成绘制列表,一些绘制指令
  • 分块:渲染进程的主线程走完上面一系列步骤后,把接下来的任务交给合成线程,合成线程并不是一次性绘制整个文档,而是将文档分成固定大小的块。一个视口可能包含多个图块。
  • 光栅化:合成线程会将视口附近的图块优先生成位图(这一过程称为栅格化,栅格化是在栅格化线程池内进行,依靠GPU加速生成),并保存在GPU内存中。
  • 合成与显示:一旦所有图块都被光栅化,合成线程就会告诉浏览器进程,浏览器进程将页面内容绘制到内存中,最后再将内存显示到屏幕上。

13. 重排,重绘,合成

  • 重排:更新了元素的几何属性,那么需从“布局”阶段整个重新走一遍渲染流水线,开销巨大
  • 重绘:更新了元素的绘制属性,那么可以重新样式计算但可以跳过“布局”和“分层”阶段,直接进行绘制及接下来的流水线。效率比重排要高些。
  • 合成:使用transform实现动画,并没有涉及几何和绘制属性,则直接从“分块”即“绘制”后的所有流程。绘制效率更高。

14.script的三种加载方式(默认,async,defer)

lruihao.cn/posts/async…

  • JS 的加载分为两个部分:下载和执行。

  • 浏览器在执行 HTML 的时候如果遇到<script>时会停止页面的渲染, 去下载和执行 js 的文件直接遇见</scirpt>会继续渲染页面。

  • 因此存在三种方式来加载执行js

    默认:同步加载执行,都阻塞;一般建议把<script>标签放在<body>结尾处,这样尽可能减少页面阻塞。但是需要在渲染结束前就引入执行的JS环境配置等,则可以放到head标签结尾处,后通过函数调用或事件监听来有效执行。

async:异步加载,阻塞执行。**如果 js 前后有依赖性,用 async,就很有可能出错。**

defer:异步加载和异步执行。不过 defer 会按照原本的 js 的顺序执行,所以如果前后有依赖关系的 js 可以放心使用。

  • 总结:

1. 尽可能使用async,然后是defer,最后不使用属性。 2. 如果脚本是模块化的,并且不依赖于任何脚本,则使用async 3. 如果脚本依赖于或依赖于另一个脚本,则使用defer 4. 如果脚本很小并且有async脚本依赖该脚本,则不加属性。

15. Web性能优化:让页面更快地显示和响应

blog.csdn.net/Liu_yunzhao…

  • 加载阶段:主要因素有网络和 JavaScript 脚本;总的优化原则就是减少关键资源个数,降低关键资源大小,降低关键资源的 RTT (往返时延,14KB为一个单位)次数;
  1. 减少关键资源个数合理安排位置JS:对于代码量少且与首屏显示无关的改为内联JS并放在body底部;非操作DOM的JS按需使用defer或者async属性。CSS:对于代码量少且与首屏显示无关的改为内联JS并放在

    head

    (因为DOM解析和CSSOM解析可以同时进行,不会阻塞;而且这样解析到DOM的时候相应的CSS也已经下载完毕可以解析了,可以同时进行,减少重排);适当合并CSS资源(控制在14KB内);跟首屏显示无关的CSS,添加媒体-取消阻止显示标志;

    因为"渲染进程"中的"渲染引擎"(负责DOM和CSSOM)和"JS引擎"(负责JS交互)是互斥的,所以JS脚本的执行会阻塞渲染。而DOM和CSSOM的下载和解析却是可以同时进行的。

  2. 降低关键资源大小:压缩资源;

  3. 降低关键资源的 RTT:资源大小<14KB;使用CDN;

  4. 图片懒加载

  5. 防抖和节流

  6. 简化并优化CSS选择器,将嵌套层改到最小

  • 交互阶段:主要因素是 JavaScript 脚本;总的优化原则是提高帧渲染速度;大部分情况下,生成一个新的帧都是由 JavaScript 通过修改 DOM (重排)或者 CSSOM(重绘) 来触发的,还有另外一部分帧是由 CSS (变形、渐变、动画等特效,合成)来触发的;
  1. 减少 JavaScript 脚本执行时间:将一次执行的函数分解为多个任务,使得每次的执行时间不要过久;采用 Web Workers;
  2. 少用全局变量,多用局部变量,合理使用window作为函数参数避免作用域查找耗时
  3. 类型转换:把字符串类型转换为number类型,前使用‘+’计算符,效率最高,+''
  4. 避免强制同步布局:
  • 关闭阶段:页面所做的一些清理操作

16.SSL/TLS互联网安全加密技术

介于各种应用层与TCP之间的可选层。

主要用途:

  1. 认证用户和服务器,确保数据发送到正确的客户机和服务器;
  2. 加密数据以防止数据中途被窃取;
  3. 维护数据的完整性,确保数据在传输过程中不被改变。

其中,密钥协商过程称为TLS握手,大致流程如下:客户端发出请求(ClientHello)-服务器回应(SeverHello)-客户端回应(Certificate Verify)-服务器的最后回应(Server Finish)。

segmentfault.com/a/119000000…

17.URI与URL

URI(抽象概念):统一资源标识符,指可以唯一标识资源的方法方式;协议+主机(不含端口)+具体路径(不一定含文件)。

URL:统一资源定位符。强调必须定位到某一具体资源。是实现URI的一种具体方式。协议+主机(默认是80,或是别的端口)+具体文件路径(还必须是对应到路径下面的具体文件名称)。

18.DomContentLoaded事件监听

反应了document文档的加载状态

switch (document.readyState) {  
case "loading":
    // 表示文档还在加载中,即处于“正在加载”状态。
    break;
  case "interactive":
    // 文档已经结束了“正在加载”状态,DOM元素可以被访问。
    // 但是像图像,样式表和框架等资源依然还在加载。
    var span = document.createElement("span");
    span.textContent = "A <span> element.";
    document.body.appendChild(span);
    break;
  case "complete":
    // 页面所有内容都已被完全加载.
    let CSS_rule = document.styleSheets[0].cssRules[0].cssText;
    console.log(`The first CSS rule is: ${CSS_rule }`);
    break;
}

// 模拟 DOMContentLoaded/ jquery ready
document.onreadystatechange = function () {
  if (document.readyState === "interactive") {
    initApplication();
  }
}

// 模拟 load 事件
document.onreadystatechange = function () {
  if (document.readyState === "complete") {
    initApplication();
  }
}

19.事件监听:使用on和addEventListener区别

20.网络请求:

1. XHR

浏览器内建的对象,用于向服务器交换数据。可以手动设置是异步还是同步,但其异步模型是基于onreadystatechange事件的而不是基于现代的promise,generator/yield,async/await,异步机制比较落后。请求方式可以设置Get,Post。

2. Ajax

仅仅是一个技术名词,可以通过原生JS的XHR实现也可以通过Jquery封装XHR的.ajax方法、.ajax方法、.get方法、$.post方法,还可以使用fetch实现。AJAX 仅仅组合了浏览器内建的 XHR 对象(从 web 服务器请求数据);JavaScript 和 HTML DOM(显示或使用数据)。是一种实现不刷新页面而可以异步请求数据并更新部分页面的思想。

3. fetch

全局window的一个方法,相对于XHR来说,支持promise异步和async/await,并不是对XHR的封装,更接近底层,API丰富,但原生支持率不高,需要引入一些指定的 polyfill来实现兼容支持。

4. axios

基于promise,从浏览器中创建 XMLHttpRequests,从 node.js 创建 http 请求。本质上也是对XHR的封装。可以类似promise的语法对多个并发请求进行处理(axios.all,因为是基于promise的,本质是promise对象)。

21. 状态码

22. Get与POST的区别