一个数据包的互联网漫游记
尽管大多数最终用户都不关心 Internet 是如何工作的,但是我相信你们当中的仍然会有一些人对 Internet 运转的基本原理非常感兴趣。
在这篇文章中,我们会通过阐释一个数据包从源到目的地的征程,来试着拨开互联网的第一层神秘面纱。由此出发,我们会尽量让本文的内容保持简单基础。
在更进一步之前,让我们先快速简要地理解一下 DHCP 和 DNS 的概念。
DHCP
你有没有想过你的计算机是如何获取 IP 地址的?嗯……首先你必须知道计算机获取 IP 地址是有两种途径的,一种是静态方式,另一种是动态方式。
静态方式就是由管理员在机器上手动设置 IP 地址。如果你的计算机连接在局域网或者类似的网络上的话,需要注意的是你所设置的 IP 地址不能和网络上任何其他机器的 IP 地址相同,因为那样会引起 IP 地址冲突,两台机器就都不能连接到 Internet 了。
动态方式就是计算机(在系统启动的时候)请求一台服务器来为它分配一个 IP 地址。这一过程使用的协议称为动态主机配置协议(DHCP)。这里提到的服务器就是 DHCP 服务器,这台服务器负责为网络上所有的计算机分配 IP 地址。此时由 DHCP 来保证没有 IP 地址冲突。如果一台机器关闭继而重启,那么一条刷新 DHCP 的请求就发送到 DHCP 服务器上,这一次可能会分配给它一台与原来相同或者不同的 IP 地址。通常 DHCP 服务器会得到一个 IP 地址池,DHCP 服务器只能分配该地址池内的地址。这么做是为了保证将其他 IP 地址作为静态地址的时候不会引起任何冲突。
DNS
我们大多数人都使用过 google.com 进行搜索,不过你有没有想过为什么在浏览器里面输入 google.com 就能连接到 Google 的服务器呢?嗯……要理解这件事情,我们需要理解域名服务器(DNS)的概念。
就如同现实生活中人们通过名字互相区分一样,在计算机网络的世界里,不同的计算机之间通过指派给它们的 IP 地址来互相区分。IP 地址有两种类型:公有和私有。通常服务器使用公有地址,这是因为他们要被全世界数以百万计的计算机访问。至于你那连接在路由器上的个人电脑,它通常获取到的是私有 IP。由于公有地址的数量是有限的,所以现在搭设局域网(在一个有着公有 IP 的路由下搭建)然后使用私有地址开始变得流行并且非常成功。
记忆 IP 地址这样的任务对于大多数人来说有些困难,于是每个服务器都还有一个名字(比如 google.com)。这样最终用户只需要记住这个名字,在浏览器中输入它,然后敲回车就可以了。现在让我来看看当用户在浏览器中输入名字并敲回车之后究竟发生了什么。首先,需要把域名转换成对应的 IP。要完成这项任务,需要给默认网关(大多数情况下就是路由器)发送一条与 DNS 请求。路由都会配置一个 DNS 服务器地址,这个地址便是 DNS 请求的目的地。
DNS 服务器用来将域名转换成 IP 地址。当 DNS 服务器收到一条请求后,它会检查它有没有该请求需要的转换信息。如果没有这条转换信息,那么 DNS 服务器会把这条请求转发给其他的 DNS 服务器。通过这种方式,就完成了从域名到 IP 地址的转换。转换结果随后会发回到发出请求的计算机。
DNS 之后又发生了些些什么?
要理解下面的解释,你应该对 TCP/IP 协议族有一定的了解。当然啦,我们会尽量保持解释的简单性。
* 一旦通过 DNS 得到了目标 IP 地址,应用层就会准备好一条 HTTP GET 请求。随后这条请求会前进到传输层。
* 这一层有两个主要的协议——TCP 和 UDP。正是在这一层,该请求被封装为了传输层数据包。如果用的是 TCP 协议,还要保证包的大小不能超过源和目的地之间 MTU(最大传输单元)的最小值。这样做的目的是避免数据包在传输过程中分片(fragmentation)。另一方面,如果用的 UDP 的话,就不用顾虑这么多了,当然了这种情况下,包就有可能分片了。
* 一旦传输层的包封好了,接着它就会被推到 IP 层。这一层会增加源和目的地的 IP 地址以及一些其他有用的信息,诸如 TTL(time to live 生存期)、分段信息等等。所有的这些信息在数据包抵达目的地的路途中都是有用的。
* 当数据包进入到数据链路层之后,与 MAC 地址相关的信息就会加诸其上,随后数据包就会被推送到物理层。这样一串“0”和“1”就会从你的网卡里发送到物理媒介中去。
如果数据包的目的地没有直接连接在源计算机上,那么根据源计算机上存储的路由信息,数据包就会传送到最近的网络节点上去。网络中有各种不同的节点,比如路由、网桥、网关等等。每一种都有重要的作用,例如路由用来传送 IP 包,网桥用来连接不同协议的异构网络。
* 如果我们考虑一个简单的网络的话,路由就是将数据包从源送到目的地的最主要的角色。数据包离开源计算机后,与其相连的路由(数据要先发送到这里)的 MAC 地址就这个包的目的 MAC 地址。
数据包到达该路由之后,路由就会做下面几件事情:
* 减记 TTL 值,重新计算数据包的校验和。
* 搜索其路由表,以检查其中有没有数据包的目的 IP 所对应的主机。如果有的,路由就将该包发到相应的主机。
* 如果没有这样的一条路由,就从路由表中搜索源于该目的 IP 的网络的地址。如果有的话就将该包发送到这个特定的网络。
* 如果上面的两次搜索都失败的话,该数据包就被发送到路由表中指定的默认路由。
上面的所有情况中,不论数据包是在路由之间传送还是从路由传送到目的地,目的 MAC 地址都会变成最近的路由或者目的地的 MAC 地址。通过这种方式,目的 IP 地址保持不变,而目的 MAC 地址不断从一个路由变为另一个。就是这样,数据包从一台路由到下一台路由,直到它抵达终点。
啊哈,千难万险之后我们的 HTTP GET 请求终于到达目的地啦:
* 数据包首先抵达物理层,该层向 CPU 发送一个 IRQ 来告诉 CPU 有数据到了,需要处理。
* 当数据被送到数据链路层后,就会检查它的 MAC 地址来确认该数据包确实发到这台计算机的。
* 如果上述检查通过的话,数据包就会传到 IP 层。在这里会检查数据包的 IP 地址以及其校验和,随后将其传送到相应的传输层。
* 这些都完成了之后,该信息(在我们的例子里是 HTTP GET 请求)就会根据端口的值被送到监听该端口的应用中去。
* 我们的请求就是这样送抵 Google 的网络服务器的。