计算机网络
键入网址到网页显示,期间发生了什么?
一.浏览器的第一步工作是解析URL
首先浏览器做的第一步工作就是要对URL进行解析,从而生成发送给Web服务器的请求信息。

所以图中的长长的 URL 实际上是请求服务器里的文件资源。
要是上图中的蓝色部分 URL 元素都省略了,那应该是请求哪个文件呢?
当没有路径名是,就代表访问目录下事先设置的默认文件,也就是/index.html或者/default.html这些文件,这样就不会发送混乱了。
二.生产HTTP请求信息
对URL进行解析之后,浏览器确定了Web服务器和文件名,接下来就是根据这些信息生成HTTP请求消息了。

一个孤单 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 服务器。
域名解析的工作流程
- 客户端首先会发出一个 DNS 请求,问 www.server.com 的 IP 是啥,并发给本地 DNS 服务器(也就是客户端的 TCP/IP 设置中填写的 DNS 服务器地址)。
- 本地域名服务器收到客户端的请求后,如果缓存里的表格能找到 www.server.com,则它直接返回 IP 地址。如果没有,本地 DNS 会去问它的根域名服务器:“老大, 能告诉我 www.server.com 的 IP 地址吗?” 根域名服务器是最高层次的,它不直接用于域名解析,但能指明一条道路。
- 根 DNS 收到来自本地 DNS 的请求后,发现后置是 .com,说:“www.server.com 这个域名归 .com 区域管理”,我给你 .com 顶级域名服务器地址给你,你去问问它吧。”
- 本地 DNS 收到顶级域名服务器的地址后,发起请求问“老二, 你能告诉我 www.server.com 的 IP 地址吗?”
- 顶级域名服务器说:“我给你负责 www.server.com 区域的权威 DNS 服务器的地址,你去问它应该能问到”。
- 本地 DNS 于是转向问权威 DNS 服务器:“老三,www.server.com对应的IP是啥呀?” server.com 的权威 DNS 服务器,它是域名解析结果的原出处。为啥叫权威呢?就是我的域名我做主。
- 权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS。
- 本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接。
至此,我们完成了 DNS 的解析过程。现在总结一下,整个过程我画成了一个图。

那是不是每次解析域名都要经过那么多的步骤呢?
当然不是了,还有缓存这个东西的嘛。
浏览器会先看自身有没有对这个域名的缓存,如果有,就直接返回,如果没有,就去问操作系统,操作系统也会去看自己的缓存,如果有,就直接返回,如果没有,再去 hosts 文件看,也没有,才会去问「本地 DNS 服务器」。
数据包表示:“DNS 老大哥厉害呀,找到了目的地了!我还是很迷茫呀,我要发出去,接下来我需要谁的帮助呢?”
指南好帮手——协议栈
通过DNS获取到IP后,就可以把HTTP请求的传输工作交给操作系统中的协议栈。
协议栈的内部分为几个部分,分别承担不同的工作。上下关系是有一定的规则,上面的部分会向下的部分委托工作,下面的部分收到委托的工作并执行。

应用程序(浏览器)通过调用Socket库,来委托协议栈工作。协议栈的上半部分有两块,分别是TCP和UDP协议,这两个传输协议会接受应用层的委托执行收发数据的操作。
协议栈的下面一半是用IP协议控制网络包收发操作,在互联网上传数据时,数据会被切分成一块块的网络包, 而将网络包发送给对方的操作就是由IP负责的。
此外IP中还包括ICMP协议和ARP协议。
- ICMP用于告知网络包传送过程中产生的错误以及各种控制信息。
- ARP用于根据IP地址查询相应的以太网MAC地址
IP下面的网卡驱动程序负责控制网卡硬件,而最下面的物理硬件网卡则负责完成实际的收发操作,也就是对网线中的信号执行发送和接收操作。
数据包看了这份指南表示:“原来我需要那么多大佬的协助啊,那我先去找TCP大佬!”
可靠传输——-TCP
HTTP是基于TCP协议传输的,所以在这我们先了解TCP协议。
TCP包头格式
先看看TCP报文头部格式

源端口号和目的端口号是必不可少的,如果没有这两个端口号,数据就不知道应该发给哪个应用。
接下来的序号,这个是为了解决乱码问题。
还有应该有的是确认序列,目的是确认发送出去对方是否有收到。如果没有收到就应该重新发送,直到送到,确认序列是为了解决丢包问题。
接下来还有一些状态位(第四行蓝色部分)。例如SYN是发起一个连接,ACK是回复,RST是重新连接,FIN是结束连接等。TCP是面向连接的,因而双方要维护连接的状态,这些状态位的包的发送,会引起双方的状态变化。
还有一个重要的是窗口大小。TCP要做流量控制,通信双方各声明一个窗口(缓存大小),标识自己当前能够的处理能力,别发送太快也别发送太慢。
除了做流量控制以外,TCP还会做拥塞控制,对于真正的通路堵车不堵车,它无能为力,唯一能做的就是控制自己,也即控制发送的速度。不能改变世界,就改变自己嘛。
TCP传输数据之前,要先三次握手建立连接
在 HTTP 传输数据之前,首先需要 TCP 建立连接,TCP 连接的建立,通常称为三次握手。
这个所谓的「连接」,只是双方计算机里维护一个状态机,在连接建立的过程中,双方的状态变化时序图就像这样。

- 一开始,客户端和服务端都处于closed状态。先是服务端主动监听某个端口,处于listen状态。’
- 然后客户端主动发起连接SYN,之后处于SYN-SENT状态。
- 服务端收到发起的连接,返回SYN,并且ACK客户端的SYN,之后处于SYN-REVD状态,
- 客户端收到服务端发送的SYN和ACK之后,发送对SYN确认的ACK,之后客户端处于ESTABLISHED状态,因为它一发一收成功了。
- 服务端收到自己ACK的ACK之后,处于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报文之后,就需要交给下面的网络层处理。
至此,网络包的报文如下图。

此时,遇上了 TCP 的 数据包激动表示:“太好了,碰到了可靠传输的 TCP 传输,它给我加上 TCP 头部,我不再孤单了,安全感十足啊!有大佬可以保护我的可靠送达!但我应该往哪走呢?”
远程定位—-IP
TCP模块在执行连接,收发,断开等各阶段操作时,都需要委托IP模块将数据封装成网络包发送给通信对象
IP包格式

在IP协议里面需要有源地址IP和目的地址IP:
- 源地址IP,即是客户端输出的IP地址;
- 目标地址,即通过DNS域名解析得到的Web服务器IP。
因为HTTP是经过TCP传输的,所以在IP包头的协议号要填写为06(十六进制),表示为TCP协议。
假设客户端有多个网卡,就会有多个IP地址,那IP头部的源地址应该选择哪个呢?
当存在多个网卡,在填写源地址IP时,就需要判断应该填写哪个地址。这个判断相当于在多块网卡中判断应该使用哪一个网卡来发送包。
这个时候就需要根据路由表规则,来判断哪一个网卡作为源地址IP。
在Linux操作系统中,我们可以使用route -n查看当前系统的路由表。

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

- 首先先和第一条目的子网掩码(
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报文生成
至此,网络包的报文如下图。

此时,加上了 IP 头部的数据包表示 :“有 IP 大佬给我指路了,感谢 IP 层给我加上了 IP 包头,让我有了远程定位的能力!不会害怕在浩瀚的互联网迷茫了!可是目的地好远啊,我下一站应该去哪呢?”
两点传输——-MAC
生成了IP头部之后,接下来网络包还需要在IP头部的前面加上MAC头部
MAC包头信息
MAC头部是以太网使用的头部,它包含了接收方和发送方的MAC地址等信息。

在 MAC 包头里需要发送方 MAC 地址和接收方目标 MAC 地址,用于两点之间的传输。
一般在 TCP/IP 通信里,MAC 包头的协议类型只使用:
0800: IP 协议0806: ARP 协议
ARP:地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。
MAC 发送方和接收方如何确认?
发送方的 MAC 地址获取就比较简单了,MAC 地址是在网卡生产时写入到 ROM 里的,只要将这个值读取出来写入到 MAC 头部就可以了。
接收方的 MAC 地址就有点复杂了,只要告诉以太网对方的 MAC 的地址,以太网就会帮我们把包发送过去,那么很显然这里应该填写对方的 MAC 地址。
TCP超时重传

