引言
作为一个程序员,我们几乎每时每刻都在跟网络打交道,比如浏览器打开一个连接、客户端调用一个接口等等。再比如一个常见的面试题:说一下从浏览器输入网址打页面展示在浏览器中,中间都经历了什么?说的越细越好。作为一个互联网从业者,虽然说网络知识不是必须要掌握的,但是如果想要走的更远,我个人觉得网络知识是必备的。
最近在看一本书《网络是怎样连接的》,个人觉得里面讲的很详细,相信看完这本书你就能很轻松的回答出上面所说的问题。虽然这本书中涉及的知识不是特别深,但是对于不是专门从事网络方面的工作的程序员来说我觉得是已经足够了,写这一系列的文章也算是自己看书的一个总结。文章中的内容大部分来自书中,有兴趣的可以直接去看书。
IP地址和域名
1.1什么是域名?
简称域名、网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)。比如我们在上网的时候输入的www.baidu.com就是域名。
1.2什么是IP地址?
任何一个可以上网的设备都会被分配一个IP地址,这个地址就相当于是我们现实世界中的xx城市xx路xx号xx室,其中“号”对应的是分配给整个子网的,“室”对应的号码就是分配给某一台计算机的,这个地址整体被称为IP地址,比如:192.169.30.56。通过IP地址,我们就可以找到目标服务器的位置,从而与之通信。关于IP地址方面的知识,后续会有专门的文章来介绍,这里就只需要知道,IP地址就是我们每一台可以上网的设备在网络中的具体地址就可以了。
1.3为什么有了域名还要有IP地址
网络是通过IP地址来确定通信对象的,如果我们不知道IP地址就无法与之通信。到这里可能会有人问,那我们在浏览器中直接输入IP地址不就好了,为什么还要搞个域名?实际上,如果用IP地址代替域名也是可以正常工作的。我们来举一个生活中的例子,现在我们每个人都有手机,每一个手机中都有通讯录,通讯录中存储的都是 名字和电话号码的一一对应,我们想打电话给某个人的时候,肯定是通过姓名找到这个人,然后通过通讯录中存储的电话号码来给这个人打电话。这跟域名和IP地址的关系是一样的,我们可能记不住那么多IP地址,但是域名我们是可以记住的。
那么可能有人有疑问了,那既然这样,我们在网络中不要用IP地址了,直接用域名不就好了。我们知道,一个IP地址(这里所说的都是IPV4)占用四个字节,一个域名呢?最少也要十几个字节吧,如果使用域名来确定通信对象,那就会加重网络传输的任务。
因此,我们现在使用的方案是,让人来使用名称,让路由器使用IP地址。那么很显然了,我们需要有一个把域名转为IP地址或者把IP地址转换为域名的机制。这个机制就是DNS了。
Socket库
根据域名查询IP地址的方法非常简单,只要询问DNS服务器"www.baidu.com"的IP地址是多少就可以了,然后DNS服务器会回答说“该服务器的IP地址是xx.xx.xx.xx”。这一步非常简单,相信很多读者也清楚这一过程。但是具体的流程呢?
向DNS服务器查询IP地址就是向DNS服务器发出查询消息,并接收服务器返回的消息。想要完成这一过程,我们的浏览器上肯定是要有一个客户端的,而相当于DNS客户端的部分成为DNS解析器,或者简称解析器。通过DNS查询IP地址的这一过程叫做域名解析,那么负责解析的就叫做解析器了。
那么解析器存在在哪里呢?解析器实际上是一段应用程序,它包含在操作系统的Socket库中。Socket库是干什么的呢?Socket库中包含的程序组件可以让其他的应用程序调用操作系统的网络功能,而解析器就是Socket库中的一种程序组件。
通过解析器向DNS服务器发出查询
Socket库中的程序组件都是标准组件,我们只要从应用程序中调用就可以了。如下图所示:
具体来说就是在编写应用程序的时候,写上解析器的程序名称gethostbyname,然后加上我们需要查询的服务器域名就可以了,这样就完成了对解析器的调用。调用解析器之后,解析器会向DNS服务器发送查询消息,然后接收DNS服务器的响应消息。接收到响应之后,从消息中取出IP地址,写入到浏览器指定的内存地址中。浏览器在向服务器发送消息的时候,从该内存地址中取出IP地址,并将它跟HTTP消息一起交给操作系统就可以了。
下面来介绍下解析器的内部原理。当控制流程转移到解析器中之后,解析器会生成一条要发送给DNS服务器的查询某一个域名对应的IP地址的请求消息,并将这一消息发送给DNS服务器。但是实际上,解析器并不具备直接跟DNS服务器通信的能力,因此发送消息这一操作并不是由解析器来完成的,而是要委托给操作系统内部的协议栈来完成。然后协议栈再通过网卡将消息发送给DNS服务器。当DNS服务器收到消息之后,就会根据消息中的内容来完成对应的查找工作,并将结果返回去。返回去的步骤是这样的,响应消息先到达计算机设备,然后通过协议栈交还到解析器,最后解析器再将IP地址写入到浏览器指定的内存中,这样就完成了整个查询过程。其中查询这一步骤也是比较复杂的,我们稍后会介绍。
计算机的内部结构就是这样一层一层的。很多的程序组成不同的层次,彼此之间分工协作,从而完成整体的工作。
顺带提一下,我们向DNS服务器查询IP地址的时候,我们需要知道DNS服务器的IP地址。如果我们不知道DNS服务器的地址,那就陷入了一个先有鸡还是先有蛋的问题当中。实际上,在操作系统中已经预先设置好了DNS服务器的IP地址,这样我们直接取出来用就可以了。
DNS大接力
前面介绍了解析器与DNS的交互过程,下面来看下DNS服务器的工作。DNS服务器的基本工作就是接收来自客户端的消息,然后根据消息的内容返回即可。
4.1域名的层次接口
我们知道,域名和IP地址信息存储在DNS服务器中。但是在互联网的世界中,有着不计其数的服务器,我们不可能把所有的服务器信息都保存在同一台服务器中。因此肯定会出现找不到我们要查询的信息的情况,那么此时DNS服务器是如何工作的呢?
首先DNS服务器中的所有信息都是按照域名以分层次的结构来保存的。就比如说:www.baidu.com这个域名按照公司里面的组织结构来说就是:com事业部 baidu部们的 www这样。这种具有层次结构的域名信息会被注册到DNS服务器中,而每个域都是作为一个整体来处理的。换句话说就是一个域的信息是作为一个整体存放在DNS服务器中的,不能将一个域拆开来存放在不同的服务器中。不过,DNS服务器和域也并不是一对一的关系,一个DNS服务器可能会存放多个域。我们这里先默认一个DNS服务器就只存放一个域的信息,理解了只存放一个域之后,存放多个域的情况自然也就理解了。
域名是怎么分层的呢?我们打个比方还是 www.baidu.com,在域名中,越靠右的位置表示其 层级越高。上述域名可以理解为:com域下的baidu域下的www服务器。
4.2寻找相应的DNS服务器并获取IP地址
从上面的内容我们可以知道,我们要找到某一个域名对应的IP地址,关键是要找到该域名对应的信息存放在哪一台DNS服务器上。互联网中有数万台DNS服务器,我们肯定不能一个一个去找,那样效率太低了,因此我们需要一个规则。
域名的存放规则是将负责管理下级域的DNS服务器的IP地址注册到它们上级的DNS服务器中,然后上级的DNS服务器的地址再注册到更上一级中,以此类推。也就是说负责管理baidu.com域名的DNS服务器的IP地址需要注册到com域的DNS服务器中。这样我们就可以通过上级DNS服务器查询到下级DNS服务器的IP地址。
我们可能会认为com域或者cn域就是最顶层了,但是并不是这样的。实际上,在com或者cn这些顶级域上面还有根域。根域不像com顶级域一样有自己的名字,因此在书写时都会忽略。如果要明确表示根域,需要在域名的最后加上一个. ,类似www.baidu.com.,而最后的点就代表根域。根域的DNS服务器中保管着顶级域的的DNS服务器的信息,而顶级域又包含下级域的DNS服务器信息,因此我们可以从根域开始,顺藤摸瓜找到我们需要的DNS服务器的地址。
那么根域的DNS服务器地址我们又是如何知道呢?实际上,根域的DNS服务器信息保存在所有的DNS服务器中,这样一来,任何一个DNS服务器都可以找到并访问根域DNS服务器。这样,客户端只要能找到任意一台DNS服务器就可以找到根域DNS服务器,进而找到我们需要的域名所在的DNS服务器。这一过程如下图所示:
下面我们再用一张图来总结客户端查询域名对应IP地址的过程:
1.客户端询问最近的DNS服务器,www.lab.glasscom.com的IP地址是多少?
2.最近的DNS服务器会找到根域名服务器,询问www.lab.glasscom.com的IP地址是多少?跟域名服务器返回信息说,你去问com域所在的DNS服务器吧
3.最近的DNS服务器询问com域所在的DNS服务器,www.lab.glasscom.com的IP地址是多少?com域所在的DNS服务器回答,你去问glasscom.com域所在的DNS服务器吧。
4.最近的DNS服务器询问glasscom.com域所在的DNS服务器,www.lab.glasscom.com的IP地址是多少,回答你去问lab.glasscom.com吧。
5.询问lab.glasscom.com域所在的DNS服务器,www.lab.glasscom.com对饮的IP地址是什么,回答你去问www.lab.glasscom.com域所在的DNS服务器吧
6.问www.lab.glasscom.com域所在的DNS服务器,www.lab.glasscom.com的IP地址是多少?回答是:xx.xx.xx.xx,拿到结果之后,将IP地址返回给客户端。
以上就是客户端向DNS服务器查询IP地址的全过程了。
4.3通过缓存加快DNS服务器的相应
上面所讲述的过程其实与真实的过程还是有一些差距的。比如,在正式的互联网世界中,一个DNS服务器可以管理多个域的信息,另外还有上级域和下级域在同一个DNS服务器的情况。但是如果我们明白了上述过程,其实这两种情况的过程也能大致明白。比如说:www.lab.glasscom.com这个域名,如果glasscom域和lab.glasscom域的信息在同一台DNS服务器中,那么上述过程我们就可以省去第4步了。
此外,有时候我们并不需要从根域开始查找,因为DNS服务器都会有缓存功能。如果要查询的域名信息已经在缓存中,那么就可以直接返回响应。并且,如果要查询的域名信息不存在,也会返回“不存在”这个这一结果,并且这一结果也会被缓存。
另外还有一种情况需要注意,那就是当数据被缓存后,原始数据可能会发生改变。这样的话,缓存中的数据就是不对的了。因此,DNS服务器的缓存会有一个有效期,在有效期过了之后,就会清除缓存。然后就是,每次在查询的时候,返回结果会告诉客户端,是不是从缓存中读取的。
那这样的话,相信很多读者都会有这样的猜想。如果我们读取的缓存已经失效,原始数据已经改变,这个时候会怎么样呢?这个时候我能想到的是这样的:
客户端拿到已经失效的缓存之后,重新发送请求,并且告诉DNS服务器,不要从缓存中读取,因为缓存的数据已经失效。这个时候,DNS服务器就会把缓存信息删除,然后重新去查找新的IP。
当然,上述只是我的猜想,书中也并没有提及。有兴趣的朋友可以去查一下这部分的内容,就当做留给大家的一个思考了。