Skip to content

归纳下(输入 url 后发生了什么事情)[https://github.com/skyline75489/what-happens-when-zh_CN],后面在这基础下进行拓展

重点:该文篇文本纯属个人使用

  • 在浏览器输入 url

  • 判断是否符合网络协议(如:http, ftp, https, ssl....)

  • 转换非 ASCII 的 Unicode 字符

    • 浏览器检查输入是否含有不是 a-z, A-Z,0-9, - 或者 . 的字符
  • 检查 HSTS 列表(不懂)

    • 判断网站是否在 HSTS 列表里,如果在,就会使用 https 协议。如果不在,但是可以要求浏览器上对自己使用 HSTS 政策进行访问(雾)
  • DNS 查询

    • 浏览器检查域名是否在缓存中
    • 没有的话,调用 gethostbyname 库函数进行查询(gethostbyname不懂)
    • gethostbyname 会先检查本地的 HOSTS
    • 如果没找到,会向 DNS 服务器请求查询(DNS 服务器由网络通信栈提供,通常是本地路由器或者 ISP 的缓存DNS服务器)
    • 查询本地 DNS 服务器
    • 如果 DNS 服务器和我们的主机在同一个子网内,系统会按照下面的 ARP 过程对DNS 服务器进行 ARP查询(子网,网关的相关知识)
    • 如果DNS服务器和我们的主机在不同的子网,系统会按照下面的 ARP 过程对默认网关进行查询
  • ARP 过程

    • 发送 ARP (地址解析协议)广播,需要目标 ip 地址和发送 ARP 的接口的 MAC 地址 (什么是 ARP)
    • 首先查询 ARP 缓存,获取命中,返回结果: 目标 IP = MAC
    • 没有命中,查看路由表,目标 IP 是否在本地路由表中的某个子网内,是就使用那个子网相连的接口,否则使用默认网关相连的接口
    • 查询选择的网络接口的 MAC 地址
    • 发送一个二层( OSI 模型中的数据链路层) ARP 请求(什么是二层 ARP)。根据连接主机和路由器的硬件类型不同,分为几种情况:
    • ...省略,好复杂,看不懂
  • 使用套接字

    • 当浏览器获取目标服务器的 ip 地址,以及 URL 中给出的端口号,浏览器会调用系统库函数 socket,请求一个 TCP 流套接字,对应参数:AF_INET/AF_INET6 和 SOCK_STREAM
      • 这个请求首先被交给传输层,在传输层请求被封装成 TCP segment。目标端口被加入头部,源端口会在系统内核的动态端口范围内选取(什么是 TCP segemnt)
      • TCP segment 被送往网络层,网络差会在其中再加入一个 IP 头部,里面包含目标服务器的 IP 地址以及本机的 IP 地址,把它封装成一个 TCP packet
      • 这个 TCP packet 接下来会进入链路层,链路层会在封包中加入 frame 头部,里面包含了本地内置网卡的 MAC 地址以及网关(本地路由器)的 MAC 地址。如果内核不知道网关的 MAC 地址,它必须进行 ARP 广播来查询其他地址(理解每个层)
    • TCP 封包主板好,可以使用以太网、WIFI、蜂窝数据网络进行传输
    • 大部分用户,封包会从本地计算机触发,经过本地网络,再通过调制解调器把数字信号转换成模拟信号,使其适于在电话线路,有线电视光缆和无线电话线路上传输。在传输线路另一端,是另外一个调制解调器,它把模拟信号转回数字信号,交有网络节点处理(如何转换信号,网络节点是什么东西)
    • 最终封包,会到达管理本地子网的路由器。它会继续经过自治区域(autonomous system,缩写 AS)的边界路由器,其他自治区域最终到达目标服务器。一路上,经过的这些路由器会从 IP 数据报头部里提取出目标地址,并封包正确地路由到下一个目的地。IP 数据报头部 time to live (TTL) 域的值,每经过一个路由器就减1,如果封包的 TTL 变为0,或者路由器于网络拥堵等原因,封包队列满了,这个包就会被路由器丢弃(什么是 AS)
    • 上面的发送和接受过程在 TCP 连接期间会发生很多次:
      • 客户端选择一个初始序列号(ISN),将设置了 SYN 位的封包发送给服务器端,表明自己要建立连接并设置了初始序列号(ISN 有啥)
      • 服务器端接收到 SYN 包,如果它可以建立连接(什么是 SYN 包)
        • 服务器端选择它自己的初始序列号
        • 服务器端设置 SYN 位,表明自己选择了一个初始序列号
        • 服务器端把(客户端 ISN + 1)复制到 ACK 域,并且设置 ACK 位,表明自己接收到了客户端的第一个封包
      • 客户端通过发送一个封包,确认这次链接
        • 自己的序列号 + 1
        • 接收端 ACK + 1
        • 设置 ACK 位
      • 数据通过下面的方式传输:
        • 当一方发送了 N 个 Bytes 的数据包之后,将自己的 SEQ 序列号也增加 N (什么是 SEG)
        • 另一方确认接收到这个数据包后,它发送一个 ACK 包,ACK 的值设置为接收到的数据包的最后一个序列号(什么是 ACK )
      • 关闭连接时:
        • 要关闭连接的一方发送一个 FIN 包 (什么是 FIN )
        • 另一方确认这个 FIN 包,并且发送自己的 FIN 包
        • 要关闭的一方使用 ACK 包确认接收到了 FIN
  • TLS 握手

    • 客户端发送一个 ClientHello 消息到服务器端,消息中同事包含了它的 Transport Layer Security (TLS) 版本,可用的加密算法和压缩算法(什么是 TLS)
    • 服务器端向客户端返回一个 ServerHello 消息,消息中包含了服务端的 TLS 版本,服务器所选择的加密和压缩算法,以及数字证书认证机构(Certificate Authority,缩写 CA)签发的服务器公开证书,证书中包含了公钥。客户端会使用这个公钥加密接下来的握手过程,知道协商生成一个新的对称密钥
    • 客户端根据子级的信任 CA 列表,验证服务器端的证书是否可信,如果可信,客户端会生成一串伪随机数(什么是伪随机数?),使用服务器的公钥加密他。这串随机数会被用于生成新的对称密钥(是否可信,如何判断)
    • 服务器端使用自己的私钥解密上面提到的随机数,然后使用这串随机数生成自己的对称住密钥
    • 客户端发送一个 Finished 消息给服务器端,使用对称密钥加密这次通讯的一个散列值
    • 服务器端生成自己的 hash 值,然后解密客户端发送来的信息,检查是否对应,如果对应向客户端发送一个 finished 消息,也使用协商好的对称密钥加密(握手结束)
    • 接下来整个 TLS 会话都使用对称密钥进行加密,传输应用层(HTTP)内容
  • HTTP 协议。。。

  • 然后好像没挥手。。。。

  • 浏览器获取到服务的资源后,处理的事情

    • 解析 —— HTML、CSS、JS
    • 渲染 —— 构建 DOM 树 -> 渲染 -> 布局 -> 绘制
  • HTML 解析

    • 浏览器渲染引擎从网络层取得请求的文档,一般情况下文档会分成8KB大小的分块传输
    • HTML 解析器的主要工作是对 HTML 文档进行解析,生成解析树
    • 解析树是以 DOM 元素以及属性为节点的树。 DOM 是文档对象模型( Document Object Module )的缩写,它是 HTML 文档的对象表示。树的根部是 Document 对象
    • 解析算法, HTML 不能使用常见的自顶向下或自底向上方法进行分析
      • 语言本身的“宽容”特性
      • HTML 本身是残缺的,对于常见的残缺,浏览器需要有传统的容错机制来支持它们
      • 解析过程需要反复,对于其他语言,源码不会再解析过程变化,但是 HTML 是动态代码
    • 解析接受后,浏览器开始加载网页的外部资源,此时浏览器把文档标记为可交互的( interactive ),浏览器开始解析处于“推迟( deferred )”模式的脚本,之后文档就会变为“完成( complete )”,触发 load 事件。 HTML 不会出现无效语法( Invalid Syntax )错误,浏览器会修复所有错误内容,继续解析
  • CSS 解析

    • 根据 css词法和句法分析 css 文件和 style 内容(什么是css词法和句法)
    • css 解析器可能是自顶向下或自底向上,看浏览器
  • 页面渲染

    • 不同的浏览器可能不一样
    • 通过遍历 DOM 节点树创建一个 Frame 树或者渲染树,计算每个节点的各个 css 值
    • 通过累加子节点的宽度,该节点的水平 padding, border, margin,自底向上的计算 Frame 树的每个节点的首选宽度(...)
    • 使用上面的计算结果构建每个节点的坐标
    • 当存在 floated, abslutely, relatively 属性会有更多复杂计算
    • 创建 layer 来表示页面中的哪些部分可以成组的被绘制(如何区分),而不用被重新栅格化处理(栅格化处理是啥)
    • 计算各个层的最终位置,一组命令有 XXX 发出, GPU 命令缓冲区清空,命令传至 GPU 并异步渲染,帧被送到 window server
  • 浏览器渲染这块很迷,要找其他资料

http 缓存

http 缓存,看了几篇文章和 《http 图解》这本书,进行一次总结。

HTTP 缓存机制一二三

初识Http 缓存君

Http 缓存有几种,强缓存、协商缓存

强缓存

响应字段有两个: Expires/Cache-Control 来表明规则

Expires

Expires 是指缓存过期的时间,超过这个时间点就代表资源过期。而Expires 是 HTTP/1.0 的标注,Cache-Control 的优先级会更高

Cache-Control

Cache-Control 可以由多个字段组合而成,主要有以下几个取值:

  • max-age,指定一个时间长度,在这个时间段,缓存有效,单位为 s
  • s-maxage,同覆盖max-age,max-age、Expires,但仅适用于共享缓存,在私有缓存中被忽略
  • public 可以被任何对象缓存
  • private 只能被单个用户缓存,是非共享,不能被代理服务器缓存
  • no-cache,强制所有缓存了该相应的用户,在数据过期前,要发送带验证器请求到服务器
  • no-store,禁止缓存
  • must-revalidate,如果配置了 max-age,当缓存资源小于 max-age 时,使用缓存,否则验证资源,所以 must-revalidate 需要和 max-age 配合使用

expires 和 max-age ,建议使用 max-age ,expires 是 1.0 的标准,使用 expires 没有配置好用户正确的是时区,就会可能出错,缓存的生命周期不正确

协商缓存

缓存资源到期,如果缓存的资源和服务器上的资源没差异,客户端和服务器就会通过某种验证机制验证当前请求资源是否可以使用缓存

浏览器第一次请求数据后,会把某个响应头部的缓存标识存储起来,再次请求该资源,就会带上这个头部字段,服务器验证是否可用

Last-modified/If-Modified-Since

Last-modified: 服务器端资源的最后修改时间,相应头部会带上这个标识,第一次请求后,浏览器会记录这个时间,再次请求,请求头部就会带上 If-Modified-Since ,为之前的时间。服务器就会根据这个时间去对比,是否要返回新资源

Etag/If-None-Match

服务器生成一段 hash 字符串,第一次请求带上 ETag: xxxx,后面的请求带上 If-None-Match: xxxx,服务器进程 ETag ,返回状态码304

last-modified 和 Etag 区别

  • 某些服务器不能精确得到资源的最后修改的时间,这就无法判断资源是否更新
  • Last-modified 只能精确到秒
  • 如果资源最后修改时间变了,但内容没变, Last-modified 看不出内容没有改变
  • Etag 的精度比 Last-modified 高,属于强验证,要求资源字节级别一致,优先级高
  • 计算 Etag 也是需要占用资源