在浏览器输入一个网址期间发生了什么?

解析 URL

首先浏览器做的第一步就是要对 URL 进行解析,从而生成发送给 Web 服务器的请求信息,让我们看一下 URL 包括什么,如图:

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

在浩瀚复杂的网络中,到底是如何通过这 URL 来发送到远在天边的服务器呢?

地址查询 —-DNS

域名服务器

浏览器解析 URL 生成 HTTP 消息后,会通过操作系统发送给 Web 服务器。但是在发送之前需要进行服务器域名所对应的 IP 查询,因为操作系统的协议栈中的网络层是必须知道目的端的 ip 地址的。
就好比我们打电话的时候,必须知道对方的电话号码,但是电话号码难以记忆,所以我们会将对方电话号 + 姓名保存在通讯录里。
所以,有一种服务器专门保存了 Web 服务器域名和 IP 的对应关系,他就是 DNS 服务器。
DNS 中的域名都是句点来分隔开的,比如 www.server.com. 越靠右的位置表示其层级越高,实际上域名后面还会有一个点,比如 www.server.com., 这里最后一个。代表根域名在最顶层,它的下一层就是.com 顶级域,再下面是 server.com 权威 DNS 服务器.

根域 DNS 服务器的信息保存在互联网中所有 DNS 服务器中,这样任何 DNS 服务器都可以找到并访问根 DNS 服务器了。因此客户端只要能够找到任意一台 DNS 服务器,就可以通过它找到根域服务器,然后一路顺藤摸瓜找到目标 DNS 服务器。

域名解析的工作流程:

1. 客户端会先发出一个 DNS 请求,问 www.server.com 的 IP 是啥,发送给本地 DNS 服务器 (也就是在客户端的 TCP/IP 设置中填写的 DNS 服务器地址,在 Windows 下是 “控制面板”->” 网络和 Internrt”->” 网络和共享中心”->” 更改适配器设置”-> 右键当前网络连接 (如 Wwi-Fi / 以太网)->” 属性”-> 双击 “Internet 协议版本 4 (TCP/IPv4)” 即可看到,在 Linux 下可以查看 /etc/resolv.conf)
2. 本地域名服务器收到客户端的请求后,如果缓存里找到则直接返回 ip 地址,如果没有本地 DNS 回去问他的根域名服务器,根域名服务器是最高层次的,他不会直接用于域名解析,但是会指出一条明路。
3. 根 DNS 收到来自本地 DNS 请求后,发现后置是.com 说”www.server.com” 这个域名归.com 区域管理,我给你.com 顶级域名服务器地址给你,你去问问它吧”
4. 本地 DNS 收到顶级域名服务器的地址后,去查询。
5. 顶级域名服务器说,” 我给你负责 www.server.com 区域的 DNS 服务器的地址,你去问他”.
6. 本地 DNS 于是转向权威 DNS 服务器:”www.server.com 对应的 IP 是啥呀?”,权威 DNS 服务器工作就是域名解析,查询后返回本地 DNSIP 地址是”x.x.x.x”
7. 本地 DNS 再将 IP 地址返回给客户端,客户端和目标建立连接。

这就是 DNS 解析的全部过程,但是并不是每次解析域名都需要这么多的步骤,这样效率太低,因此需要使用到缓存,浏览器会先查看自身有没有对应域名的缓存,如果有直接返回,没有就去问操作系统,操作系统也会看自己的缓存,如果有就直接返回,如果没有再去 hosts 文件看,也没有才回去问本地配置的 DNS 服务器。

顺便提一下,这里的 hosts 文件是什么呢?
在 DNS 解析过程中,​hosts 是操作系统中一个静态的本地域名解析文件,用于手动指定域名和 IP 地址的映射关系。它的优先级高于 DNS 查询,系统在进行 DNS 解析时,会首先检查 hosts 文件,如果文件中存在对应域名的记录,则直接使用该记录,而不会向 DNS 服务器发起请求。
比如我们可以手动配置域名和 IP 映射,覆盖全局 DNS 解析结果,127.0.0.1 localhost,或者将一些恶意的域名指向 hosts 文件中定义的无效 ip 0.0.0.0,我们可以使用 sudo nano /etc/hosts 来查看 hosts 文件中的配置。

实践

理论始终是理论,让我们实践来看一下 DNS 的过程,以 Linux 操作系统为例,首先要确定我们使用的 DNS 服务是什么?如 (systemd-resolved、nscd 或 dnsmasq),我的是 systemd-resolved,通过 resolvectl statistics 命令可以查看当前的缓存状态,如:

抓一下本地 DNS 服务的包:
1.sudo resolvectl flush-caches 清空缓存
2. 修改配置文件 /etc/systemd/resolved.conf 进行添加
[Resolve]
Cache=yes
#代表启用缓存
DNSStubListener=udp
#控制 systemd-resolved 是否监听本地回环接口(127.0.0.53:53)的 DNS 请求,yes 表示同时监听,no 禁止本地监听,udp 代表仅仅监听 udp 端口
3.sudo systemctl restart systemd-resolved
#重启 DNS 解析服务
4.sudo tcpdump -i lo -n port 53
#在本地回环接口 (lo) 上抓取所有 53 端口的 DNS 数据包
显示如下:

本地 41241 号端口向 53 号端口 DNS 服务询问 www.fffxy.xin 的 ip,其中 A 代表 ipv4,AAAA 代表 ipv6,查询完毕后 53 号端口程序返回给 41241 端口 www.fffxy.xin 的 ip 地址。

协议栈

通过 DNS 获取到 IP 之后,就可以把 HTTP 的传输工作交给操作系统的协议栈
协议栈的内部分为几个部分,分别承担不同的工作,上下关系是有一定的规则的,上面的部分会委托下面部分工作。


应用程序 (浏览器) 通过调用 Socket 库,来委托协议栈工作。协议栈的上半部分有两块,分别时负责收发数据的 TCP 和 UDP 协议,这两个传输协议会接受应用层的委托执行数据收发。协议栈的下面一半是 IP 协议控制网络包的收发操作,其中 IP 还包括 ICMP 协议和 ARP 协议。

  • ICMP:用于告知网络包传送过程中产生的错误以及各种控制信息。
  • ARP:用于根据 IP 地址来获取 MAC 地址。

传输层 —TCP

HTTP 协议是基于 TCP 协议传输的,让我们先了解一下 TCP 协议。
TCP 报文头部格式:

源端口和目的端口:用于确定发送给哪个应用。
包序号:为了解决乱序问题。
确认号:确认发出去的对方是否接收到,为了解决丢包问题。
状态位:SYN 是发起一个连接,ACK 是回复,RST 是重新连接,FIN 是结束连接,TCP 是面向连接的,因此双方要维护连接的状态,这些带状态位的数据包会引起双方状态变更。
窗口大小:TCP 要做流量控制,双方各自声明一个窗口大小,标识自己的处理能力,别发送太快,撑死我,也别发送太慢,饿死我。除了流量控制还会做拥塞控制。
在 HTTP 传输数据之前,首先需要 TCP 建立连接,TCP 连接的建立,通常称为三次握手。

可以使用 Linux 中的 netstat -tanp 查看 TCP 的连接状态。

如果 HTTP 的请求消息比较长,超出了 MSS 长度,此时 TCP 需要把 HTTP 的数据拆解成一块块的数据发送,而不是一次性发送完全部数据。

  • MTU:一个网络包的最大长度,以太网中一般为 1500 字节。
  • MSS:去除 IP 和 TCP 头部之后,一个网络包可以容纳的 TCP 数据的最大长度。

远程定位 —IP

看一下 IP 报文头部格式:

源地址 IP:就是客户端的 IP 地址。
目的地址 IP:就是 DNS 域名解析出来的 Web 服务器的 IP。
这里有一个问题:假设客户端有很多网卡,就会有多个 IP 地址,那 IP 头部的源地址应该选择哪个 IP 呢?
这个时候就要根据路由表规则来判断哪一个网卡作为源地址 IP。
在 Linux 操作系统,可以使用 route -n 命令查看当前系统的路由表。

通过目的 IP 和子网掩码进行与运算来抉择出从哪个网口发出报文。

两点传输 —MAC

生成了 IP 头部之后,接下来网络包还需要在 IP 头部前面加上 MAC 头部。

如果是在局域网内传输数据,那么会通过广播 ARP 来获取目标主机的 MAC 地址。
如果在广域网中,则是通过不断更换目的端的 MAC 地址来实现数据包的传递。
至此网络包的报文如下:

出口 — 网卡

数据包只不过是在内存中的二进制数据,没办法直接发送给对方,因此,我们需要将数字信息转换为电信号,才可以在网线上传输,这才是真正的发送数据。
负责这一操作的是网卡,要控制网卡还需要网卡驱动程序,网卡驱动会在获取网络包之后将其复制到网卡内的缓存区中,接着在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列。最后网卡将包转为电信号发送出去。

送别者 — 交换机

交换机是工作在 MAC 层的,也就是二层网络设备,是集线器的一种” 升级版”。交换机的作用就是把数据包原样转发到目的地

交换机接收包流程

  • 电信号到达网线接口,交换机接收之后,将电信号转换为数字信号。
  • 通过包末尾的 FCS 校验错误,如果没有问题将网络包放到缓冲区。
  • 查询 MAC 地址表,查找接收方 MAC 地址是否在 MAC 地址表中有记录,如果有按照 MAC 地址表中指定的端口进行转发电信号即可,如果没有交换机会把数据包转发到除源端口外的所有端口上,无论该设备连接在哪个端口都可以收到数据包。这里思考一下,发送这么多冗余的包是否会造成网络拥塞呢?
  • 如果接收方 MAC 地址是一个广播地址,那么交换机会将包发送到除源端口之外的所有端口,广播地址 (MAC:FF:FF:FF:FF:FF:FF,IP:255.255.255.255)。如果目的 ip 不在局域网内那么目的 MAC 填写就是默认网关也就是路由器。

上面的思考题,其实完全不必担心,因为以太网的设计本来就是将数据包发送到整个网络的,接收到网络包的计算机通过核对 MAC 地址选择丢弃,只有相应的接收者才会接收这个包。并且当目标设备接收了包之后会响应,因此交换机就可以将它的地址写入 MAC 地址表,下次也就不需要将数据包转发到所有端口了。

出境大门 — 路由器

  • 路由器是基于 IP 设计的,俗称三层网络设备,路由器的各个端口都有 MAC 地址和 IP 地址。它和计算机的网卡是一样的。
  • 交换机是基于以太网设计的,俗称二层交换机,交换机的端口不具有 MAC 地址。

接收包时,电信号到达网线接口部分,路由器中的模块会将电信号转为数字信号,然后通过包末尾的 FCS 进行错误校验,如果没问题则检查 MAC 头部中的接收方 MAC 地址,查看是不是发送给自己的包,如果是就放到接收缓冲区,不是就丢弃这个包。

当转发包时,路由器端口会接收发给自己的以太网包,然后路由表插查询转发目标,由相应的端口作为发送方将以太网包发送出去。

查询路由表确定输出端口
完成包接收操作之后,路由器会去掉包开头的 MAC 头部,MAC 头部的作用是将包转发到路由器现在作用已经达到了,接着路由器会根据目的 IP 查询路由表判断要转发的下一个局域网路由器。

  • 注意,操作和上面的多网卡计算机确定源 ip 地址是一样的,那目的 ip 地址和子网掩码进行按位与操作,如果和条目中的目的 ip 匹配则查看网关地址就是下一跳的 ip 地址,然后通过 ARP 协议通过 ip 地址获取 MAC 地址。
    看这个路由表是不是有些奇怪,前两行的网关地址为空,这是因为如果结果落在前两行那么表示已经无需进行转发,此数据包的目标主机就在当前局域网内。
  • 如果网关地址不为空,则需要进行转发,但是我们需要的是 MAC 地址,此时只有网关的 ip 地址,并且这两个是独立的局域网如何获取 MAC 地址呢?虽然 ARP 是局域网协议,但是只要运营商通过技术手段将两个站点合并到同一个逻辑子网中,此时为以太网型广域网,此时 ARP 依然可以在” 虚拟局域网” 内生效。

服务器与客户端

数据包抵达服务器后,服务器会先扒开数据包的 MAC 头部,查看是否和服务器自己的 MAC 地址符合,
符合就将包收起来。
接着继续扒开数据包的 IP 头,发现 IP 地址符合,根据 IP 头中协议项,知道自己上层是 TCP 协议。
于是,扒开 TCP 的头,里面有序列号,需要看一看这个序列包是不是我想要的,如果是就放入缓存中然后
返回一个 ACK, 如果不是就丢弃。TCP 头部里面还有端口号,HTTP 的服务器正在监听这个端口号。
于是,服务器自然就知道是 HTTP 进程想要这个包,于是就将包发给 HTTP 进程。
服务器的 HTTP 进程看到,原来这个请求是要访问一个页面,于是就把这个网页封装在 HTTP 响应报文
里。
HTTP 响应报文也需要穿上 TCP、IP、MAC 头部,不过这次是源地址是服务器 IP 地址,目的地址是客户端
IP 地址。
穿好头部衣服后,从网卡出去,交由交换机转发到出城的路由器,路由器就把响应数据包发到了下一个路
由器,就这样跳啊跳。
最后跳到了客户端的城门把守的路由器,路由器扒开 IP 头部发现是要找城内的人,于是又把包发给了城
内的交换机,再由交换机转发到客户端。
客户端收到了服务器的响应数据包后,同样也非常的高兴,客户能拆快递了!
于是,客户端开始扒皮,把收到的数据包的皮扒剩 HTTP 响应报文后,交给浏览器去渲染页面,一份特别
的数据包快递,就这样显示出来了!
最后,客户端要离开了,向服务器发起了 TCP 四次挥手,至此双方的连接就断开了。

参考资料

[1]2.2 键入网址到网页显示,期间发生了什么? | 小林 coding (xiaolincoding.com)
[2] 户根勤。网络是怎么连接的。人民邮电出版社

如果觉得本文对您有所帮助,可以支持下博主,一分也是缘分😊
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇