面试题:【当键入网址后,到网页显示,期间发生了什么?

下面一张较简单的网络拓扑模型作为例子,探究探究其间发生了什么?

HTTP


浏览器做的第一步工作是解析****URL

首先浏览器做的第一步工作就是要对URL进行解析,从而生成发生给Web服务器的请求信息

在一条长长的URL里的各个元素的代表是什么

这里的URL实际上是请求服务器里的文件资源

当上图的蓝色部分URL元素都省略了,那么请求的是哪个文件呢?

当没有路径名时,就代表访问根目录下事先设置的默认文件,也就是/index.html或者/default.html这些文件,这样就不好发生混乱

生产HTTP请求信息

URL进行解析之后,浏览器确定了Web服务器和文件名,接下来就是根据这些信息来生产HTTP请求信息

真实地址查询——DNS


通过浏览器解析URL并生成HTTP消息后,需要委托操作系统将消息发送给Web服务器

但是在发送之前,还需要查询服务器域名对应的IP地址,因为委托操作系统发送消息时,必须提供通信对象的IP地址

这里,有一种服务器就专门保存了Web服务器域名与IP的对应关系——DNS服务器

域名的层级关系

DNS中的域名都是用句点来分隔的,比如www.server.com,这里的句点带不了不同层次之间的界限

在域名中,越靠右的位置表示其层级越高

实际上域名最后还有一个点,比如www``.server.com.,这个最后一个点代表根域名

也就是说,.根域是最顶层,它的下一层是.com顶级域,再下面是server.com

所以,域名的层级关系类似一个树状结构

  • 根DNS服务器.

  • 顶级域DNS服务器.com

  • 权威DNS服务器server.com

根域的DNS服务器信息保存在互联网中所有的DNS服务器中,这样一来任何DNS服务器都可以找到并访问根域DNS服务器

因此,客户端只要能够找到任意一台 DNS 服务器,就可以通过它找到根域 DNS 服务器,然后再一路顺藤摸瓜找到位于下层的某台目标 DNS 服务器。

域名解析的工作流程

  1. 客户端首先会发出一个DNS请求,问www.server.com的IP是啥,并发给本地DNS服务器(也就是客户端的TCP/IP设置中填写的DNS服务器地址)

  2. 本地域名服务器收到客户端的请求后,如果缓存里的表格能找到www.server.com,则直接返回IP地址,如果没有,本地DNS会去问它的根域名服务器, 根域名服务器是最高层次的,它不直接用于域名解析,但能指明一条道路。

  3. 根DNS收到本地DNS的请求后,根据www.server.com的后置是.com,这个域名归于.com区域管理,返回.com顶级域名服务器地址给本地

  4. 本地DNS收到顶级域名服务器地址后,向顶级域名服务器请求负责www.server.com的权威DNS服务器的地址,

  5. 本地DNS最后向权威DNS服务器请求www.server.com的IP地址,该server.com的权威服务器就是域名解析结果的出处。

  6. 权威DNS服务器查询结后将对应的IP地址X.X.X.X返回到本地DNS

  7. 本地DNS再将IP地址返回客户端,客户端和目标建立连接

至此,DNS的解析过程完成了,其过程可见下图

域名解析并不是每次都需要经过这么多步骤

还有缓存的存在。

浏览器会先看自身有没有对这个域名的缓存,如果有,就直接返回,如果没有,就去问操作系统,操作系统也会去看自己的缓存,如果有,就直接返回,如果没有,再去 hosts 文件看,也没有,才会去问「本地 DNS 服务器」。

协议栈


通过DNS获取IP后,就可以把HTTP的传输工具交给操作系统中的协议栈

协议栈的内部分为几个部分,分别承担不同的工作。上下关系是有一定的规则的,上面的部分会向下面的部分委托工作,下面的部分收到委托的工作并执行。

应用程序(浏览器)通过调用 Socket 库,来委托协议栈工作。协议栈的上半部分有两块,分别是负责收发数据的 TCP 和 UDP 协议,这两个传输协议会接受应用层的委托执行收发数据的操作。

协议栈的下面一半是用 IP 协议控制网络包收发操作,在互联网上传数据时,数据会被切分成一块块的网络包,而将网络包发送给对方的操作就是由 IP 负责的。

此外 IP 中还包括 ICMP 协议和 ARP 协议。

  • ICMP 用于告知网络包传送过程中产生的错误以及各种控制信息。

  • ARP 用于根据 IP 地址查询相应的以太网 MAC 地址。

IP 下面的网卡驱动程序负责控制网卡硬件,而最下面的网卡则负责完成实际的收发操作,也就是对网线中的信号执行发送和接收操作。

可靠传输——TCP


HTTP是基于TCP协议传输的,

TCP包头格式

TCP 报文头部的格式:

首先,源端口号目标端口号是必不可少的,如果没有这两个端口号,数据不知道发送给哪些应用

接下来有包的序号,这个是为了解决包乱序的问题

还有确认号,目的是确认发出去对方是否有收到。如果没有收到就重新发送直到送达,这是是为了解决丢包问题

还有一些状态位。例如 SYN 是发起一个连接,ACK 是回复,RST 是重新连接,FIN 是结束连接等。TCP 是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。

还有一个重要的就是窗口大小。TCP要做流量控制,通信双方各声明一个窗口(缓存大小),标识自己当前能够提供的处理能力,以控制流量发送的快慢

除了流量控制以外,TCP还做了拥塞控制,能够控制数据发送的速度

TCP传输数据之前,要先三次握手建立连接

在HTTP传输数据之前,首先需要TCP建立连接,TCP连接的建立,通常称为三次握手

这个所谓的「连接」,只是双方计算机里维护一个状态机,在连接建立的过程中,双方的状态变化时序图就像这样。

  • 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。

  • 然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。

  • 服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。

  • 客户端收到服务端发送的 SYNACK 之后,发送对 SYN 确认的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。

  • 服务端收到 ACKACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。

所以三次握手目的是保证双方都有发送和接收的能力

如何查看TCP的连接状态

TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。

TCP分割数据

如果 HTTP 请求消息比较长,超过了 MSS 的长度,这时 TCP 就需要把 HTTP 的数据拆解成一块块的数据发送,而不是一次性发送所有数据。

  • MTU:一个网络包的最大长度,以太网中一般为 1500 字节。

  • MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度。

数据会被以 MSS 的长度为单位进行拆分,拆分出来的每一块数据都会被放进单独的网络包中。也就是在每个被拆分的数据加上 TCP 头信息,然后交给 IP 模块来发送数据。

TCP报文生成

TCP协议里面会有两个端口,一个是浏览器监听的端口(通常为随机生成),一个是Web服务器监听的端口(HTTP默认是80,HTTPS默认是443

双方建立连接后,TCP报文中的数据部分就是存放了HTTP头部+数据,组装好TCP报文之后,就需要交给下面的网络层处理

至此,网络包的报文如下图。

远程定位——IP


TCP模块在执行连接、收发、断开等阶段操作时,都需要委托IP模块将数据封装成网络包发送给通信对象

IP包头格式

在IP协议中,需要有源地址IP目标地址IP

  • 源地址IP:客户端输出的IP地址

  • 目标地址:通过DNS域名解析得到的Web服务器IP

因为HTTP是经过TCP传输的,所有在IP包头的协议号,要填写06(十六进制),表示协议为TCP协议

假设客户端有多个网卡,就会有多个IP地址,那么IP头部的源地址应该选择哪个地址?

这个判断相当于在多块网卡中,判断应该使用哪个网卡来发送包

这时候就需要根据路由表规则,来判断哪一个网卡作为源地址IP

在 Linux 操作系统,我们可以使用 route -n 命令查看当前系统的路由表。

举个例子,根据上面的路由表,我们假设 Web 服务器的目标地址是 192.168.10.200

  1. 首先先和第一条目的子网掩码(Genmask)进行 与运算,得到结果为 192.168.10.0,但是第一个条目的 Destination192.168.3.0,两者不一致所以匹配失败。

  2. 再与第二条目的子网掩码进行 与运算,得到的结果为 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发送方和接收方如何确认?

发送方的MAC地址获取简单,MAC地址在网卡生产时写入到ROM里面,只要将这个值读取出来写入到MAC头部就行

接收方的MAC地址就有点复杂,只要告诉以太网对方的MAC地址,以太网就会把包发送过去,那么对方的MAC地址哪里来呢?

这是得先清楚应该把包发给谁,需要查询一下路由表,在路由表中找到匹配的条目,然后把包发送给Gateway列中的IP地址

如何获取对方的MAC地址?

这里需要ARP协议帮我们找到路由器的MAC地址

ARP协议会在以太网中以广播的形式,对以太网所有的设备进行广播,如果对方和自己处在同一张子网中,那么上面操作就可可以得到对方的MAC地址,那么将这个MAC地址写入MAC头部后,MAC头部就完成了

ARP缓存

在得到对方的MAC地址后,操作系统会将本次的查询结果放到一块ARP缓存的内存空间,不过缓存保存的时间就几分钟

也就是说,在发包时:

  • 先查询 ARP 缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询,直接使用 ARP 缓存中的地址。

  • 而当 ARP 缓存中不存在对方 MAC 地址时,则发送 ARP 广播查询。

查看 ARP 缓存内容

在 Linux 系统中,我们可以使用 arp -a 命令来查看 ARP 缓存的内容。

MAC 报文生成

至此,网络包的报文如下图。

出口——网卡


网络包只是存放在内存中的一串二进制数字信息,没有办法直接发送给对方。因此,我们需要将数字信息转换为电信号,才能在网线上传输,也就是说,这才是真正的数据发送过程。

负责执行这一操作的是网卡,要控制网卡还需要靠网卡驱动程序

网卡驱动获取网络包之后,会将其复制到网卡内的缓存区中,接着会在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列

  • 起始帧分界符是一个用来表示包起始位置的标记

  • 末尾的 FCS(帧校验序列)用来检查包传输过程是否有损坏

最后网卡会将包转为电信号,通过网线发送出去。

交换机


下面来看一下包是如何通过交换机的。交换机的设计是将网络包原样转发到目的地。交换机工作在 MAC 层,也称为二层网络设备

交换机的包接收操作

……

服务器 与 客户端


数据包抵达服务器后,服务器会先扒开数据包的 MAC 头部,查看是否和服务器自己的 MAC 地址符合,符合就将包收起来。

接着继续扒开数据包的 IP 头,发现 IP 地址符合,根据 IP 头中协议项,知道自己上层是 TCP 协议。

于是,扒开 TCP 的头,里面有序列号,需要看一看这个序列包是不是我想要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。TCP头部里面还有端口号, HTTP 的服务器正在监听这个端口号。

于是,服务器自然就知道是 HTTP 进程想要这个包,于是就将包发给 HTTP 进程。

服务器的 HTTP 进程看到,原来这个请求是要访问一个页面,于是就把这个网页封装在 HTTP 响应报文里。

HTTP 响应报文也需要穿上 TCP、IP、MAC 头部,不过这次是源地址是服务器 IP 地址,目的地址是客户端 IP 地址。