对前端相关的网络问题进行一次探究。这篇文章将按照DNS解析,TCP连接的建立,HTTP缓存和状态码,浏览器的渲染的顺序来探究。长文预警。

前言

对于这个问题浏览器输入 url 到页面的展现发生了什么,经常可以在各大面试经验贴中看到,个人的理解也是和一般笼统的答案差不多:

在浏览器地址栏输入URL
浏览器解析URL获取协议,主机,端口,path
浏览器组装一个HTTP(GET)请求报文
浏览器获取主机ip地址
打开一个socket与目标IP地址,端口建立TCP链
TCP链接建立后发送HTTP请求
服务器将响应报文通过TCP连接发送回浏览器,浏览器接收HTTP响应
根据资源类型决定如何处理(假设资源为HTML文档)
解析HTML文档,构件DOM树,下载资源,构造CSSOM树,执行js脚本
最后展现出来给用户

关于准备面试,我想到这个地步就可以了吧?emm,直到我看到这篇文章

这位阿里的面试官这样说道:

基本如果应聘者只回到了上述步骤,很多关键步骤(前端应该了解的知识点)没有提及,那么基本凉凉一半了。这里简述下笔者感觉,这其中你应该具体展开说明的。

  1. 浏览器发送请求,是否需要查看缓存?是否请求资源在缓存中并且新鲜,跳转到转码步骤?如果资源已经缓存,是否新鲜?如何检查?怎么判断、http1.0 和 http1.1 的区别是什么,这些字段的优先级是怎么样子的。
  2. 浏览器解析 url 获取协议,过程是什么?DNS 递归查询可否介绍下?
  3. 建立 TCP 链接的三次握手是否可以介绍下?
  4. 服务器接受到请求,是否需要检查缓存?检查什么字段?什么样的缓存会需要服务端检查?
  5. 服务端发送 TCP 链接,浏览器接受 http 相应后,根据什么来决定是否需要关闭连接?关闭 TCP 的四次挥手是什么?
  6. 浏览器是否需要检查状态码,有哪些状态码?(笔者高频考码:304、200)
  7. 在解析的时候,具体如何解析、是否有顺序。(重绘重排高频考题就在这里)
  8. 总结如上、我们是否可以给出一些基本的网站优化手段?

龟龟,这些东西好像都知道一点,但缺乏系统的整理,磨砺两可。所以这篇文章将按照DNS解析TCP连接的建立HTTP缓存和状态码浏览器的渲染的顺序来探究。

DNS解析过程

解析原理

要想建立连接发送数据,首先需要知道服务器的ip地址。DNS协议用于查找某个域名对应的ip。DNS服务器采用分布式分层架构,通常来说有以下几类:

  1. 根DNS服务器
  2. 顶级DNS服务器(简称TLD,负责顶级域名.com .cn之类的),
  3. 权威DNS服务器(一般为一个组织的DNS服务器,比如我用腾讯云,他有自己的DNS服务器。最终要查询我的主机的ip是要找到这台DNS服务器的呃)
  4. 本地DNS服务器(一个区域内的DNS服务器,用来转发该区域内用户的DNS查询请求)

下图是一次DNS请求的示意图,比如我要查询自己网站luckyq.cn的ip地址

  1. DNS解析请求首先发到了本地DNS服务器,如果本地DNS服务器有对应域名的DNS记录缓存,则本地DNS服务器将直接返回给我。缓存记录设置有实效时间,比如我的域名在配置时设置的过期时间为600秒。

  2. 如果本地DNS服务器中没有对应的缓存,本地DNS服务器将询问根服务器(如果此时本地DNS服务器有对应TLD的缓存则跳过根DNS服务器)。根服务器查询.cn域名对应的DNS服务器,然后返回给本地DNS服务器。

  3. 现在本地DNS服务器知道了.cn DNS服务器的地址,向.cn TLD DNS服务器发送查询请求。以我为例,在腾讯云购买并注册域名时,绑定了我的域名luckyq.cn和ip地址123.206.25.72。

    腾讯云在向.cn DNS服务注册时会添加两条记录:
    luckyq.cn, f1g1ns1.dnspod.net, NS
    f1g1ns1.dnspod.net, 14.215.150.17, A

    现在进行查询的时候,查到luckyq.cn所解析的权威DNS名称和IP地址,将信息返回给本地DNS服务器。

  4. 本地DNS服务器向权威服务器进行查询,根据我的网站域名查询到了ip,返回。得到ip后客户端和服务器就可以进行通信了。

DNS请求抓包分析

命令:dig +trace luckyq.cn
+trace的含义是跟踪整个DNS解析的过程
luckyq.cn是我网站的域名,ip地址为123.206.25.72
下图是命令行返回的结果

wireshark抓取数据包,毕竟过滤出本次请求相关的包

根据上图我们可以看到:

  1. 第一条表示我向本地DNS服务器(61.130.254.34)请求根DNS服务器的地址
  2. 第二条,本地dns服务器向本机返回了13个顶级域的NS记录和A记录
  3. 在拿到13个顶级域的域名后,本机开始向本地DNS服务器请求13个顶级域的ip地址(🤔这里其实我有点疑惑,上一条不是已经返回NS和A记录了吗,为什么后面还要对13个顶级域名的ip都询问一遍。可能的原因是第一条本机查询时只要了NS记录,所以本机只关心NS记录)。在本地DNS返回顶级DNS域名后,本机就可以开始询问了。192.5.5.241#53(f.root-servers.net)最先返回了cn的顶级NS和A记录。接着就和上面的过程类似了,询问本机各cn顶级DNS的ip,然后接着查询。
  4. 203.119.28.1#53(d.dns.cn)最先返回了要查询的权威DNS的NS记录(腾讯云的DNS服务器),然后本机询问本地DNS服务器ip,最后查询相应ip返回结果。

注:上一次实验过了几分钟,当你再次执行相应命令并抓包时,会发生什么?(🤔为什么只有这么几个包)个人觉得原因是:上一次查询刚过不久,本地DNS缓存了一些A记录,比如根DNS服务器域名和ip的对应关系,cn顶级DNS服务器域名和ip对应的关系,所以本机不再向本地DNS服务器进行询问,减少了很多包。

客户端和服务器如何建立连接

TCP请求头结构

当年考研的时候看计算机网络,看的是计算机网络自顶向下(这本书很不错,推荐)。整体给我的感觉是网络这块协议很复杂。不过好歹学过,七层协议,各层协议主要是干嘛的,一些专业术语还是有印象的。不过你要问我TCP协议具体的请求头结构,三次握手的细节这些我可能就记不太清了(所以想记录下来)。我会在这一节先回顾下TCP协议请求头的结构,一些特别基础的知识就不在本文写了。下图是TCP请求头的结构。

TCP三次握手

在上一节中,客户端获取了服务器的ip地址,两者开始建立TCP连接。建立TCP连接时,就要说到经典的三次握手。下图是对访问知乎的抓包。前三个数据包就是三次握手。

在TCP连接建立和销毁时,总共有11种TCP的状态,具体如下表。

TCP状态 含义
LISTEN 侦听来自远方的TCP端口的连接请求
SYN-SENT 发送连接请求后等待匹配的连接请求(客户端)
SYN-RECEIVED 收到和发送一个连接请求后等待对方对连接请求的确认(服务器)
ESTABLISHED 代表一个打开的连接
FIN-WAIT-1 等待远程TCP连接中断请求,或先前的连接中断请求的确认
FIN-WAIT-2 收到和发送一个连接请求后等待对方对连接请求的确认(服务器)
CLOSE-WAIT 等待从本地用户发来的连接中断请求
CLOSING 等待远程TCP对连接中断的确认
LAST-ACK 等待原来的发向远程TCP的连接中断请求的确认
TIME-WAIT 等待足够的时间以确保远程TCP接收到连接中断请求的确认
CLOSED 没有任何连接状态

下面就结合抓包的数据和状态变化来分析TCP三次握手

  1. 第一次握手,客户端发送数据包,将SYN标志位置为1,表示要建立连接,且序列号Seq=0。此时客户端的TCP状态由CLOSED变为SYN-SENT
  2. 第二次握手,服务端收到客户端数据包后,将自己的SYN,ACK标识位设置为1,表示建立连接和收到。服务端也会指定一个新的序列号Seq=0。Ack=1表示服务端下一个期望收到的包序列是1。到此时,服务端的TCP状态由CLOSEDLISTENSYN-RECEIVED
  3. 第三次握手,客户端收到包后将自己的ACK置为1表示收到,并且Seq变为1。此时客户端处于ESTABLISHED状态。
  4. 服务端收到客户端的ACK包后也处于ESTABLISHED状态,双方建立连接开始发送数据。

加上HTTPS

一般来说,浏览器访问服务器,https的建立流程如下图(网上偷的图)
在连接的建立过程中,涉及到比较重要的三个算法:

  1. 非对称加密算法(用于保证传输的内容不被破解),常用的算法是RSA,关于RSA算法的原理可以参考阮老师的文章。RSA算法原理
  2. 对称加密算法(由于非对称加密需要较大的运算量,所有的传输数据不可能都用非对称加密算法加密,一般而言是用非对称加密算法确保双方安全拿到对称加密算法的信息,后续的数据传输就用双方都知道的对称加密算法进行通信)。
  3. 散列hash算法(用于确保数据的完整性)。

如果你对一些专有名词比如数字证书,上面提到的算法用途还不太清楚,建议先阅读下文末参考链接中和https相关的文章。下面我将直接访问京东首页并抓包分析https的建立过程。

  1. client_hello
    客户端发起请求,以明文传输请求信息,包含版本信息,加密套件候选列表,压缩算法候选列表,随机数,扩展字段等信息。

  2. server_hello+certificate+server_key_exchange+sever_hello_done
    server_hello。服务端返回协商的信息结果,包括选择使用的协议版本,选择的加密套件cipher suite,选择的压缩算法 compression method、随机数等,其中随机数用于后续的密钥协商.

    certificate。发送服务器证书,将服务器配置的证书(链)发送到客户端(用户证书在前,上级证书在后)。
    server_key_exchange 。对于使用DHE/ECDHE非对称密钥协商算法的SSL握手,将发送该类型握手(查阅的资料,后面打算g研究下各种加密算法的区别)。
    server_hello_done。通知客户端 server_hello 信息发送结束。

  3. 客户端进行证书校验。客户端验证证书的合法性,如果验证通过才会进行后续通信,否则根据错误情况不同做出提示和操作,合法性验证主要包括证书链的可信性,有效期,是否吊销,域名是否匹配等。

  4. client_key_exchange+change_cipher_spec+encrypted_handshake_message

TCP四次挥手

emm,关于四次挥手我抓包了很多网站,发现区别很大,下面以西电的官网为例讲解四次挥手的过程。

  1. 第一次挥手,客户端将标志位FIN置为1,表示想断开连接。报文中会指定一个序列号,此时客户端处于FIN_WAIT1状态。
  2. 第二次挥手,服务端把客户端的序列号 + 1 作为 报文的序列号,表明已经收到客户端的报文了,此时服务端处于CLOSE_WAIT状态。
  3. 第三次挥手,如果服务端也想断开连接了,和客户端的第一次挥手一样将 FIN 置为1,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
  4. 第四次挥手,客户端收到服务端ACK报文之后进入FIN_WAIT2状态。此时如果发送一个报文作为应答,且把服务端的序列号值 + 1 作为自己 ACK 报文的序列号值,则客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入CLOSED 状态
  5. 服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。

常见问题:

  1. 为什么挥手要4次,握手只要3次?
    挥手的时候,客户端请求断开时服务端可能还没发完最后的数据,因此需要先回应一下客户端,等数据发完后再发起断开请求。

  2. 为什么要有TIME_WAIT状态而不是马上关闭连接?
    要确保服务器是否已经收到了我们的 ACK 报文,如果没有收到的话,服务器会重新发 第三次挥手的FIN 报文给客户端,客户端再次收到 ACK 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文。

  3. 挥手一定是四次吗?从我抓了几个网站的包来看其实并不一定。比如知乎:

http缓存探究

浏览器的渲染机制

参考资料

  1. 《计算机网络自顶向下(第七版)》
  2. 作为前端的你了解多少tcp的内容
  3. 工程师最容易搞错的域名知识
  4. DNS 原理入门
  5. TCP请求头
  6. tcp连接全过程各种状态详解
  7. HTTPS协议详解(四):TLS/SSL握手过程
  8. SSL/TLS协议运行机制的概述
  9. HTTPS协议详解(三):PKI 体系
  10. 看图学HTTPS
  11. HTTP 缓存机制一二三
  12. HTTP1.0、HTTP1.1 和 HTTP2.0 的区别