【TCP/IP网络编程】:08域名及网络地址

OwenJi 2019-12-29

本篇文件简要介绍了域名系统及其与IP地址之间的关系。

域名系统

DNS(Domain Name System,域名系统)是对IP地址和域名进行相互转换的系统,其核心是DNS服务器。

什么是域名

提供网络服务的服务器端也是通过IP地址区分的,但由于IP地址形式繁琐,通常使用更为简洁的域名来取代IP地址。使用域名请求服务的另一个优点在于,服务器的域名一般不会轻易改变,但其IP地址可能需要经常变动。

DNS服务器

在浏览器的地址栏中输入百度的网站IP地址183.232.231.172即可浏览百度首页,但我们通常输入的是百度的域名www.baidu.com,这样更为方便。从结果来看,两种方式都可以进入百度的主页,并没有什么其他区别。域名是赋予服务器端的虚拟地址,而非实际地址。因此,需要将域名转换为实际的IP地址才能进行正常通信,DNS服务器就是负责域名和IP地址之间转换的工具。

那么DNS服务器的工作方式是怎样的?通常计算机会有内置的默认DNS服务器,但其不可能知道所有的域名和IP地址信息。若默认DNS服务器无法解析,则会询问其他DNS服务器,并将最终结果反馈给用户。

【TCP/IP网络编程】:08域名及网络地址

 DNS请求服务

默认DNS服务器收到自己无法解析的请求,会向上级DNS服务器询问。通过逐级向上请求的方式,到达顶级DNS服务器---根DNS服务器时,它知道该向哪个下级DNS服务器询问,并将最终解析出的IP地址信息原路返回并传递至请求主机。DNS便是这样一种层次化管理的分布式数据库系统。

IP地址和域名之间的转换

之所以有IP地址和域名之间转换的需求,是因为实际的程序编写中通常使用的是更为稳定的域名而非IP地址,这就需要再将域名转换为对应的IP地址信息。看似多余的操作,其实是考虑了程序的可靠性而做的保障。

利用域名获取IP地址

#include <netdb.h>

struct hostent *gethostbyname(const char *hostname);
    -> 成功时返回hostent结构体指针,失败时返回NULL指针

//hostent结构体定义
struct hostent
{
    char *h_name;        //official name
    char **h_aliases     //aliase list
    int h_addrtype;      //host address type
    int h_length;        //address lengh
    char **h_addr_list;  //address list
}

【TCP/IP网络编程】:08域名及网络地址

 hostent结构体变量结构

【TCP/IP网络编程】:08域名及网络地址

 h_addr_list结构体成员

hostent结构体变量中最重要的或者说我们最关注的成员是h_addr_list,该变量以字符指针数组的形式保存域名对应的IP地址结构(使用char*而非in_addr*是为了兼容性的考虑,其实更为合适的类型是void*,不过当时void*还并未标准化)。之所以会存在多个IP地址,是考虑到大量用户情况下需要利用多个服务器进行负载均衡。gethostbyname函数的示例代码如下。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int i;
    struct hostent *host;
    if(argc!=2) {
        printf("Usage : %s <addr>\n", argv[0]);
        exit(1);
    }
    
    host=gethostbyname(argv[1]);
    if(!host)
        error_handling("gethost... error");

    printf("Official name: %s \n", host->h_name);
    
    for(i=0; host->h_aliases[i]; i++)
        printf("Aliases %d: %s \n", i+1, host->h_aliases[i]);
    
    printf("Address type: %s \n", 
        (host->h_addrtype==AF_INET)?"AF_INET":"AF_INET6");

    for(i=0; host->h_addr_list[i]; i++)
        printf("IP addr %d: %s \n", i+1,
                    inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc(‘\n‘, stderr);
    exit(1);
}

gethostbyname

【TCP/IP网络编程】:08域名及网络地址

运行结果

若输入我们所熟悉的百度域名,则可得到如下结果:

[ exercises]# ./ttt www.baidu.com

Official name: www.a.shifen.com 
Aliases 1: www.baidu.com 
Address type: AF_INET 
IP addr 1: 183.232.231.172 
IP addr 2: 183.232.231.174 

利用IP地址获取域名

#include <netdb.h>

struct hostent *gethostbyaddr(const char *addr, socklen_t len, int family);
    -> 成功时返回hostent结构体变量指针,失败时返回NULL指针

通过输入google的IP地址,gethostbyaddr函数的示例代码如下。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int i;
    struct hostent *host;
    struct sockaddr_in addr;
    if(argc!=2) {
        printf("Usage : %s <IP>\n", argv[0]);
        exit(1);
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_addr.s_addr=inet_addr(argv[1]);
    host=gethostbyaddr((char*)&addr.sin_addr, 4, AF_INET);
    if(!host)
        error_handling("gethost... error");

    printf("Official name: %s \n", host->h_name);

    for(i=0; host->h_aliases[i]; i++)
        printf("Aliases %d: %s \n", i+1, host->h_aliases[i]);
    
    printf("Address type: %s \n", 
        (host->h_addrtype==AF_INET)?"AF_INET":"AF_INET6");

    for(i=0; host->h_addr_list[i]; i++)
        printf("IP addr %d: %s \n", i+1,
                    inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));    
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc(‘\n‘, stderr);
    exit(1);
}

gethostbyaddr

【TCP/IP网络编程】:08域名及网络地址

运行结果

相关推荐