完整的一次 HTTP 请求响应过程

因特网无疑是人类有史以来最伟大的设计,它互联了全球数亿台计算机、通讯设备,即便位于地球两端的用户也可在顷刻间完成通讯。

可以说『协议』是支撑这么一个庞大而复杂的系统有条不紊运作的核心,而所谓『协议』就是通讯双方所必须遵守的规则,在这种规则下,不同的数据报可能被解析为不同的响应动作。

简而言之,『协议』就是指如果发送和接收方按照这个规则进行数据报文的发送,即可在基本的数据传输之上得到某些特殊的功能或服务,否则你的数据别人是不认识的。例如:遵循 TCP 协议的两端,可以在不可靠的网络传输中得到可靠的数据传输能力。

整个计算机网络是分层的,有七层模型,也有五层模型,个人觉得五层模型更利于理解。我们从上至下的介绍这五个层,它们分别是,应用层,运输层,网络层,数据链路层和物理层

应用层

『应用层』算是距离用户最近的一层了,主机上的一个个的进程就构成了『应用层』。比如你在你的浏览器地址栏输入了 「www.baidu.com」,你的浏览器在应用层会做哪些事情呢?

首先浏览器会使用 DNS 协议返回域名「www.baidu.com」所对应的 IP 地址,关于 DNS 我们待会详细介绍。

接着,应用层决定创建一个『TCP 套接字』,然后将这个请求动作封装成一个 Http 数据报并推入套接字中。

套接字分为两种类型,『TCP 套接字』和『UDP 套接字』,应用层同时可能会有几十个数据报的发出,而运输层也会收到所有的响应报文,那么它该如何区分这些报文到底是谁的响应报文呢?

而套接字就是用于区分各个应用层应用的,往往由端口号和 IP 地址进行标识,运输层只要查看响应报文的源端口号和 IP 地址就能够知道该将报文推送给哪个套接字了。

当一个应用层数据报被推动进套接字之后,应用层的所有工作也算是全部完成了,关于后续报文的去向,它已经不用管了。

这里还要说明一点的是,『TCP 套接字』和『UDP 套接字』两者本质上的区别在于,前者保证数据报可靠地到达目的地,但是必然耗时,而后者不保证数据报一定能到达目的地,但是速度快,这也是应用层协议在选择运输层协议的时候需要考虑的一点。

关于 TCP 和 UDP,我们后续还会继续说,下面我们看看域名解析协议 DNS 是如何运作的,它是如何将一个域名解析返回它的 IP 地址的。

DNS 原理

首先明确一点的是,DNS 是一个应用层协议,并且它选择的运输层协议是 UDP,所以你的域名解析过程一般会很快,但也会经常出现解析失败的情况,然而刷新一下又好了。

image

 

在 DNS 服务器上,域名和它所对应的 IP 地址存储为一条记录,而所有的记录都不可能只存储在一台服务器上,我相信无论多么强大的服务器都扛不住全球上亿次的并发量吧。

大致来说,有三种类型的 DNS 服务器,根 DNS 服务器,顶级域 DNS 服务器和权威 DNS 服务器。

其中,顶级域 DNS 服务器主要负责诸如 com、org、net、edu、gov 等顶级域名。

根 DNS 服务器存储了所有顶级域 DNS 服务器的 IP 地址,也就是说你可以通过根服务器找到顶级域服务器。例如:「www.baidu.com」,根服务器会返回所有维护 com 这个顶级域服务器的 IP 地址。

然后你任意选择其中一个顶级域服务器,请求该顶级域服务器,该顶级域服务器拿到域名后应当能够做出判断并给出负责当前域的权威服务器地址,以百度为例的话,顶级域服务器将返回所有负责 baidu 这个域的权威服务器地址。

于是你可以任意选择其中一个权威服务器地址,向它继续查询 「www.baidu.com」 的具体 IP 地址,最终权威服务器会返回给你具体的 IP 地址。

至此,我们简单描述了一个域名解析的大致过程,还有一些细节之处并未提及,我们等会会通过一个实例来完整的看一下,下面描述一个非常重要的概念。

整个 DNS 解析过程中,有一个非常核心的人物我们一直没介绍它,它就像主机的『助理』一样,帮助主机查询域名的 IP 地址。它叫做『本地 DNS 服务器』。

image

 

大家每次通过 DHCP 动态获取 IP 地址的时候,这一点后文会说。其实路由器不仅给你返回了 IP 地址,还会告诉你一个 DNS 服务器地址,这个就是你的本地 DNS 服务器地址,也就是说,你的所有域名解析请求只要告诉它就行了,它会帮你查并返回结果给你的。

除此之外,本地 DNS 服务器往往是具有缓存功能的,通常两天内的记录都会被缓存,所以大部分时候你是感觉不到域名解析过程的,因为往往就是从缓存里拿的,非常快。

下面我们看一个简单的案例:

网上找的一个图,自己画实在太费时间了,但足以说明问题,现在假设请求 「www.xx.com」 。

image

 

  • ①:主机向负责自己的本地 DNS 发送查询报文,如果本地服务器缓存中有,将直接返回结果
  • ②:本地服务器发现缓存中没有,于是从内置在内部的根服务器列表中选一个发送查询报文
  • ③:根服务器解析一下后缀名,告诉本地服务器负责 .com 的所有顶级服务器列表
  • ④:本地服务器选择一个顶级域服务器继续查询,.com 域服务器拿到域名后继续解析,返回负责 .xx 域的所有权威服务器列表
  • ⑥:本地服务器从返回的权威服务器之一再次发送查询报文,最终会从某一个权威服务器上得到具体的 IP 地址
  • ⑧:向主机返回结果

其实整个 DNS 报文的发送与响应过程都是要走我们的五层协议的,只是这里重点在于理解 DNS 协议本身,所以并未提及其他层的具体细节,这里的强调是提醒你 DNS 只是一个应用层协议。

运输层

运输层的任务就是将应用层推出套接字的所有数据报收集起来,并且按照应用层指定的运输层协议,TCP 或 UDP,重新封装应用层数据报,并推给网络层等待发送。

TCP 和 UDP 是运输层的两个协议,前者是基于连接的可靠传输协议,后者是无连接的不可靠传输协议,所以前者更适合于一些对数据完整性要求高的场合,后者则适合于那种可以允许数据丢失但对传输速率要求特别高的场景,例如:语音电话,视频等,丢一两个包最多卡顿一下,无伤大雅。

UDP

UDP 不同于 TCP 那样复杂,它既不保证数据可靠的传输到目的地,也不保证数据按序到达目的地,仅仅提供了简单的差错检验。报文格式如下:

image

 

其中,数据就是应用层推出来的数据,源端口号用于响应报文的交付,目的端口号用于向目的进程交付数据,校验和用于检查传输过程中数据是否受损,如果受损,UDP 将直接丢弃该报文。

TCP

TCP 要稍微复杂些,它是面向连接的,并且基于连接提供了可靠的数据传输服务,它的数据报文格式如下:

image

 

单纯的解释报文格式中各个字段的含义并没有太过实际的意义,你也很难理解了,在我们介绍 TCP 是如何『三次握手』,『四次挥手』以及『丢包重传』等动作时,不间断的会说明这些动作时如何使用报文中的相关字段的。

首先我们来看耳熟能详的『三次握手』,这基本上是 TCP 的代名词了,无论懂不懂具体原理的人,提到 TCP,基本上都是知道『三次握手』的。

而本身,TCP 的三次握手就是为了确保通讯双方能够稳定的建立连接并完成数据报文的请求与响应动作,至于为什么是三次握手而不是四次五次,这是一个哲学问题,这里就不做讨论了。

第一步:

客户端向服务端发送一份特殊的 TCP 报文,该报文并不包含应用层的数据,是一份特殊的报文,它的 TCP 首部中 SYN 字段值为 1 (参见上述报文格式)。

除此之外,客户端还会随机生成一个初始序号,填在报文的「序号」字段,代表当前报文的序号是这个,并且我后续的分组会基于这个序号递增。

然后该报文将会经网络层、链路层、物理层发送到服务端。

第二步:

如果分组丢失了,那么客户端会经过某个时间间隔再次尝试发送。

而如果分组准确的到达服务端了,服务端拆开 TCP 首部会看到,这是一个特殊的 SYN 握手报文,于是为此次连接分配缓存等资源。

接着服务端开始构建响应报文,SYN 是一个用于同步需要的字段,响应报文中依然会被置为 1,并且服务端也将随机生成一个初始序号放置的响应报文的序号字段中。

最后,服务端还会为响应报文中的确认字段赋值,这个值就是客户端发过来的那个序号值加一。

整体上的意思就是说,「我同意你的连接请求,我的初始序号为 xxx,你的初始序号我收到了,我等着你的下一个分组到来」

第三步:

客户端收到服务端的响应报文,于是分配客户端 TCP 连接所必须的缓存等资源,于是连接已经建立。

实际上从第三步开始,客户端就可以携带应用层数据向服务端交换报文了,以后的每份报文中,SYN 都为 0,因为它只是用于同步初始序号的,这一点需要明确。

总的来说,整个『握手』过程大致如下图所示:

image

 

下面我们看看拆除一条 TCP 连接的『四次挥手』是怎样的过程。

因为一条 TCP 连接会消耗大量的主机资源,不仅仅服务端需要分配各种缓存资源,客户端也同样需要分配相应资源。因为 TCP 是『全双工通信』,服务端和客户端两方其实是一样的,谁是客户谁是服务器是相对的。

强调这一点是为了说明,一条 TCP 连接不是只有客户端才能断开,服务端也同样可以主动断开连接,这一点需要清楚。

我们这里假设客户端主动发起断开连接的请求为例:

第一步:

客户端构建一份特殊的 TCP 报文,该报文首部字段 FIN 被置为 1,然后发送该报文。

第二步:

服务端收到该特殊的 FIN 报文,于是响应客户端一个 ACK 报文,告诉客户端,请求关闭的报文已经收到,我正在处理。

第三步:

服务端发送一个 FIN 报文,告诉客户端,我将要关闭连接了。

第四步:

客户端返回一个 ACK 响应报文,告诉服务端,我收到你刚才发的报文了,我已经确认,你可以关闭连接了。

当服务端收到客户端发送的 ACK 响应报文时,将释放服务端用于该 TCP 连接的所有资源,与此同时,客户端也会定时等待一定时间后完全释放自己用于该连接的相关资源。

用一张图更直观的描述一下:

image

 

结合着图与相关序号信息,我们再详细说说其中的一些细节。

首先,客户端发送一个特殊分组,该分组的序号为 u。发送完成之后,客户端进入 FIN-WAIT-1 这个状态,这个状态下,该 TCP 连接的客户端不再能发送数据报,但是是可以接受数据报的,它等待着服务端的响应报文。

接着,服务端收到客户端发送的终止连接报文请求,服务端构建响应报文,告诉客户端「序号 u+1 以前的分组我都收到了」,并且进入 CLOSE-WAIT 状态,这个状态持续时间很短。

服务端会紧接着发送它的 FIN 数据报,通知客户端我服务端即将关闭连接,并随即进入 LAST_ACK 状态等待客户端响应报文。

一旦客户端收到这个 FIN 报文,将返回确认报文并进入 TIME-WAIT 状态,等待 2MSL 时间间隔后完全释放客户端 TCP 连接所占用资源。

与此同时,当服务端收到客户端最后的确认报文,就将直接断开服务端连接并释放相关资源。

至于为什么最后客户端需要等 2MSL 时间长度再完全释放 TCP 相关资源呢?

那是因为 2MSL 是一份报文存在于网络中最长的时间,超过该时间到达的报文都将被丢弃,而如果客户端最后的确认报文于网络中丢失的话,服务端必将发起超时请求,重新发送第三次挥手动作,此时等待中的客户端就可随即重新发送一份确认请求。

这是为什么客户端等待一个最长报文传输时间的原因。有人可能好奇为什么前面的各次请求都没有做超时等待而只最后一次数据发送做了超时等待?

其实原因很简单,相信你也能想到,就是 TCP 自带计时能力,超过一定时间没有收到某个报文的确认报文,会自动重新发送,而这里如果不做等待而直接关闭连接,那么我如何知道服务端到底收到没我的确认报文呢。

通过等待一个最长周期,如果这个周期内没有收到服务端的报文请求,那么我们的确认报文必然是到达了服务端了的,否则重复发送一次即可。

至此,TCP 的『三次握手』和『四次挥手』我们已经简单描述完成了,下面我们看看 TCP 的一些其他特性,比如:可靠传输,拥塞控制等

首先我们来看 TCP 是如何实现可靠传输的,即如何解决网络传输中丢包的问题。

TCP 使用『回退 N 步』协议实现的可靠传输,准确来说,TCP 是在它的基础上进行了一部分优化。

image

 

『回退 N 步』协议也被称作『滑动窗口』协议,即最多允许发送方有 N 个「已发送但未被确认」的数据报文,如图所示,p1 到 p3 长度即为 N,这里的窗口指的就是 p1 到 p3 这个区间。

只有当发送端收到 p1 的确认报文后,整个窗口才能向前滑动,而实际上在没有收到 p1 的确认报文前,即便它后面的报文已经被接收,服务端也仅仅会缓存这些『非预期的报文』

直到服务端收到最小预期的那个报文后,从缓存中取出已经到达的后续报文,合并并向上交付,然后向发送端返回一个确认报文。

当发送端窗口从左往右已经连续多个报文被确认后,整个窗口将向前滑动多个单位长度。

下面我们看一个例子:

image

 

这是一个发送方的窗口,灰色表示已经被确认的报文,黄色表示已发送但未被确认的报文,绿色表示下一个待发送的报文,白色表示不可用的报文。

这是我们假设服务端已经收到 6、7 两份报文,但是它上一次向上交付给应用层的是 4 号报文,也就是说它在等 5 号报文,所以它暂时会将 6、7 两个报文缓存起来,等到 5 号报文来了一并交付给应用层。

现在 5 号报文由于超时被重传了,终于到达目的地了,如愿以偿,服务端向上交付 5、6、7 三份报文,并返回一份确认报文,ACK = 8,表示序号 8 以前的所有报文都收到了

当发送端收到这份确认报文后,5、6、7 变成灰色,窗口向前移动三个单位长度。

此外,我还想强调一个细节,TCP 是没有否定确认的,所以如果服务端连续响应的多份报文是对同一序号的确认,那很有可能该序号以后的某个报文丢失。

例如:如果服务端发送多个对分组 5 的 ACK 确认,那说明什么?说明目前我服务端完整的向上交付的序号是 5 号,后续的报文我没收到,你最好重新发一下别等待超时了。

这也是『快速重传』的核心原理。

那么 TCP 的可靠传输我们也基本介绍完了,下面我们看看如果网络拥塞的时候,TCP 是如何控制发送流量的呢?

TCP 认为:丢包即拥塞,需要降低发送效率,而每一次收到确认数据报即认为网络通畅,会增加发送效率。

TCP 的拥塞控制算法包含三个部分,慢启动、拥塞避免和快速恢复

慢启动的思想是,刚开始缓慢的发送,比如某个时间段内只发送一次数据报,当收到确认报文后,下一次同样的时间间隔内,将发送两倍速率的两份数据报,并以此类推。

所以,短时间内,一个 TCP 连接的发送方将以指数级增长,但一旦出现丢包,即收到冗余的 ACK 确认,或者对于一个包的确认 ACK 始终没收到而不得不启动一次超时重传,那么发送方认为「网络是拥塞的」。

于是将速率直接调成一,即一个往返时间段,只发送一个分组,并且设置一个变量 ssthresh 表述一个阈值的概念,这个值是上次丢包时发送方发送速率的一半。

之后的发送方的发送效率一样会以指数级增长,但是不同于第一次,这次一旦达到这个阈值,TCP 将进入『拥塞避免』模式,该模式下的发送效率将不再指数级增长,会谨慎的增长。

拥塞避免的思想是,每个往返时间段发送的所有数据报全部得到确认后,下一次就增加一个分组的发送,这样缓慢的增长效率是谨慎的。

那么一旦出现发送端超时丢包,注意这里是超时,将发送速率置为一并重新进入慢启动状态,阈值就是当前发送效率的一半。

而如果是服务端返回多个冗余 ACK 以明确你丢包,TCP 认为这不是严重的,对于这种情况,TCP 减半当前发送效率并进入快速恢复阶段。

快速恢复的基本思想是,收到几个冗余的 ACK 就增加几个分组的发送效率,就是说,你服务端不是没收到我的几个报文吗,这两次发送我提升速率迅速发给你。

当这期间出现了由发送端超时导致的丢包,同样的处理方式,初始化发送速率为一并减半当前发送效率作为阈值,进入慢启动阶段。

当然,如果这期间收到了对丢失报文的确认,那么将适当降低发送效率并进入拥塞避免状态。

这样,整个 TCP 最核心的几个思想都已经介绍完了,整个运输层基本上也算明了了。关于运输层,你应当有了一定的理解,我再总结一下。

运输层的任务就是从应用层的各个进程的套接字那取回来所有需要发送的数据,然后选择 TCP 或者 UDP 将数据封装并推给下面的网络层待发送。

网络层

『网络层』其实解决的就是一个「转发」的问题,通过传说中的『IP 协议』划分了网络范围,即我没有直接用网线和你连在一起,我也能通过你的 IP 分析出该怎么样找到负责你的网关路由器,并通过你的网关路由给你传输数据报。

这就是『网络层』做的事情,它本质上解决了两台不存在于同一子网络下的主机相互通信的问题。而『IP 协议』以及「如何解析 IP 的算法」算是两个最核心的内容,我们首先看看这个『IP 协议』的相关概念。

以 IPv4 为例,使用 32 个比特位描述一个 IP 地址,所以理论上,整个 IPv4 可以提供 40 几个亿的 IP 地址,我们一般使用『点分十进制』来表示。

例如:11000001 00100000 11011000 00001001 的 IP 地址一般记为 193.32.216.9。

由此,我们解决了 IP 编址的问题,但是如何通过 IP 地址判断出它所属的子网络呢?

引入一个名词『子网掩码』,它在形式上和 IP 地址一样,使用 32 位比特位进行表述。其中,描述网络部分的比特位全为 1,子网络中的该主机编号部分全为 0 。

例如:子网掩码 11111111.11111111.11111111.00000000,写成十进制就是255.255.255.0。它明确了某个使用该子网掩码的 IP 的前 24 位是它的子网络部分,而后 8 位是该 IP 对应的主机在子网络下的一个编号。

举个例子:

IP 地址 172.16.254.1 所对应的子网掩码为 255.255.255.0,那么我们只需要做『AND』运算这两者即可得到该 IP 地址的网络部分。

所以,这个 IP 地址的网络号为 172.16.254 。

下面我们探讨一个十分重要的协议,它解决了一个刚加入子网络的主机如何获取属于它的 IP 地址的问题,这个协议叫,动态主机配置协议(DHCP)

DHCP

一般来说,我们有两种方式来配置主机的 IP 地址,一种是管理员手动的指定一个 IP 地址,当然,这样的成本是非常高的,你不能配置了一个已经被分配出去的 IP地址,即管理员需要记录所有已分配的 IP 地址。

另外一种呢,就是我们的 DHCP 协议,它允许新加入的主机自动获取一个 IP 地址以及相关的子网掩码和网关地址等。

默认情况下,路由器隔离广播包,不会将收到的广播包从一个子网发送到另一个子网。当 DHCP 服务器和客户端不在同一个子网时,充当客户端默认网关的路由器将广播包发送到DHCP服务器所在的子网,这一功能就称为 DHCP 中继(DHCP Relay)。

也就是说,一个子网络中应当有一台 DHCP 服务器,用于整个子网中 IP 地址的分配。但为每个子网都单独配置一个 DHCP 服务器也有点「愚蠢」。

所以另一种解决办法就是,某个网络中的网关会知道负责该网络的 DHCP 服务器在什么位置,IP 地址是什么,网关路由会负责转发 DHCP 报文请求并返回响应的报文,这就叫 DHCP 中继。

当然了,实际上现在的路由器本身就可以充当一个 DHCP 服务器,为其所在的子网提供动态地址获取服务,所以往往也不需要转发那样麻烦。

而完整的 DHCP 请求与响应的过程则是这样的:

第一步:

DHCP 服务器发现。 这个阶段的首要任务是,找到当前网络中 DHCP 服务器的位置,并且整个 DHCP 报文的交换是基于 UDP/IP 协议的,向目的端口 67 发送。

本机由于没有 IP 地址,所以 IP 数据报中的『源地址』为「0.0.0.0」,『目的地址』为「255.255.255.255」。

这样在链路层广播该数据报的时候,同一子网络下的所有主机都会接受该数据报,但只有 DHCP 服务器会响应这个请求。

于是如果路由器本身就是一个 DHCP 服务器的话,那将进入第二步,否则路由器将分组转发到 DHCP 服务器所在的网络内。

第二步:

DHCP 服务器提供。 DHCP 服务器,无论是位于外网或是网关路由本身,在收到一个『发现报文』后,将响应一个『提供报文』。

该报文中将包含,推荐客户使用的 IP 地址、子网掩码、IP 地址租用周期等信息。

第三步:

DHCP 请求。这其实是一个选择阶段,客户端主机确认服务器推荐的参数,决定使用,于是依然以广播的形式发送请求向服务器确认。

第四步:

DHCP ACK。收到客户端主机发来的确认请求后,服务器将实际从 IP 池中分配出一块 IP 地址出来,并返回客户端确认信息的 ACK。

从此之后,该主机算是获得了一块可用的 IP 地址了,终于加入了网络。

除此之外,还有一个细节不知道大家日常有没有留心,就是我们对于同一个子网络,IP 地址基本总是一样的,并没有因为每次开机后连入网络而被分配不同的 IP。

这一点算是 DHCP 协议的一个约定了,当某台主机第一次加入某个子网络,它将从 DHCP 服务器获取一个全新的 IP 地址。

而以后该主机重新加入到该网络时,将直接进入 DHCP 请求的第三步,将主机上次使用的参数发给服务器,确认是否可用。而一般情况下服务器会同意并按照你的要求分配出去一块 IP 地址,这也是为什么你每次使用的几乎是同一 IP。

讲完了 DHCP 动态获取 IP 地址,接着我们简单看看 IP 数据报的基本格式,并在最后讨论一下路由器的选择算法,看看一个 IP 数据报是如何被路由器给转发出去的。

image

 

关于其中的各个字段或选项是如何被使用的,我们这里暂时先不做讨论,强行解释并适合大家理解,等到具体分析报文分发与解析时会容易理解很多。

路由器

路由器是网络层的一个核心设备,它完成了从「目的 IP 地址」到「目的 IP 所在的子网络」的完整路径转发过程。它的内部结构如下:

image

 

每个端口都直接连接了一台设备,而其中的『路由选择处理器』则负责解析一个输入端口进来的数据应该被推出到哪个输出端口中去。

所以,你应该也发现了,整个路由器的核心应该是这个『路由选择处理器』,也就是驱动这个『路由选择处理器』工作的算法,我们称之为『路由选择算法』。算法本质上就是解决,一个数据报输入进路由器内存,该从哪个输出口转发出去的问题。

一个好的 『路由选择算法』不仅仅应该解决如何到达目的地的问题,还应该考虑如何最快的到达目的地,即能够判断并选择性的绕过拥塞的网络路径。

整个路由选择算法分为两大类,全局式路由选择算法和分散式路由选择算法。前者的一个最典型的实现就是『链路状态路由选择算法』,后者的一个最典型的实现就是『距离向量算法』。

这两者算法的理论原理这里不再和大家一起探讨了,我们着重看看因特网中是如何基于这两种算法实现的路由选择。

首先,整个因特网是一个很庞大且复杂的系统,所以整体上被划分为一个一个的自治系统(AS),在每一个 AS 中都运行着同样的路由算法,自治系统之间使用 BGP 协议交换信息。

image

 

整个因特网大致就是这样的一个个自治系统互联构成的,而自治系统内部的所有路由器都运行着同样的路由选择算法,基于距离向量的『RIP 协议』或基于链路状态的『OSPF 协议』。

至于为什么要拆分自治系统,等我们介绍完这个 RIP 或者 OSPF,你就明白了。

RIP 协议的算法是这样的

image

 

简单的一个自治系统,我们以此为例看看整个 RIP 协议是如何工作的。

首先最开始,路由器 A 的转发表肯定是这样的:

----------------------------
目的子网   下一跳路由   跳数
x           B           1
q           E           1
----------------------------
复制代码

其他路由器也是类似的,第一步都建立起与自己直接相连邻居的连接。

第二步是一个不断进行的过程,相邻的路由器之间每隔 30 秒就相互交换信息,告知对方自己的转发表内容。

所以经过一次交换之后,路由器 A 将收到来自 B 和 E 的转发表信息,于是路由转发表更新如下:

----------------------------
目的子网   下一跳路由   跳数
x           B           1
q           E           1
y           B           2
p           E           2
----------------------------
复制代码

但是这里有一个细节,子网络 y 是可以通过 A - B - y 到达的,但同时也可以通过 A - E - C - y 到达。你也许已经猜到了,路由器当然会选择最短路径的一条来更新自己的转发表。

所以,这个距离向量的算法本质上就是通过相互之间不断的交换信息以保证某个自治系统内,所有的路由器都知道某个目的子网的最短路径。

OSPF 的实现是这样的:

我们同样以上面的例子进行解释:

image

 

OSPF 是基于链路状态路由选择算法进行实现的,所以它也是一个全局性路由选择算法,算法运行一次即可完成全网的路由信息更新。

而 OSPF 本质上就是一个迪杰斯特拉求最短路径问题,它通过不断的迭代与计算更新整个路由转发表。假设现在我们的路由器 A 运行 OSPF 协议:

第一次迭代完成后,它得到与 B、E 两台路由器相关的子网络的路径计算。

第二次以 B 或者 E 为起点重新运行算法,这里我们假设以 B 为起点运行了算法,那么与 C 相关的子网络的路径也被更新进 A 的路由转发表。

第三次以 C 为起点同样的运行算法,得到和 D 相关的子网络路径更新。

由于 D 作为末端路由,并没有直接相连的其他路由,所以算法不再继续,回到 E。

第四次,以 E 作为原点,运行算法,得到了 C 相关子网络的路径,如果有更短的路径,将更新 A 的转发表以最优路径。

那么,待整个算法运行结束,一个自治系统中的所有路由器几乎全部遍历,但是却不同于 RIP,OSPF 相对而言收敛快,可以迅速完成任务,而 RIP 则需要不断的交换信息以达到需求,往往会陷入一个长周期。

当然了,OSPF 需要较强的 CPU 计算能力和更多的内存存储空间。所以总的而言,他们都广泛应用于整个因特网之中,RIP 应用在较为底层的 ISP 上,而 OSPF 则运行在较为高级的 ISP 中。

至此,整个网络层的基本内容也介绍完了,总结一下,网络层的核心任务就是负责转发分组,而如何将分组转发到目的主机的网络中呢,牵扯出 IP 协议,通过 IP 地址与子网掩码划分子网络,而路由器执行路由选择算法得知目的子网络的完整路由路径并进行分发。

链路层

网络层解决的是,分组转发的目的网络,也就是转发给目的网络的网关路由,而链路层解决的是,将分组广播给个人,也即目的主机。

网络层的 IP 数据包会在链路层被封装成『以太网帧』,它的基本结构是这样的:

image

 

前导码用于同步时钟,按照我的理解就是区分一个一个的帧,源和目的地址指的是『Mac 地址』,也称作物理地址。

『Mac 地址』是硬件级别的主机唯一标识,由生产厂家唯一确定。类似这样:

34-E6-AD-17-A5-6B

全球任意一台主机的 Mac 地址都是不同的,它不像 IP 地址可以在别人不用的时候共享。

下面我们要讲一个协议,它完成了主机 IP 地址到 Mac 地址的转换,他就是 ARP 地址解析协议。

ARP 协议其实有点类似于我们之间在应用层介绍的 DNS 协议,输入一个域名 地址,输出一个 IP 地址,而 ARP 而言,输入一个 IP 地址,输出一个 Mac 地址。

网络中的每台主机,包括路由器,都内置的 ARP 模块和 ARP 表。当一份数据报到达链路层时,首先要做的就是以该数据报的目的 IP 作为输入,先查询自己主机的 ARP 模块,如果能够得到该 IP 的目的主机 Mac 地址,那么封装一个以太网帧交给物理层发送出去就好。

而如果本机的 ARP 表中并没有存储目的 IP 主机的 Mac 地址,那么就需要向同网络中的其他主机进行查询。

发送方会构建一个特殊的 ARP 分组,源 Mac 地址为发送方的 Mac 地址,目的 Mac 地址为广播地址:255.255.255.255,以及源和目的 IP 地址,本质上就是一个特殊的以太网帧。

于是该网络下的所有主机都将收到这个 ARP 分组,那么他们要做的就是拆开 IP 地址比对是否和自己的 IP 地址相同,如果是则响应一个 ARP 分组,告诉发送方自己的 Mac 地址。

如果不是自己,则还会检查自己的 ARP 模块,看看是否能提供帮助。

最终,发送方会得到想要的目的 Mac 地址并更新自己的 ARP 表,然后封装一个正常的以太网帧发送出去。

由于以太网采取的是『广播』方式,所以同一子网络中任意一台主机发送报文,所有的其余主机都会收到,但是它们会匹配目的 Mac 地址是否是自己,不是则丢弃,这一点很重要。

好了,那么到此为止我们也简单介绍了链路层的相关内容,关于物理层,其实没什么介绍的,就是 0、1 的电信号传输。

关于整个 OSI 五层模型,我们从上至下也已经完成了学习!

作者:YangAM
链接:https://juejin.im/post/5b10be81518825139e0d8160
来源:掘金

猜你喜欢

转载自blog.csdn.net/yjclsx/article/details/85599592