阅读 851

[回炉计划]当输入xxxxhub的时候,居然是这样

前言

回炉计划第三篇,这是一个经典的前端面试题,考察范围非常的广,可深入的角度也非常的多。本篇文章的目的主要是回顾,所以内容和网络上流传的文章会有一定的重叠性。

过程

过程大致分为以下几个步骤

  1. DNS 解析
  2. TCP 连接
  3. 发送 HTTP 请求
  4. 服务器处理请求并返回 HTTP 报文
  5. 浏览器解析渲染页面
  6. 连接结束

DNS 解析

DNS 服务和 HTTP 协议一样,都是位于应用层的协议,它提供域名到 ip 地址之间的解析服务。 以查询www.baidu.com的 ip 地址为例

  1. 浏览器搜索自己的 DNS 缓存(维护一张域名与 IP 地址的对应表)
  2. 搜索操作系统中的 DNS 缓存(维护一张域名与 IP 地址的对应表)
  3. 搜索操作系统的 hosts 文件( Windows 环境下,维护一张域名与 IP 地址的对应表
  4. 操作系统将域名发送至 LDNS(本地区域名服务器,如果你在学校接入互联网,则 LDNS 服务器就在学校,如果通过电信接入互联网,则 LDNS 服务器就在你当地的电信那里。)LDNS 查询自己的 DNS 缓存(一般查找成功率在 80% 左右),查找成功则返回结果,失败则发起一个迭代 DNS 解析请求;
    1. LDNS 向 Root Name Server (根域名服务器,其虽然没有每个域名的的具体信息,但存储了负责每个域,如 com、net、org 等的解析的顶级域名服务器的地址)发起请求,此处,Root Name Server 返回 com 域的顶级域名服务器的地址;
    2. LDNS 向 com 域的顶级域名服务器发起请求,返回 baidu.com 域名服务器地址;
    3. LDNS 向 baidu.com 域名服务器发起请求,得到 www.baidu.com 的 IP 地址;
  5. LDNS 将得到的 IP 地址返回给操作系统,同时自己也将 IP 地址缓存起来
  6. 操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起来
  7. 至此,浏览器已经得到了域名对应的 IP 地址

补充:域名和 url 是两个概念,域名是用来确认服务器的地址的,而 url 是用来确认资源的地址;域名和 ip 地址不是一一对应的,一个域名同一时刻只能解析出一个 ip 地址,而一个 ip 地址可以绑定多个域名

TCP 连接

这就涉及到著名的 http 三次握手。简单来说与服务器建立连接需要经历以下三个过程。

  1. 第一次握手:建立连接时,客户端发送 syn 包(syn=j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;
  2. 第二次握手:服务器收到 syn 包,必须确认客户的 synack=j+1),同时自己也发送一个 SYN 包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;
  3. 第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。

这里可以延申拓展一下 HTTPS 的知识 HTTPS 详解--segmentfault

服务器处理请求并返回 HTTP 报文

这一步主要是后端从接口获取到 tcp 报文,处理之后返回HTTP Request对象,也就是响应报文。

HTTP 响应报文也是由三部分组成: 状态码, 响应报头和响应报文。

状态码由三位数字组成,表示响应的类型。状态码有五大类 1xx,2xx,3xx,4xx,5xx

  • 1xx:指示信息–表示请求已接收,继续处理。
  • 2xx:成功–表示请求已被成功接收、理解、接受。
  • 3xx:重定向–要完成请求必须进行更进一步的操作。
  • 4xx:客户端错误–请求有语法错误或请求无法实现。
  • 5xx:服务器端错误–服务器未能实现合法的请求。

HTTP 状态码-菜鸟编程

响应报头 HTTP 请求头提供了关于请求,响应或者其他的发送实体的信息。 HTTP 响应头信息-菜鸟编程

响应报文:就是服务器返回给浏览器的文本信息了,包括 html,js,css 等资源

浏览器解析渲染页面

浏览器拿到响应报文之后,开始解析报文并呈现网页。由于不同的浏览器引擎实现的方法可能不一致,我们以webkit内核为例进行说明。 WebKit 渲染的过程大致分为四步 浏览器的工作原理:新式网络浏览器幕后揭秘

构建 dom 树 -> 构建 render 树 -> 布局 render 树 -> 绘制 render 树

呈现引擎将开始解析 HTML 文档,并将各标记逐个转化成“内容树”上的 DOM 节点。同时也会解析外部 CSS 文件以及样式元素中的样式数据。HTML 中这些带有视觉指令的样式信息将用于创建另一个树结构:呈现树。

呈现树包含多个带有视觉属性(如颜色和尺寸)的矩形。这些矩形的排列顺序就是它们将在屏幕上显示的顺序。

呈现树构建完毕之后,进入“布局”处理阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标。下一个阶段是绘制 - 呈现引擎会遍历呈现树,由用户界面后端层将每个节点绘制出来。

需要着重指出的是,这是一个渐进的过程。为达到更好的用户体验,呈现引擎会力求尽快将内容显示在屏幕上。它不必等到整个 HTML 文档解析完毕之后,就会开始构建呈现树和设置布局。在不断接收和处理来自网络的其余内容的同时,呈现引擎会将部分内容解析并显示出来。

上面说到: 浏览器是一个边解析边渲染的过程。这个过程就涉及到两个比较重要的概念:重绘(repain)和回流(reflow)。

当页面中元素样式的改变并不影响它在文档流中的位置时(例如:colorbackground-colorvisibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。

Render Tree中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。

会导致回流的操作

  • 页面首次渲染
  • 浏览器窗口大小发生改变
  • 元素尺寸或位置发生改变
  • 元素内容变化(文字数量或图片大小等等)
  • 元素字体大小变化
  • 添加或者删除可见的 DOM 元素
  • 激活 CSS 伪类(例如::hover)
  • 查询某些属性或调用某些方法

会导致回流的属性和方法

  • clientWidthclientHeightclientTopclientLeft
  • offsetWidthoffsetHeightoffsetTopoffsetLeft
  • scrollWidthscrollHeightscrollTopscrollLeft
  • scrollIntoView()scrollIntoViewIfNeeded()
  • getComputedStyle()
  • getBoundingClientRect()
  • scrollTo()

页面在首次加载的时候必定会经历reflowrepainreflowrepain 过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少 reflowrepain

连接结束(四次挥手)

TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),也叫做改进的三次握手。客户端或服务器均可主动发起挥手动作,在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作。

  1. 第一次挥手(FIN=1,seq=x)

假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为 1 的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。

发送完毕后,客户端进入 FIN_WAIT_1 状态。

  1. 第二次挥手(ACK=1,ACKnum=x+1)

服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。

发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。

  1. 第三次挥手(FIN=1,seq=y)

服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为 1。

发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个 ACK。

  1. 第四次挥手(ACK=1,ACKnum=y+1)

客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT 状态,等待可能出现的要求重传的 ACK 包。

服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。

客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。

image

【问题 1】为什么连接的时候是三次握手,关闭的时候却是四次握手? 答:因为当 Server 端收到 Client 端的 SYN 连接请求报文后,可以直接发送 SYN+ACK 报文。其中 ACK 报文是用来应答的,SYN 报文是用来同步的。但是关闭连接时,当 Server 端收到 FIN 报文时,很可能并不会立即关闭 SOCKET,所以只能先回复一个 ACK 报文,告诉 Client 端,"你发的 FIN 报文我收到了"。只有等到我 Server 端所有的报文都发送完了,我才能发送 FIN 报文,因此不能一起发送。故需要四步握手。

【问题 2】为什么 TIME_WAIT 状态需要经过 2MSL(最大报文段生存时间)才能返回到 CLOSE 状态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入 CLOSE 状态了,但是我们必须假象网络是不可靠的,有可以最后一个 ACK 丢失。所以 TIME_WAIT 状态就是用来重发可能丢失的 ACK 报文。

参考资料