# Http 基础知识

# 1. HTTP 和 HTTPS

# 1.http 和 https 的基本概念

http: 是一个客户端和服务器请求和应答的标准 (TCP), 用于从 www 服务器传输超文本到本地浏览器的超文本传输协议

https: 是以安全为目的的 HTTP 通道,即 HTTP 下加入 SSL 层进行加密。作用:建立一个信息安全通道,来确保数据的传输,确保网站的真实性

# 2.http 和 https 的区别及优缺点

  • http 是超文本传输协议,信息是明文传输,HTTPS 协议要比 http 协议安全,https 是具有安全性的 ssl 加密传输协议,可防止数据在传输过程中被窃取,改变,确保数据的完整性 (并非绝对安全)
  • http 协议的 默认端口 为 80, https 的默认端口为 443
  • http 的连接很简单模,是无状态的. https 握手阶段比较费时,会使页面加载时间延长 50%, 增加 10%~20% 耗电
  • https 缓存 不如 http 高效,会增加数据开销
  • https 协议需要 ca 证书,费用较高,功能越强大的 证书 费用越高
  • SSL 证书需要绑定 IP, 不能在同一个 IP 上绑定多个域名,IPV4 资源支持不了这种消耗

# 3. https 协议的工作原理

客户端在使用 HTTPS 方式与 web 服务器通信时有以下几个步骤:

  1. 客户端使用 https url 访问服务器,则要求 web 服务器 建立ssl链接
  2. web 服务器接收到客户端请求后,会将 网站证书(证书中包含了公钥),传输给客户端
  3. 客户端 和 web 服务器端开始协商 SSL链接的安全等级 ,也就是加密等级
  4. 客户端浏,览器通过双方协商一直的安全等级, 建立会话秘钥 ,然后通过网站的公钥来加密会话秘钥,并传送给网站
  5. web 服务器通过 自己的私钥解密出会话秘钥
  6. web 服务器 通过会话秘钥加密与客户端之间的通信

相关文章: 解读 HTTP1/HTTP2/HTTP3

# 2. TCP 协议

# 1.TCP 三次握手

  1. 第一次握手: 建立连接时,客户端发送sync包(syn=j)到服务器,并进入SYN_SETNT状态 ;SYN: 同步序列编号 (Synchronize Sequence Numbers)
  2. 第二次握手: 服务器收到sync包并确认客户的SYN (ack=j+1), 同时也发送一个自己的SYN包 (syn=k) , 即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;
  3. 第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1) , 此包发送完毕,客户端和服务器进入 ESTABLISHED (TCP 连接成功状态), 完成三次握手

握手过程中传送的包中不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据

# 2.TCP 四次握手

  1. 客户端进程发出连接释放报文 ,并且停止发送数据。释放数据报文收不,FIN =1, 其序列号为 seq=u (等于前面已经传送过来的数据的最后一个字节的序号加 1), 此时, 客户端进入FIN-WAIT-1(终止等待)状态 .TCP 规定,FIN 报文段即使不携带数据,也要消耗一个序号
  2. 服务器收到链接释放报文,发出确认报文 ,ACK=1, ack=u+1, 并且带上自己的序列号 seq=v, 此时, 服务端就进入了CLOSE-WAIT(关闭等待)状态 .TCP 服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受,这个状态还要持续一段时间,也就是整个 CLOSE-WAIT 状态的持续时间
  3. 客户端收到服务器的确认请求后,此时, 客户端就进入FIN-WAIT-2(终止等待2)状态 ,等待服务器发送链接释放报文 (在此之前还需要接受服务器发送的最后数据)
  4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文 ,FIN=1,ack=u+1, 由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为 seq=w, 此时, 服务器就进入了LAST-ACJ(最后确认状态) , 等待客户端的确认.
  5. 客户端收到服务器的连接释放报文后,必须发出确认 ,ACK=1,ack=w+1, 而自己的序列号是 seq=u+1, 此时 客户端进入了TIME-WAIT(时间等待状态) . 注意此时连接还没有释放,必须经过 2**MSL (最长报文寿命) 时间后, 当客户端撤销对应的TCB后,才进入CLOSED状态 .
  6. 服务器只要收到了客户端发出的确认, 立即进入CLOSED状态 。同样,撤销 TCB 后,就结束了这次的 TCP 连接。可以看到 服务器结束TCP连接的时间要比客户端的早一些

# 3.TCP/IP 如何保证数据包传输的有序可靠

对字节流分段并进行编号,然后通过 ACK回复超时重发 两个机制来保证

  1. 为了充分保证数据包的可靠传递,发送方必须把已发送的数据包保留在缓冲区
  2. 并且为每个已经发送的数据包启动一个超时定时器
  3. 如在定时器超时之前收到了对方发来的应答信息 (可能是对本包的应答,也可以是对本包后续包的应答), 则释放该数据包占用的缓冲区
  4. 否则,重传该数据包,直到收到应道或重传次数超过规定的最大次数为止
  5. 接收方收到数据包后,先进行 CRC 校验,如果正确则把数据交给上层协议,然后给发送方发送一个累计应答包,表明该数据已收到,如果接收方正好也有数据要发给发送方,应答包也可放在数据包中携带过去

# 4.TCP 和 UDP 的区别

  1. TCP 是面向 链接 的,而 UDP 是面向无连接的
  2. TCP 仅支持 单波传输 ,UDP 提供了单播,多播,广播的功能
  3. TCP 的三次握手保证了链接的可靠性;UDP 是无连接的,不可靠的一种数据传输协议,首先体现在无连接上,通信都不需要建立链接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收
  4. UDP 的 头部开销 比 TCP 更小,数据 传输效率更高实时性更好

# 3.HTTP 请求跨域问题

# 1. 跨域的原理

跨域:浏览器不能执行其他网站的脚本,由浏览器的同源策略造成

同源策略:浏览器对 JavaScript 实施的安全限制,只要 协议,域名,端口 有任何一个不同,都被当做是不同的域

跨域原理:通过各种方式,避开浏览器的安全限制

URL说明是否允许通信
http://www.a.com/a.js <br /> http://www.a.com/b.js同一域名下允许
http://www.a.com/lab/a.js <br /> http://www.a.com/script/b.js同一域名,不同文件夹允许
http://www.a.com:8000/a.js <br /> http://www.a.com/b.js同一域名,不同端口不允许
http://www.a.com/a.js <br /> https://www.a.com/b.js同一域名,不同协议不允许
http://www.a.com/a.js <br /> http://10.10.10.11/b.js域名和域名对应 IP不允许
http://www.a.com/a.js <br /> http://script.a.com/b.js主相同,子域不同不允许
http://www.a.com/a.js <br /> http://a.com/b.js同一域名,不同二级域名 (同上)不允许 (cookie 也不允许访问)
http://www.cnblogs.com/a.js <br /> http://www.a.com/b.js不同域名不允许

# 2. 解决方案

  • JSONP:

    ajax 请求收同源策略影响,不允许进行跨域请求,而 script 标签 src 属性中的链接却可以访问跨域的 js 脚本,利用这个特性,服务端不再返回 JSON 格式的数据,而是返回一段调用某个函数的 js 代码,在 src 中进行了调用,实现了跨域

    步骤:

    1. 创建一个 script 标签
    2. script 的 src 属性设置接口地址
    3. 接口参数,必须要带一个自定义函数名,不然后台无法返回数据
    4. 通过定义函数名去接收返回的数据
    // 动态创建 script
    var script = document.createElement('script')
    // 设置回调函数
    function getData(data) {
        console.log(data)
    }
    // 设置 script 的 src 属性,并设置请求地址
    script.src = 'http://localhost:3000/?callback=getData'
    // 激活 script
    document.body.appendChild(script)

    JSONP 的缺点:

    JSON 只支持 get, 应为 script 标签只能使用 get 请求;JSONP 需要后端配合返回指定格式的数据

  • document.domain 基础域名相同,子域名不同

  • window.name 利用在一个浏览器窗口内, 载入的所有域名都是共享一个 window.name

  • CORS CORS (Cross-origin resource sharing) 跨域资源共享 服务器设置对 CORS 的支持原理:

    服务器设置 Access-Control-Allow-Origin HTTP 响应头之后,浏览器将会允许跨域请求

  • proxy 代理 目前常用方式,通过服务器设置代理

  • window.postMessage() 利用 h5 新特性 window.postMessage ()

相关文章: 跨域,基础概念

# 4.Cookie, SessionStorage, localStorage 的区别

相同点:

  • 存储在客户端

不同点:

  • cookie 数据大小不能超过 4K; sessionStorage 和 localStorage 的存储比 cookie 大得多,可达到 5M+

  • cookie 设置的过期时间之前一直有效;localStorage 永久存储,浏览器关闭后数据不丢失,除非主动删除数据;

    SessionStorage 数据在当前浏览器窗口关闭后自动删除

  • cookie 的数据会自动的传递到服务器;SessionStorage 和 localStorage 数据保存在本地

# 5. 粘包问题分析与策略

TCP 粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾

粘包出现的原因:

在流传输中出现,UDP 不回出现粘包,因为它有 消息边界

粘包情况有两种,一种是 粘在一起的包都是完整的数据包 ,另外一种情况是 粘在一起的包有不完整的包

为了避免粘包现象,可采取以下几种措施:

  1. 对于发送方引起的粘包现象,用户可以通过编程设置来避免, TCP提供了强制数据立即传送的操作指令PUSH ,TCP 收到该操作指令后,就立即将本端数据发送出去,而不必等待发送缓冲区满
  2. 对于接收方引起的粘包,则可通过优化程序设计,精简接收进程工作量, 提高结束进程优先级等措施 ,使其及时接收数据,从而尽量避免出现粘包现象
  3. 由接收方控制,将一包数据按结构字段,认为控制分多次接收,然后合并,通过这种手段来避免粘包. 分包多发

三种错误的不足之处:

  1. 第一种编程设置方法虽然可以避免发送方引起的粘包,但是关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用
  2. 第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网路突发可能使某个时间段数据包到达接收方比较快,接收方还是有可能来不及接收,从而导致粘包
  3. 第三种方法虽然避免了粘包,但应用程序的效率比较低,对实时应用的场景不合适

比较周全的对策:接收方创建 -- 预处理线程,对接收到的数据包进行预处理,将粘连的包分开.

# 6. 浏览器

# 1. 从输入 URL 到页面加载的全过程

页面加载过程

  1. 浏览器中输入 URL
  2. 查找缓存:浏览器先查看 浏览器缓存 -- 系统缓存 -- 路由缓存中是否有该地址页面,如果有则显示页面内容。如果没有则进行下一步.
    • 浏览器缓存:浏览器会记录 DNS 一段时间,因此,只是第一个地方解析 DNS 请求.
    • 操作系统缓存:如果在浏览器缓存中不包含这个记录,则会使用系统调用操作系统,获取操作系统的记录 (保存最近的 DNS 查询缓存)
    • 路由器缓存:如果上述两个步骤均不能成功获取 DNS 记录,继续搜索路由器缓存
    • ISP 缓存:若上述均失败,继续向 ISP 搜索
  3. DNS 域名解析:浏览器向 DNS 服务器发起请求,解析该 URL 中的域名对应的 IP 地址. DNS服务器是基于UDP的,因此会用到UDP协议
  4. 建立 TCP 链接:解析出 IP 地址户,根据 IP 地址和默认 80 端口,和服务器建立 TCP 连接
  5. 发起 HTTP 请求:浏览器发起读取文件的 HTTP 请求,该请求报文作为 TCP 三次握手的第三次数据发送给服务器
  6. 服务器响应请求并返回结果:服务器对浏览器请求做出响应,并把对应的 HTML 文件发送给浏览器
  7. 关闭 TCP 连接:通过四次挥手释放 TCP 连接
  8. 浏览器渲染:客户端 (浏览器) 解析 HTML 内容并渲染出来,浏览器接收到数据包后的解析流程为:
    • 构建 DOM 树:词法分析然后解析成 DOM 树 (dom tree), 是由 DOM 元素及属性节点组成,树的根是 document 对象
    • 构建 CSS 规则数:生成 CSS 规则数 (CSS Rule Tree)
    • 构建 render 树: Web 浏览器将 DOM 和 CSSOM 结合,并构建出渲染树 (render tree)
    • 布局 (Layout): 计算出每个节点在屏幕中的位置
    • 绘制 (Painting): 即遍历 render 树,并使用 UI 后端层绘制每个节点

浏览器渲染流程

  1. JS 引擎解析过程:调用 JS 引擎执行 JS 代码 (JS 的解析阶段,预处理阶段,执行阶段生成执行上下文,VO, 作用域回收机制等)
    • 创建window对象 : window 对象也称全局执行环境,当页面产生时就被创建,所有的全局变量和函数都属于 window 属性和方法,而 DOM Tree 也会映射在 window 的 document 对象上。当关闭网页或者浏览器时,全局执行环境会被销毁.
    • 加载文件 :完成 JS 引擎分析它的语法与词法是否合法,如果合法进入预编译
    • 预编译 :在预编译的过程中,浏览器会寻找全局变量声明,将它作为 window 的属性加入到 window 对象中,并给变量赋值为 undefined ; 寻找全局函数声明,把它作为 window 的方法加入到 window 对象中,并将函数体赋值给它 (匿名函数是不参与编译的,因为它是变量). 而变量提升作为不合理的地方在 ES6 中已解决,函数提升还存在.
    • 解释执行:执行到变量就赋值,如果变量没有被定义,也没有被预编译直接赋值,在 ES5 非严格模式下这个变量会成为 window 的一个属性,也就是全局变量. string, int 这样的值就是直接将值放在变量的存储空间,Object 对象就是把指针指向变量的存储空间。函数执行,就将函数的环境推入一个环境的栈中,执行完成后再弹出,控制权交还给之前的环境.JS 作用域其实解释这样的执行流机制实现的

相关文案: DNS 域名解析过程 => 浏览器的工作原理

# 2. 浏览器重绘与重排的区别

  • 重排/回流(Reflow) : 当 DOM 的变化影响了元素的集合信息,浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程就叫做重排。表现为重新生成布局,重新排列元素.

  • 重绘(Repaint) : 当一个元素的外观发生改变,但是没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。表现为某些元素的外观被改变

  • 仅改变元素的外观,肯定不会硬气网页重新生成布局,但当浏览器重排之后,将会重新绘制受到此次重排影响的部分

  • 重排和重绘的代价高昂,会破坏用户体验,并且让 UI 展示非常迟缓,而相比之下重排的性能影响更大,两者无法避免时,一般选择代价更下的重绘

[重绘] 不一定会出现 [重排], [重排] 必然会出现 [重绘]

# 3. 如何触发重排和重绘

任何改变用来构建渲染树的信息都会导致一次重排或重绘:

  • 添加,删除,更新 DOM 节点
  • 通过 display: none 隐藏一个 DOM 节点 -- 触发重排和重绘
  • 通过 visibility: hidden 隐藏一个 DOM 节点 -- 只触发重绘,因为没有几何变化
  • 移动或给页面中的 DOM 节点添加动画
  • 添加一个样式表,调整样式属性
  • 用户行为,例如调整窗口大小,改变字号,或者滚动

# 4. 如何避免重绘或重排

  1. 集中改变样式

  2. 不要把 DOM 节点属性值放在循环里当做循环变量使用

  3. 为动画的 HTML 元件使用 fixedabsoultposition , (此时修改 CSS 就不回触发 reflow)

  4. 不使用 table 布局,因为可能很小的一个改动会造成整个 Table 的重新布局

  5. 尽量只修改 position: absoulefixed 元素,对其他元素影响不大

  6. 动画开始 CPU 加速,translate 使用 3D 变化

  7. 提升为合成层

    将元素提升为合成层的优势

    • 合成层的位图,会交由 CPU 合成,比 CPU 处理快
    • 当需要 repaint 时,只需要 repaint 本身,不回影响到其他的层
    • 对于 transfrom 和 opacity 效果,不回触发 layout 和 paint

    提升合成层最好的方式是使用 CSS 的 will-change 属性:

    #target {
        will-change: transform;
    }

合成层详解: 无线性能优化

# 5.304 过程

  • 浏览器请求资源时首先命中资源的 Expires 和 Catch-Control, Expires 受限于本地时间,如果修改了本地时间,可能会造成缓存失效,可以通过 Cache-control: max-age 指定最大生命周期,状态仍然返回 200, 但不会请求数据,在浏览器中能明显看到 from cache 字样
  • 强缓存失效,进入协商阶段,首先验证 ETagETag 可以保证每一个资源是唯一的,资源变化都会导致 ETag 变化。服务器更具客户端上送的 if-None-Match 值来判断是否命中缓存.
  • 协商缓存 Last-Modify/if-Modify-Since 阶段,客户端第一次请求资源时,服务返回的 header 中会加上 Last-Modify, Last-modify 是一个时间标识该资源的最后修改时间。再次请求该资源时,request 的请求头中会包含 if-Modify-Since, 该值为缓存之前返回的 Last-Modify. 服务器收到 if-Modify-Since 后,根据资源的最后修改时间判断是否命中缓存

# 6. 浏览器的缓存机制 强制缓存 && 协商缓存

  1. 浏览器与服务器通信的方式为应答模式,浏览器发起 HTTP 请求 -- 服务器响应该请求.
  2. 浏览器第一次向服务器发起请求后拿到请求结果,会根据响应报文中 HTTP 头的缓存表示,决定是否缓存结果
  3. 是则将请求结果和缓存标识缓存浏览器缓存,简单过程如下

浏览器缓存机制

由上图可知:

  • 浏览器每次发起请求,都会 现在浏览器缓存中查找该请求的结果以及缓存标识
  • 浏览器每次拿到的返回的请求结果都会 将该结果和缓存标识存入浏览器缓存中
  1. 以上两点结论就是浏览器缓存机制的关键,他确保了每个请求的缓存存入与读取,只要理解浏览器缓存的使用规则,那么所有的问题就迎刃而解了.
  2. 根据是否需要向服务器重新发起 HTTP 请求将缓存过程分为两个部分,分别是 强制缓存协商缓存

强制缓存:

强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程。当浏览器向服务器发起请求时,服务器会将缓存规则放入 HTTP 响应报文的 HTTP 头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是 ExpiresCache-Control , 其中 Cache-Control 优先级比 Expires 高.

墙纸缓存的情况只要有三种,如下:

  1. 不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求 (跟第一次发起请求一致)
  2. 存在该缓存结果和缓存标识,但结果已失效,强制缓存失效,则使用协商缓存.
  3. 存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回结果

协商缓存:

协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,同样协商缓存的表示也是在响应报文的 HTTP 头中和请求结果一起返回给浏览器的,控制协商缓存的字段分别有: Last-Modified/ If-Modified-SinceEtag / IF-None-Match , 其中 Etag / IF-None-Match 的优先级比 Last-Modified /If-Moified-Since 高。协商缓存主要有以下两种情况:

  • 协商缓存失效,返回 304
  • 协商缓存失效,返回 200 和请求结果

相关文章: 彻底理解浏览器的缓存机制

# 7. 进程,线程和协程

  1. 进程 是一个具有一定独立功能的程序在一个数据集上的一次动态执行过程, 是操作系统进行资源分配和调度的一个独立单位 ,是应用程序运行的载体。进程是一种抽象的概念,没有统一的标准定义
  2. 线程 是程序执行中的一个单一的顺序控制流程,是 程序执行流的最小单元 ,是处理器调度和分配的基本单位。一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间 (也就是所在进程的内存空间). 一个标准的线程由线程 ID,d 当前指令指针 (PC), 寄存器和堆栈组成。而进程由内存空间 (代码,数据,进程空间,打开的文件) 和一个或多个线程组成
  3. 协程,英文 Coroutinex, 是一种 基于线程之上,但又比线程更加轻量级的存在 ,这种由程序员自己写程序管理的轻量级线程叫做 [用户空间线程], 具有对内核来说不可见的特性

进程和线程的区别与联系

【区别】:

  • 调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位;
  • 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行;
  • 拥有资源:进程是拥有资源的一个独立单位,线程不拥有资源,但可以访问隶属于进程的资源
  • 系统开销:
    • 在创建或撤销进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大与创建或撤销线程时的开销.
    • 进程由独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,而线程只是一个进程中的不同执行路径.
    • 线程由自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个进程死掉就等于所有线程死掉
    • 所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源比较大,效率要差一些

【联系】:

  • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程
  • 资源分配给进程,同一进程的所有线程共享该进程的所有资源
  • 处理机分给线程,即真正在处理机上运行的是线程
  • 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步

相关文章: 线程,进程,协程及 JS 协程的发展