网络综合篇
浏览器写入网址到网页显示,这期间都发生了什么?
目录
❓:当你在浏览器中输入 google.com 并且按下回车之后发生了什么?
按下"G"键
当你按下G键,浏览器接收到了这个字符,会触发自动完成机制。浏览器会根据自己的算法,以及你是否处于隐私浏览模式,会在浏览器的输入框下方出现下拉列表提供建议。大部分这些算法会优先考虑根据你的的搜索历史和书签,cookie和整个互联网的流行搜索对结果进行排序和优先排序,给出最适合的建议。 当您键入“google.com”时,会运行许多代码块,并且每次按键都会改进建议。 它甚至可能会在您完成输入之前提示“google.com”。
当你按下回车,浏览器就会收到你需要跳转的网址。
解析URL,生成HTTP请求报文
浏览器收到网址的第一步就是对URL进行解析。
URL的组成
💡:如果没有路径名,就代表访问根目录下的设置的默认文件!例如:
/index.html
浏览器将URL解析完成后,会提取相关信息(web服务器域名和文件名)。会根据这些信息生产HTTP请求信息。
HTTP请求格式
查询IP地址-DNS
生成HTTP消息报文,需要委托操作系统将消息发送给web 服务器。但是网络巨大,怎么去找到确定的服务器呢?而且一般输入的网址是一串由点分隔的字符串,怎么找到web 服务器域名对应的IP地址呢,毕竟操作系统要根据ip地址才能发送消息的。
DES 服务器专门保存了Web 服务器域名与IP 的对应关系。
域名的组成
DNS 域名都是由句点. 来进行分隔得。
例如:www.goole.com 分割得每一部分都代表不同得层次,越靠后,层级越大
域名层级结构:
- 根
DNS服务器 - 顶级域名
DNS服务器 - 权限
DNS服务器 - 本地域名服务器
这样就可以根据域名得不同组成逐一精确找到对应得IP 地址
域名解析得工作流程
- 本机会向本地得域名服务器发送DNS请求,查询
www.goole.com的IP - 本地域名服务器接收到请求后,会进行迭代查询。
- 先查询本地域名服务器的缓存里面,如果没有查询到IP地址,会向一个根域名服务器进行查询
- 根域名服务器收到后,会告诉对应的顶级域名服务器(
.com)的对应的IP地址 - 本地域名服务器会收到后,就会向顶级域名服务器进行查询
- 顶级域名服务器会告诉下一次需要查询的权限域名服务器的IP地址
- 本地域名服务器收到后,会向权限域名服务器进行查询
- 权限域名服务器查询到
www.goole.com的IP地址,发送给本地域名服务器 - 本地域名服务器拿到IP地址会返回给本机(客户端)
可靠传输—TCP(传输层)
HTTP是基于TCP协议进行传输的。
💡:关于TCP的一些知识可以看下面这篇文章:计算机网络面试题汇总之TCP篇
TCP连接
在HTTP传输数据之前,首先需要TCP建立连接(三次握手)。
- 一开始,客户端和服务端都处于
CLOSED状态。先是服务端主动监听某个端口,处于LISTEN状态。 - 然后客户端主动发起连接
SYN,之后处于SYN-SENT状态。 - 服务端收到发起的连接,返回
SYN,并且ACK客户端的SYN,之后处于SYN-RCVD状态。 - 客户端收到服务端发送的
SYN和ACK之后,发送ACK的ACK,之后处于ESTABLISHED状态,因为它一发一收成功了。 - 服务端收到
ACK的ACK之后,处于ESTABLISHED状态,因为它也一发一收了。
保证双方有接受与发送的能力!
分割数据包
如果HTTP的消息过长,超过了MSS 的长度的话,TCP就会把HTTP的数据拆解成一个一个的数据包进行发送,每一个拆分出来的数据包都会加上TCP头信息,然后交到IP协议来发送数据。
MTU 与MSS 的区别
- MTU:一个网络包的最大长度,以太网中一般为 1500 字节。
- MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度。
TCP报文生成
TCP 协议里面会有两个端口,一个是浏览器监听的端口(通常是随机生成的),一个是 Web 服务器监听的端口(HTTP 默认端口号是 80, HTTPS 默认端口号是 443)。
网络包的报文如下:
远程定位—IP(网路层)
TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托IP 模块将数据封装成网络包发送给通信对象。
IP会选择合适的路由和交换的节点,确保数据及时发送出去。
IP包头格式
源地址IP,即是客户端输出的 IP 地址;
- 目标地址,即通过 DNS 域名解析得到的 Web 服务器 IP。
但是也有一个问题。
❓:假设客户端有多个网卡,就会有多个 IP 地址,那 IP 头部的源地址应该选择哪个 IP 呢?
这个时候会根据路由表规则,来判断选择哪一个网卡作为源地址IP
过程如下:
路由规则判断
- 首先先和第一条目的子网掩码(
Genmask)进行 与运算,得到结果为192.168.10.0,但是第一个条目的Destination是192.168.3.0,两者不一致所以匹配失败。 - 再与第二条目的子网掩码进行 与运算,得到的结果为
192.168.10.0,与第二条目的Destination 192.168.10.0匹配成功,所以将使用eth1网卡的 IP 地址作为 IP 包头的源地址。
假设 Web 服务器的目标地址是 10.100.20.100,那么依然依照上面的路由表规则判断,判断后的结果是和第三条目匹配。
第三条目比较特殊,它目标地址和子网掩码都是 0.0.0.0,这表示默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行。并且后续就把包发给路由器,Gateway 即是路由器的 IP 地址。
IP报文生成
两点传输—MAC(数据链路层)
IP包会送到数据链路层,同时会在IP包头部加上MAC 头部。
MAC包头部格式

在 MAC 包头里需要发送方 MAC 地址和接收方目标 MAC 地址,用于两点之间的传输。
一般在 TCP/IP 通信里,MAC 包头的协议类型只使用:
0800: IP 协议0806: ARP 协议
❓:如何获取对方的MAC地址呢?
需要根据ARP 协议帮我们找到对方的MAC地址
ARP 协议会以广播的形式,查询连接的所有设备,查询到对方的MAC地址后,就写入接收方的MAC。
在后续操作系统会把本次查询结果放到一块叫做 ARP 缓存的内存空间留着以后用,不过缓存的时间就几分钟。
也就是说,在发包时:
- 先查询 ARP 缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询,直接使用 ARP 缓存中的地址。
- 而当 ARP 缓存中不存在对方 MAC 地址时,则发送 ARP 广播查询。
MAC报文生成
出口-网卡(物理层)
上层交付的数据包时放在内存的一串二进制数字信息,没有办法直接发送给对方。需要将数字信息转换成电信号,才能在网线上传输。
负责执行这一操作的是网卡,要控制网卡还需要靠网卡驱动程序。
网卡驱动从 IP 模块获取到包之后,会将其复制到网卡内的缓存区中,接着会在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列。
- 起始帧分界符是一个用来表示包起始位置的标记
- 末尾的
FCS(帧校验序列)用来检查包传输过程是否有损坏
至此可以发送到目的网络地址的数据包就诞生了,但是离到达还需要经过几个地方。
交换机
交换机的设计是将网络包原样转发到目的地。交换机工作在 MAC 层,也称为二层网络设备。
交换机的接受操作
首先,接受发过来的电信号,然后交换机将电信号转换成数字信号。通过包末尾的FCS校验错误。有问题就直接丢掉,没问题就会放到缓冲区。
交换机的端口不具有MAC地址,他只是起到将数据原样交换到对方。
将包存入缓冲区后,接下来需要查询一下这个包的接收方 MAC 地址是否已经在 MAC 地址表中有记录了。
- 如果有就直接交付给MAC记录的端口。
- 如果没有找到对应的端口。将包转发到除了源端口之外的所有端口上,无论该设备连接在哪个端口上都能收到这个包。那么只有相应的接收者才接收包,而其他设备则会忽略这个包。
交换机的 MAC 地址表主要包含两个信息:
- 一个是设备的 MAC 地址,
- 另一个是该设备连接在交换机的哪个端口上。
交换机根据 MAC 地址表查找 MAC 地址,然后将信号发送到相应的端口。
路由器
路由器基本原理
路由器的端口具有 MAC 地址,因此它就能够成为以太网的发送方和接收方;同时还具有 IP 地址,从这个意义上来说,它和计算机的网卡是一样的。
当转发包时,首先路由器端口会接收发给自己的以太网包,然后路由表查询转发目标,再由相应的端口作为发送方将以太网包发送出去。
路由器包接受工作
首先,电信号到达网线接口部分,路由器中的模块会将电信号转成数字信号,然后通过包末尾的 FCS 进行错误校验。
如果没问题则检查 MAC 头部中的接收方 MAC 地址,看看是不是发给自己的包,如果是就放到接收缓冲区中,否则就丢弃这个包。
总的来说,路由器的端口都具有 MAC 地址,只接收与自身地址匹配的包,遇到不匹配的包则直接丢弃。
确定输出端口
接收到数据包后,路由器会去掉包开头的MAC头部。
MAC 头部的作用就是将包送达路由器,其中的接收方 MAC 地址就是路由器端口的 MAC 地址。因此,当包到达路由器之后,MAC 头部的任务就完成了,于是 MAC 头部就会被丢弃。
然后路由器会根据MAC头部后方的IP头部中的内容进行包转发工作。
转发过程如下
查询路由表判断转发端口,将其数据进行转发。
❓:如何查询路由表?
每个条目的子网掩码和 192.168.1.100 IP 做 & 与运算后,得到的结果与对应条目的目标地址进行匹配,如果匹配就会作为候选转发目标,如果不匹配就继续与下个条目进行路由匹配。
举例:
第二条目的子网掩码 255.255.255.0 与 192.168.1.100 IP 做 & 与运算后,得到结果是 192.168.1.0 ,这与第二条目的目标地址 192.168.1.0 匹配,该第二条目记录就会被作为转发目标。
如果找不到匹配的路由,那么就会选择默认路由。
路由器发送操作
既然要发送数据,肯定要判断对方在哪?
首先,我们需要根据路由表的网关列判断对方的地址。
- 如果网关是一个 IP 地址,则这个IP 地址就是我们要转发到的目标地址,还未抵达终点,还需继续需要路由器转发。
- 如果网关为空,则 IP 头部中的接收方 IP 地址就是要转发到的目标地址,也是就终于找到 IP 包头里的目标地址了,说明已抵达终点。
知道对方的 IP 地址之后,接下来需要通过 ARP 协议根据 IP 地址查询 MAC 地址,并将查询的结果作为接收方 MAC 地址。
路由器也有 ARP 缓存,因此首先会在 ARP 缓存中查询,如果找不到则发送 ARP 查询请求。
既然拿到了对方的MAC地址,就会给数据包封装MAC头部。
将数据信号转换成电信号,通过端口送去。
中途过程
发送的数据包又会通过交换机到达下一个路由器,
接收方 MAC 地址就是下一个路由器的地址,所以交换机会根据这一地址将包传输到下一个路由器。
接下来,下一个路由器会将包转发给再下一个路由器,经过层层转发之后,网络包就到达了最终的目的地。
在传输到目的地址过程中,源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址,因为需要 MAC 地址在以太网内进行两个设备之间的包传输。
到达目的地址
接收端就会跟拆快递一样,不断的拆开发送到的数据包的头部,并且进行校验。以这样的方式去拿到所需要的核心数据。
之后双方就会建立TCP连接,然后web服务器就会向本机发送数据,然后就会关闭TCP连接,浏览器就会进行解析渲染页面。
连接结束
当数据传输完毕,需要断开tcp连接,发起tcp四次挥手。
- 客户端打算关闭连接,此时会发送一个
FIN置为1的TCP报文,即FIN报文,之后客户端进入FIN_WAIT_1状态 - 服务端收到此报文后,会回复一个
ACK确认应答报文,之后服务端进入CLOSED_WAIT状态 - 客户端收到服务端发过来的
ACK应答报文,之后进入FIN_WAITE_2状态 - 等待服务端处理完数据后,也向客户端发送
FIN报文之后服务端进入LAST_ACK状态 - 客户端收到服务端的
FIN报文后,向服务端发送ACK确认应答报文,之后进入TIME_WAIT状态 - 服务端收到客户端发送的
ACK确认应答报文后,就直接进入CLOSE状态,至此服务端已经完成连接关闭 - 客户端经过
2MSL一段时间后,自动进入CLOSED状态,客户端完成连接关闭
浏览器解析渲染
参考博客:渲染页面:浏览器的工作原理 - Web 性能 | MDN
当浏览器收到数据,就会开始解析收到的信息。“推测性解析”,“解析”是浏览器将通过网络接收的数据转换为DOM和CSSOM的步骤,通过渲染器把DOM和CSSOM在屏幕上绘制成页面。
构建DOM树
文档对象模型 (DOM) 是HTML和XML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容。DOM 将文档解析为一个由节点和对象(包含属性和方法的对象)组成的结构集合。简言之,它会将web页面和脚本或程序语言连接起来。
第一步是处理HTML标记,构建DOM树。HTML标记分为开始标记和结束标记,属性名,属性值等,如果文档格式良好,则解析它会简单而快速。解析器将标记化的输入解析到文档中,构建文档树。
DOM树描述了文档的内容。<html>元素是第一个标签也是文档树的根节点。树反映了不同标记之间的关系和层次结构。嵌套在其他标记中的标记是子节点。DOM节点的数量越多,构建DOM树所需的时间就越长。
当解析器发现非阻塞资源,例如一张图片,浏览器会请求这些资源并且继续解析。当遇到一个CSS文件时,解析也可以继续进行,**但是对于<script>标签(特别是没有 async 或者 defer 属性)会阻塞渲染并停止HTML的解析。**尽管浏览器的预加载扫描器加速了这个过程,但过多的脚本仍然是一个重要的瓶颈。
预加载扫描器
**预加载扫描仪将解析可用的内容并请求高优先级资源,如CSS、JavaScript和web字体。**多亏了预加载扫描器,我们不必等到解析器找到对外部资源的引用来请求它。它将在后台检索资源,以便在主HTML解析器到达请求的资源时,它们可能已经在运行,或者已经被下载。预加载扫描仪提供的优化减少了阻塞。
<link rel="stylesheet" src="styles.css"/>
<script src="myscript.js" async></script>
<img src="myimage.jpg" alt="image description"/>
<script src="anotherscript.js" async></script>
在这个例子中,**当主线程在解析HTML和CSS时,预加载扫描器将找到脚本和图像,并开始下载它们。**为了确保脚本不会阻塞进程,当JavaScript解析和执行顺序不重要时,可以添加async属性或defer属性。
等待获取CSS不会阻塞HTML的解析或者下载,但是它的确阻塞JavaScript,因为JavaScript经常用于查询元素的CSS属性。
构建CSSOM树
浏览器将**CSS规则转换为可以理解和使用的样式映射。**浏览器遍历CSS中的每个规则集,根据CSS选择器创建具有父、子和兄弟关系的节点树。
浏览器需要将接收到的CSS规则转换为可以使用的内容。
CSSOM树包括来自用户代理样式表的样式。浏览器从适用于节点的最通用规则开始,并通过应用更具体的规则递归地优化计算的样式。换句话说,它级联属性值。
构建CSSOM非常非常快,创建CSSOM的总时间通常小于一次DNS查找所需的时间。
其他过程
javascript编译
JavaScript被解释、编译、解析和执行。脚本被解析为抽象语法树。一些浏览器引擎使用”Abstract Syntax Tree“并将其传递到解释器中,输出在主线程上执行的字节码。这就是所谓的JavaScript编译。
构建辅助功能树
浏览器还构建辅助设备用于分析和解释内容的辅助功能(accessibility )树。可访问性对象模型(AOM)类似于DOM的语义版本。当DOM更新时,浏览器会更新辅助功能树。辅助技术本身无法修改可访问性树。
渲染
渲染步骤包括样式、布局、绘制,在某些情况下还包括合成。
解析步骤:
- 将DOM和CSSOM组合成一个Render树,计算样式树或渲染树从DOM树的根开始构建,遍历每个可见节点。
💡:
<head>和它的子节点以及任何具有display: none样式的结点,它们不会出现在Render树上。
❗:具有
visibility: hidden的节点会出现在Render树上,因为它们会占用空间。
每个可见节点都应用了其CSSOM规则。
Render树保存所有具有内容和计算样式的可见节点——将所有相关样式匹配到DOM树中的每个可见节点,并根据CSS级联确定每个节点的计算样式。(用于后面的布局)
- 在渲染树上运行布局以计算每个节点的几何体。
💡:布局是确定呈现树中所有节点的宽度、高度和位置,以及确定页面上每个对象的大小和位置的过程。
💡:回流是对页面的任何部分或整个文档的任何后续大小和位置的确定。
为了确定每个对象的确切大小和位置,浏览器从渲染树的根开始遍历它。
在此阶段,考虑到视区大小,浏览器将确定屏幕上所有不同框的尺寸。以视区的大小为基础,布局通常从body开始,用每个元素的框模型属性排列所有body的子孙元素的尺寸,为不知道其尺寸的替换元素(例如图像)提供占位符空间。
- 将各个节点绘制到屏幕上。
在绘制或光栅化阶段,浏览器将在布局阶段计算的每个框转换为屏幕上的实际像素。
绘画包括将元素的每个可视部分绘制到屏幕上,包括文本、颜色、边框、阴影和替换的元素(如按钮和图像)。
为了确保重绘的速度比初始绘制的速度更快,屏幕上的绘图通常被分解成数层。
绘制可以将布局树中的元素分解为多个层。将内容提升到GPU上的层(而不是CPU上的主线程)可以提高绘制和重新绘制性能。
🎮:有一些特定的属性和元素可以实例化一个层,包括
<video>和<canvas>.
- 当文档的各个部分以不同的层绘制,相互重叠时,必须进行合成,以确保它们以正确的顺序绘制到屏幕上,并正确显示内容。
解析所有步骤:
- 处理HTML标记并构造DOM树。
- 处理CSS并构建CSSOM树。
- 将DOM和CSSOM组合成一个Render树,计算样式树或渲染树从DOM树的根开始构建,遍历每个可见节点。
- 渲染树上运行布局以计算每个节点的几何体。
- 将各个节点绘制到屏幕上。