首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

2.4 域 名 系 统

IP地址类似于电话号码,但是,IP地址常常比电话号码长,要记住这些IP地址实际上是不可能的。一般来说,人们不太容易记住长长的一串数字,但与此相比,单词则明显地容易记住。

因此,因特网的设计人员提出了通过名称引用IP地址的一种方法。他们决定创建一种分层系统,称为“域名系统(Domain Name System,DNS)”。

DNS是层次式结构,这就使得查找起来很容易。例如,最初因特网有7个顶级域(Top-Level Domains,TLD),应该对它们中的大多数都很熟悉,表2.11列出了这7个顶        级域。

表2.11 最初的顶级域

含    义

.com

商用

.net

网络供应商,主要是ISP

续表

含    义

.org

非营利组织

.edu

教育机构

.mil

美国军事部门

.gov

美国政府部门

.int

国际组织

正如所看到的,这些域在很大程度上是以美国为中心的,这样,一旦因特网遍及全世界,就会产生许多问题。鉴于此,就专门为每个国家分配了一个由两个字母组成的顶级域。虽然旧域还没有中止使用,但并不主张使用它们,而主张使用此两字母顶级域。这些两个字母顶级域的例子包括有:us(美国)、.uk(英国)、.fr(法国)、.de(德国)、.au(澳大利亚),甚至为并不需要的南极洲也分配了.ax这一顶级域名。

我们可以将所有这些地址都想象为一棵树的顶层,图2.9展示了这一点。DNS分层结构的下一层是域。对google.com来说,这指的就是“google”。本质上,每一个根DNS服务器都了解每个域的IP地址。

图2.9 DNS系统分层结构的一部分

因此当向DNS服务器请求google.com地址时,DNS服务器就到 .com服务器查找google,然后返回结果。这时,查找到的google.com的地址为216.239.33.100,但是当你们读到这里时有可能地址已经发生了变化。

但是,DNS服务器所完成的任务还不只这些。例如,很可能已经习惯了类似于www.google.com这样的地址。前面的www是Google网络上指定机器的名称。因此,当你的DNS服务器查找google.com时,它会接着与Google的主机(216.239.33.100)上的DNS服务器联系,并向它请求机器名为www的地址。这时,返回的地址是216.239.33.99,但是当等大家读到这里时地址有可能已经发生了改变。

为了进一步阐明个人观点,服务器news解析为216.239.51.104,服务器images则解析为216.239.37.104。www、news和images所有这3台服务器运行在Google网络的不同机器上。root.com DNS服务器并不知道是不同的机器,而且也不需要知道,它只知道root google.com DNS服务器。

看图2.9,会看到将这些服务器像链一样串起来是可能的。看一下右边.edu下的两个条目,可以根据这棵树来构建地址www.cse.buffalo.edu或者 www.eecs.berkeley.edu。

对DNS只简单地介绍到这里,DNS是一个很大的主题,如果大家对此感兴趣,建议找一本好的网络书来阅读。

现在如何在Sockets API中使用DNS呢?你可能会认为类似于下面这样的代码是可    行的:

unsigned long google = inet_addr( "www.google.com" );

但是你错了,inet_addr函数只将标准格式的IP地址转换成一个数字,它并不查找DNS。

完成DNS查找

与Sockets API中的每一个函数一样,通过DNS查找获得IP地址的函数也有些不可思议且难以理解。此函数再一次引入了另一个不可思议的结构hostent。它看起来类似于下面的代码:

struct hostent {

  char*    h_name;

  char**   h_aliases;

  short    h_addrtype;

  short    h_length;

  char**   h_addr_list;

};

坦率地说,因为除了查看IP地址外笔者根本不使用这一结构,所以实际上并不清楚所有元素的含义。

下面是gethostbyname函数,它执行DNS查找:

struct hostent* gethostbyname( const char* name );

gethostbyname接受一个用字符串表示的名称,并返回一个指向hostent结构的指针。那么,究竟是如何从中得到IP地址呢?IP地址存储在二维数组h_addr_list的前4个字节中。下面是获得这一地址的一种方法:

struct hostent* host;

host = gethostbyname( "www.google.com" );

unsigned long addr = *((unsigned long*)host->h_addr_list[0]);

  这些代码简直太难以理解了,但它是必不可少的。最后一行取得二维数组h_addr_list的第一个字符的地址,并将它转换成一个unsigned long类型的指针。最后,再解除对它的引用。如果对刚才讲的这些不能理解,也没有关系。只要明白它在起作用就行了。

Sockets API设计人员决定使事情容易一些。但是,也不要过于高兴,这也没有太大的改进。他们创建了一个名为h_addr的宏,实际上就是h_addr_list[0]。因此,现在可以用下面这行稍微容易理解一点的代码来取代前面给出的那行非常难以理解的代码了:

unsigned long addr = *((unsigned long*)host->h_addr);

如果产生错误,则函数返回NULL,而不是返回指向hostent的指针。但是,使用errno或WSAGetLastError()却无法查找到错误。相反,必须通过变量h_errno才能检索错误。但这样做也换来一点可取之处:h_errno在Sockets API和Winsock中都可以使用。

表2.12列出了如果gethostbyname失败,则由h_errno返回的错误代码。

表2.12 gethostbyname () 函数的错误代码

错     误

含    义

HOST_NOT_FOUND

地址无法解析

TRY_AGAIN

DNS服务器失败,但是地址仍然可以解析。再试试

NO_RECOVERY

产生了不可恢复的错误

NO_DATA

没有有关此地址的有效数据

另外还需要明白的一点是,如果地址采用的格式已经是IP地址,则函数就不能解析这些地址。例如,试图解析“192.168.0.1”会失败,因为它已经是一个IP地址了。切记!

DNS也可以进行逆向查找。给DNS一个IP地址,系统就力图找到与此相匹配的DNS条目。使用gethostbyaddr()函数可以完成这一任务:

struct hostent* gethostbyaddr( const char* addr, int len, int type );

又是使用了hostent结构。第一个参数是一个指向想要解析的地址的指针,它遵循网络字节顺序(NBO)。需要注意的是,我们还需要做一些处理,因为NBO想要的是char型指针,而不是unsigned long型指针。这样做是为了确保灵活性,以便函数可以不是任何指定类型的地址。第二个参数是地址的长度,因为使用的是IPv4,所以它的值是4。最后一个参数是地址类型,因为使用的是IPv4,所以它的值是AF_INET。例如,下面的代码给出了如何调用这一函数:

unsigned long address = inet_addr( "216.239.33.100" );

struct hostent* host;

host = gethostbyaddr( (char*)&address, 4, AF_INET );

如果函数执行成功,则通过访问hostent结构中的h_name变量,可以检索查找出来的字符串结果。如果函数执行失败,则返回0,可以通过h_errno访问错误代码。该函数所使用的错误代码与表2.12中所列出的错误代码相同。

顺便提一下,不要试图修改或删除由其中任一函数返回的hostent结构,Sockets API拥有并管理这些结构。遗憾的是,这就意味着这些结构可能在未预先通知的情况下被覆盖,因此应该立即复制这些结构中所需要的数据。

了解有关DNS的这些内容对我们来说足够了。如果有兴趣,可以进一步阅读更多的     资料。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论