从输入URL到网页显示内容,期间经历了什么?
这道题算是大厂热门面试题了,接下来我将结合所学的内容阐述其中详细的过程。
首先来看看URL的结构是什么样的,以这条URL为例:http://www.abc.com/D/E.html 其中http是协议名,abc.com表示域名,**/D/E.html**则是数据源的路径,其中E.html就是文件名
接着,我们在浏览器地址栏内输入这条域名,于是:
生成HTTP请求
浏览器开始解析URL,从而能够确定Web服务器以及文件名,接着开始根据解析到的数据生成HTTP请求消息
以下为层次结构下的HTTP结构:
- 请求行
- 方法(这条请求使用的是什么请求方法呢?get?post?put?…)
- url(就是地址栏中的URL)
- 版本(HTTP版本,如HTTP/1.1、HTTP/2、HTTP/3)
- 消息头
- 首部字段名(如Cache-Control、Connection、User-Agent)
- 字段值
- 消息体
此时,网络包的结构为:HTTP报文
注意:Get请求的消息体为空!只有Post请求才能有消息体
查询域名对应的IP地址
浏览器生成HTTP消息后,需要获取对应域名的IP地址,有了IP地址,操作系统才能知道要将这条信息发送到什么地方。
大家学过计网就知道,根据域名查询IP地址需要用到DNS协议。
前置知识:域名是有层级关系的,对于www.abc.com,它的实际域名其实是www.abc.com. (请注意!实际的域名最后多了个点儿)
越靠右的域名层级越高,从右往左分解www.abc.com. 可以得到:
- .(根DNS服务器)
- .com(顶级域DNS服务器)
- abc.com(权威DNS服务器)
根域的DNS服务器信息保存在互联网中所有DNS服务器内,这样任意DNS服务器都能直接访问根DNS服务器
接下来简述DNS解析流程:
- 客户端发出DNS请求,询问本地DNS服务器www.abc.com的IP是什么?
- 本地DNS服务器收到请求后在缓存表内寻找www.abc.com对应的IP,如果能找到就直接返回IP,否则就会直接询问**根DNS服务器**
- 根DNS服务器收到请求后,发现该域名后缀对应的是顶级DNS服务器.com,就会将该DNS服务器的地址发送给本地DNS服务器
- 本地DNS服务器收到响应后到顶级DNS服务器寻找www.abc.com的IP,**顶级域名服务器**发现该域名后缀为**abc.com**,于是将该服务器的地址发送给本地服务器
- 本地服务器收到响应后,去权威DNS服务器询问www.abc.com的IP,因为这个域名是当前这个权威服务器abc.com负责管理的,所以找到对应IP后会返回给本地服务器
- 本地服务器获取到www.abc.com对应的IP地址后,就能和目标建立连接
由此可见,DNS解析的过程中,不同层级的DNS服务器只会返回域名的下一级DNS服务器的地址,本地DNS服务器根据返回信息一级一级地查找目标
难道每次为了获取域名的IP地址都要经过这些查询步骤吗?其实并不是,因为还有缓存。大致询问流程为:浏览器缓存 -> 操作系统缓存 -> hosts文件 -> 本地DNS服务器
TCP传输
HTTP是基于TCP协议传输的,首先大家要知道TCP是基于字节流的传输协议,这就意味着发送的信息如果不规定消息边界就会导致一堆消息混在一起,完全分不清每个消息的开头和结尾,因此HTTP协议会通过Content-Length或是Transfer-Encoding:chunked声明消息边界,防止出现粘包现象
前置知识:
- MTU:一个网络包的最大长度,以太网中一般为1500字节
- MSS:除了IP头和TCP头,一个网络包能容纳的最大TCP数据长度
如果HTTP请求消息超过MSS长度,就需要将数据按照MSS的长度拆分,拆出来的每个数据块都会作为单独的网络包加上TCP头
到了这一步,网络包的结构就是:TCP头 + 数据块(注意哦,这里我将HTTP报文换成了数据块)
IP定位
到了这一步,就需要将上一步封装的网络包发送给目标设备
IP协议会在上一步的网络包基础上再加一层IP头,内部主要包含(省略了大部分属性):
首部校验和
源IP地址
目标IP地址(之前使用DNS协议查到的目标服务器IP地址)
现在,网络包的结构就是:IP头 + TCP头 + 数据块
不过IP地址只负责确定目标设备所处的网段,此外还需要知道MAC地址才能在该网段内找到目标设备。
封装MAC头部
MAC头部包含发送方MAC地址和接收方MAC地址
发送方MAC地址是本机网卡属性的一部分,直接读出来就是了,但是接收方MAC地址该怎么获取呢?
- 先查询ARP缓存,如果已经有对方的MAC地址,就能直接使用
- 如果缓存中没有相应数据就需要发送ARP广播获取对方的MAC地址
注意:MAC地址每跳一换,而IP地址保持不变,因为IP负责跨网转发,而MAC地址只负责本网段内的数据投递。
举个寄快递的例子,假设我需要从苏州发一个快递到广州,那么源IP就是寄件人地址,目标IP就是收件人,这两个IP始终不变。发送过程如下:
第一跳:源MAC地址就是我的地址,目标MAC地址就是苏州包裹转运站
第二跳:源MAC地址就是苏州包裹转运站,目标MAC地址就是广州包裹转运站
第三跳:源MAC地址就是广州包裹转运站,目标MAC地址就是快递的终点地址
至此,网络包的结构就变为:MAC头部 + IP头 + TCP头 + 数据块
发送
网络包终于组装完成了,接下来就需要发送出去啦。但是现在的网络包还只是一串二进制信息,需要依靠网卡将这串二进制信息转化为电信号才能通过网线传输,网卡又依赖于网卡驱动程序。网卡驱动程序获取网络包后,会将其复制到网卡缓冲区,然后在其头部加上报头和起始帧分界符,在尾部加上帧校验序列
最终,封装完成后的数据包就是:报头和起始帧分界符 + MAC头部 + IP头 + TCP头 + 数据块 + FCS
接着,网卡将数据包转为电信号通过网线发送到下一站
交换机
交换机收到电信号之后的流程如下:
- 将其转换为数字信号
- 通过尾部FCS校验错误
- 如果没有错误则将这个包放入缓冲区
- 在MAC地址表中查询接收方的MAC地址是否存在
- 如果存在则将信号发送到对应端口
- 如果目标MAC地址不存在于地址表中,交换机就会将这个包转发到除了源端口外的所有端口。又因为对于每个设备而言如果收到的数据包不属于自己,则会忽略这个包,因此这种广播发包的操作也没什么问题。
如果接收方MAC地址是广播地址,那么交换机也会将包发送到除了源端口外的所有端口
注意:二进制下全1即为广播地址,体现在IPv4即255.255.255.255,体现在MAC地址即FF:FF:FF:FF:FF:FF
路由器
现在数据包终于到达路由器,这里是数据包被发送到互联网中的最后一站
路由器接收到电信号之后的操作步骤:
- 将其转为数字信号
- 校验FCS
- 判断是否是发给自己的包
- 如果是则放入接受缓冲区,否则就丢弃
因为路由器的每个端口都拥有MAC地址,因此只接收与自身MAC地址匹配的包
接收数据包之后,路由器会去掉包结构中的MAC头部,然后根据IP头中的地址转发数据包
转发前,会重新封装MAC头,具体来说:
- 如果下一跳是本地子网内的设备就通过ARP查询下一跳的MAC地址
- 如果已经有缓存,就直接使用
然后就会重新加上一层新的MAC头,此时,源MAC就是本路由器出口网卡的MAC,而目标MAC就是下一跳设备的MAC
到达服务端
到达服务端之后就可以按照封装顺序倒过来进行解包
- 首先查看数据包的MAC头部内的MAC地址是否和自己的MAC地址符合,符合就进行下一步操作
- 继续查看数据包的IP头,如果IP地址也符合,就能够根据IP头中的协议项知道自己上层是TCP协议
- 然后,查看TCP头部数据,查看序列号是否是我需要的,如果是,就返回一个ACK响应,否则就丢弃
- 服务器此时就知道是HTTP进程想要这个包,于是就将包发给HTTP进程
- 服务器的HTTP进程发现这个请求想要访问一个页面,于是将这个网页封装到HTTP响应报文继续封装并发送到客户端
About this Post
This post is written by ByronGu, licensed under CC BY-NC 4.0.