本文针对于《计算机网络》课程内容的总结和扩展,主要是讲解浏览器从键入URL网址后,到网页显示,其间的过程。
首先,浏览器的第一步工作就是对 URL 进行解析,然后给向Web 服务器发送请求。我们先看下一条URL代表什么:
举个例子:
https://www.jb51.net/article/223007.htm
其中 “www.jb51.net” 表示这台服务器,后面的 /article/223007.htm 表示我们请求的文件路径。但是这个路径是相对路径,是以web服务器根目录为起始目录的。如图:
所以一个 URL 实际上对应Web服务器里的一个文件资源。如果没有输入路径名,例如只输入 “https://www.jb51.net” ,表示访问事先设置的默认文件,常见的有 /index.html 、 /default.html 等等。
对 URL 进行解析后,浏览器确定了 Web 服务器和文件名,接下来就是根据这些信息来构造 HTTP 请求报文。HTTP消息格式如图:
浏览器解析 URL 并生成 HTTP 数据结构后,在发给服务器之前,还需要查询域名对应的 IP 地址,因为操作系统发送消息时,必须提供通信对象的 IP 地址。
DNS 服务器就是专门保存 域名与 IP 的对应关系,负责解析域名的服务器 。注意:很多教材都说IP地址不方便记忆,所以才有了域名。其实域名不仅仅是方便记忆,他还有更重要的作用,比如用域名就可以实现一台服务器同时搭建多个网站而共用一个IP。
域名是用句点来分隔的,比如 www.server.com,这里的句点代表了不同层级的划分。在域名中,越靠右的位置表示其层级越高。
可能因为域名是外国人定义的,所以思维和中国人相反。比如表示一个地点,外国喜欢按照从小到大的顺序说(如 XX 街道 XX 区 XX 市 XX 省),而中国喜欢按从大到小的顺序(如 XX 省 XX 市 XX 区 XX 街道)。
最右边的是顶级域名,例如上面的 “.com” 。再下面是二级域名 server.com。所以域名的层级关系是一个树状结构(域名和域名服务器是两个概念):
所有的 DNS 服务器都有根域DNS 服务器的信息。这样一来,任何 DNS 服务器都可以找到根域 DNS 。因此,客户端只要能够找到任意一台 DNS 服务器,就可以通过它找到根域 DNS 服务器,然后再一步步找到位于下层的某台目标 DNS 服务器。
域名解析过程:
经过上述步骤 获取到 IP 后, HTTP 的传输工作就交给系统中的协议栈了。协议栈的内部分为几个部分,分别承担不同的工作。上下关系是有一定的规则的,上面的部分会向下面的部分委托工作,下面的部分收到委托的工作并执行。如图:
应用程序(浏览器)通过调用 Socket 库,来委托协议栈工作。协议栈的上半部分有两块,分别是负责收发数据的 TCP 和 UDP 协议,它们会接受应用层的委托执行收发数据的操作。协议栈的下面一半是用 IP 协议控制网络包收发操作,在互联网上传数据时,数据被划分为一个个的网络包,而将网络包发送给对方的操作就是由 IP 层负责的。
此外 IP 中还包括 ICMP 协议和 ARP 协议。ICMP 用于告知网络包传送过程中产生的错误以及各种控制信息。ARP 用于根据 IP 地址查询相应设备的 MAC 地址。IP 下面的网卡驱动程序负责控制网卡硬件,而最下面的网卡则负责完成实际的收发操作,也就是发送和接收比特流。
HTTP 是基于 TCP 协议传输的,所以先讲下 TCP 协议。我们先看 TCP 报文头部的格式:
其中,源端口号和目标端口号是不可少的,这个端口号是区分应用程序的,如果没有,系统就不知道要传给哪个应用。包的序号是为了解决包先后到达的乱序问题。
确认号,目的是确认发出去对方是否有收到。如果没有收到就重新发送,直到送达,是为了解决丢包的问题。还有一些状态位。例如 SYN 表示发起一个连接,ACK 表示回复确认,RST 是重新连接,FIN 是结束连接。TCP 是面向连接的,因而双方要维护连接的状态,这些状态位就是调整连接状态的。
窗口大小,因为TCP 要做流量控制,所以通信双方需要各声明一个窗口(缓存大小),这个窗口表示本机当前的数据处理能力,控制对方发包的快慢。除了流量控制,TCP还会做拥塞控制,对于真正的网络拥堵,它无能为力,它只能控制自己这个连接,控制发送的速度。
在 HTTP 传输数据之前,通信双方要建立TCP连接,建立过程俗称“三次握手”。这个所谓的“连接”,只是计算机里维护的一个状态值,并不是真的在网络上有一条连接。
三次握手目的是保证双方都有发送和接收的能力。 Linux 系统可以通过命令 netstat -napt 查看本机有哪些连接以及状态。
如果 HTTP 请求消息比较长,超过了 MSS 的长度,这时 TCP 就需要把 HTTP 的数据进行划分。MTU 与 MSS 区别如图:
MTU是一个网络包的最大长度,我国一般定为 1500 字节。MSS是除去 IP 和 TCP 头部后,一个网络包所能容纳的 TCP 数据的最大长度,那必然是小于1500字节的。数据会按照 MSS 的长度进行划分,划分出来的每一块数据都会被放进单独的网络包中。即每个被拆分的数据都要加上 TCP 头信息,然后再交给 IP 层处理。如图:
前面我们讲了,TCP 协议头部有两个端口,一个是浏览器监听的端口(通常是随机生成),一个是 Web 服务器监听的端口(HTTP 默认是 80, HTTPS 默认是 443)。在双方建立了连接后,TCP 报文中的数据部分就是存放 HTTP 头部 + 数据,组装好 TCP 报文之后,就交给下面的网络层处理。HTTP的数据经过TCP处理后,报文大致如下:
TCP 模块在执行连接、收发、断开等各阶段操作时,都需要依托 IP 模块将数据封装成网络包发送给通信对象。我们再看 IP 报文头部的格式:
IP 报头需要有源 IP 和 目标 IP,前者是客户端的 IP 地址,后者是 服务器 IP地址。因为 HTTP 是基于 TCP 传输的,所以在 IP 包头的协议部分是固定的,为 06(十六进制),表示协议是 TCP。如果客户端有多个网卡和多个IP,在填写源 IP 时,就需要系统判断应该使用哪一块网卡来发送包。这个时候就需要根据路由表规则,来判断哪一个网卡作为源 IP。在 Linux 操作系统,我们可以使用 route -n 命令查看当前系统的路由表。例如:
根据上面的路由表,我们假设 Web 服务器是 192.168.10.200。判断过程如图:
首先和第一条目的子网掩码进行 与运算,得到结果为 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头部后,接下来网络包还需要在 IP 头部的前面加上 MAC 头部。MAC 头部是以太网使用的头部,它包含了接收和发送方的网卡 MAC 地址等信息。mac头部很简单,只有3个字段:48位的目标MAC地址、48位的源MAC地址和16位的协议类型。
MAC 包头里的源 MAC 地址和目标 MAC 地址,用于两设备之间的传输。在 TCP/IP 协议栈里,MAC 包头的协议类型一般只使用:0800 ,IP 协议 和 0806 ,ARP 协议。MAC 地址是网卡生产时烧录到 ROM 里的,只要将这个值读取出来写到 MAC 头部源MAC就可以了。下一跳的IP地址可以根据路由表获得,在表中找到相匹配的条目就可以了。MAC地址也是根据查表,叫ARP缓存表。此时需要 ARP 协议解析下一跳IP的 MAC 地址。
ARP 协议会以广播的形式,对局域网内所有的设备询问IP对应的mac地址。如果对方和自己处于同一个子网中,那么通过上面的操作就可以得到对方的 MAC 地址。然后,我们将这个 MAC 地址写入 MAC 头部的目标mac。操作系统会把本次查询结果放到 ARP 缓存表备用。也就是说,在发包时:先查询 ARP 缓存,如果有对应的记录,就不需要发送 ARP广播 查询。当 ARP 缓存中不存在相关记录时,才发送 ARP 广播查询。在 Linux 系统中,可以使用 arp -a 命令来查看 ARP 缓存内容。
IP 生成的网络包是存放在内存中的一串二进制数字信息,没有办法直接发送给对方。系统需要将数字信息转换为电信号,才能在网线上传输。负责执行这一操作的是网卡,要控制网卡还需要靠网卡驱动程序。网卡驱动从 IP 模块获取到包之后,会将其复制到网卡内的缓存区中,然后在开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列。如图:
起始帧分界符表示包起始位置的标记;末尾的 FCS(帧校验序列)用来检查包传输过程是否有损坏。最后网卡会将二进制数据转为电信号,通过网线发送出去。
比特流顺着网线发出去以后,需要经过很多中间设备才能到达服务器。首先就要经过交换机。交换机主要是负责转发,将网络包原样转发到目的地。交换机工作在 MAC 层,也称为二层网络设备,当然也有三层交换机。
首先,电信号到达交换机的网线接口,交换机里的模块进行接收并将电信号转换为数字信号。然后通过包末尾的 FCS 校验错误,如果没问题则放到缓冲区。这部分操作基本和计算机的网卡相同,但交换机的工作方式和网卡不同。因为网卡本身有 MAC 地址,会核对收到的包目标 MAC 地址是不是发给自己的,如果不是则丢弃;而交换机的端口不核对接收方 MAC 地址,直接接收所有的包并存放到缓冲区中。这是和网卡不同的一点,交换机的端口没有 MAC 地址。
交换机将包存入缓冲区后,就会查询这个包的接收方 MAC 地址是否在 MAC 地址表中。交换机的 MAC 地址表主要包含两个信息:一个是设备的 MAC 地址;另一个是该设备连在交换机的哪个端口。
如上图,交换机内维护着一张mac地址表,即mac地址与网线端口的映射表。收到包时,就会把mac地址和对应的端口写入表中,交换机根据mac地址表进行转发。
如果地址表没有接收方 MAC 地址的记录,可能是因为该设备还没有向交换机发过任何的包,或一段时间没有工作(比如关机)导致地址被从地址表中删除。这种情况下,交换机会将包转发到除了源端口之外的所有端口上,无论该设备连接在哪个端口上都能收到这个包。
那么这样做会发送大量的包,会不会造成网络拥塞呢?其实不用担心,因为发送了包之后目标设备会作出响应,有了响应包,交换机就可以将它的地址写入 MAC 地址表,下次就不需要把包发到所有端口了。而且以太网的设计本来就是将包发送到整个网络的,然后只有相应的接收者才会接收包,其他设备则会忽略这个包。局域网中每秒可传输上千个包,多出一两个包并无大碍。此外,如果接收方 MAC 地址是一个广播地址,那么交换机还是会将包发送到除源端口之外的所有端口。以下属于广播地址:
MAC 地址中的 FF:FF:FF:FF:FF:FF
IP 地址中的 255.255.255.255
数据包经过交换机后,紧接着到达路由器,并在此被转发到下一个中间设备。这一步转发的工作原理和交换机类似,也是通过查表判断包转发的方向。不过具体的操作流程,和交换机还是有区别的。因为路由器是基于 网络层 设计的,俗称三层网络设备,其各个端口都具有 MAC 地址和 IP 地址;而交换机是基于网络接口层设计的,俗称二层网络设备,交换机的端口不具有 MAC 地址。
路由器的端口具有 MAC 地址,同时还具有 IP 地址,从这个意义上来说,它和计算机的网卡是一样的。当转发包时,路由器端口会接收发给自己的数据包,然后路由表查询转发目标,再由相应的端口作为发送方将数据包发送出去。
首先,电信号到达网线接口部分,路由器中的模块会将电信号转成数字信号,然后通过包末尾的 FCS 进行错误校验。如果没问题则检查 MAC 头部中的接收方 MAC 地址,看看是不是发给自己的包,如果是就放到接收缓冲区中,否则就丢弃这个包。
完成数据包接收操作后,路由器会去掉 MAC 头部,MAC 头部的作用就是将包送达路由器,其中的接收方 MAC 地址就是路由器端口的 MAC 地址。因此,当包到达路由器之后,MAC 头部的任务就完成了,于是 MAC 头部就会被丢弃。接下来,路由器会根据 IP 头部中的内容进行包的转发。转发分为几个阶段,首先查询路由表判断转发目标。
上图是一台华为路由器设备的路由表。判断转发目标的第一步,就是根据包的接收方 IP 地址查询路由表中的目标地址栏,以找到相匹配的记录。路由匹配和前面讲的一样,每个条目的子网掩码和 目标 IP 做 与运算,得到的结果与对应条目的目标地址进行匹配,如果匹配就会作为候选转发目标,如果不匹配就继续。如果找不到匹配路由,就会选择默认路由,路由表中子网掩码为 0.0.0.0 的记录表示“默认路由”。
匹配路由后,就会进入包的发送操作。路由器根据路由表的下一跳判断对方的地址。知道对方的 IP 地址之后,接下来需要通过 ARP 协议根据 IP 地址查询 MAC 地址,并将查询的结果作为接收方 MAC 地址。路由器也有 ARP 缓存,因此首先会在 ARP 缓存中查询,如果找不到则发送 ARP 查询请求。接下来是发送方 MAC 地址字段,这里填写输出端口的 MAC 地址。还有一个以太类型字段,填写 0080 (十六进制)表示 IP 协议。
网络包完成后,接下来会将其转换成电信号并通过端口发送出去。发送出去的网络包会通过交换机到达下一个路由器。接下来,下一个路由器会将包转发给再下一个路由器,经过层层转发之后,网络包就到达了最终的目的地。在网络包传输的过程中,源 IP 和目标 IP 始终不会变,变化的是 MAC 地址,因为需要在不同的中间设备中传输。
数据包最终抵达了服务器,服务器会进行解包。数据包抵达服务器后,服务器会先检查数据包的 MAC 头部,查看是否和服务器自己的 MAC 地址符合,符合就接收包。接着继续查看数据包的 IP 头,发现 IP 地址符合,根据 IP 头中协议项,知道自己上层是 TCP 协议。接着查看 TCP 的头,里面有序列号,检查该序列包是不是需要的,如果是就放入缓存然后返回一个 ACK,否则丢弃。TCP头部里面还有端口号, Web服务正在监听这个端口号。于是,系统就知道了是 HTTP 进程的包,便把包发给 HTTP 进程。
服务器的 HTTP 进程检查http头部,发现该请求是要访问一个页面,于是就把这个网页封装在 HTTP 响应报文里。HTTP 响应报文也需要经过 TCP、IP、MAC 头部的层层封装,不过这次源IP和目标IP调换了位置。封装好后从网卡出去,经过交换机转发到路由器,路由器就把响应数据包发到了下一个路由器,到了最后一跳路由器,路由器检查IP 头部发现是本局域网的设备,于是把包发给了内网交换机,再由交换机转发到客户端。
客户端收到了服务器的响应数据包后,也开始逆向解析,一直解析到 HTTP 响应报文后,然后交给浏览器去渲染页面,网页就这样显示出来了。最后,浏览器关闭了,向服务器发起了 TCP 四次挥手,至此双方的连接就断开了。
OK,这就是从输入url到网页显示的全过程了,希望对正在学习《计算机网络》这门课程的同学有所帮助。
在《计算机网络·自顶向下》这部书中有更多相关内容,这本书也是我老师推荐我读的,确实不错,对于想深入了解计算机网络的值得一读。这本书目前最新的是第7版。为了方便大家学习,我已经将其打包挂在公众号上了,关注:极客随想。回复消息:计算机网络。即可获取。