域名系统

DNS是对IP地址和域名进行互相转换的系统,其核心是DNS服务器。提供网络服务的服务端也是通过IP地址来区分的,但由于IP地址难于记忆,因此通过容易记忆并表述的域名来取代IP地址

在浏览器地址栏输入www.baidu.com,或如图1-1用ping命令获取其IP地址,便可访问百度主页,那么通用域名访问和通过IP访问这二者有何区别?

图1-1   用ping命令获取百度地址

实际上,域名是赋予服务端的虚拟地址,而非实际地址。因此需要将虚拟地址转化为实际地址。那么,如何将域名转化为IP地址呢?DNS服务器承担此重任,可以向DNS服务器请求转换地址。所有计算机中都记录着默认DNS服务器地址,就是通过默认DNS服务器得到相应域名的IP地址信息。在浏览器地址栏中输入域名后,浏览器通过默认DNS服务器获取该域名对应的IP地址信息,之后才真正接入该网站

计算机内置的默认DNS服务器并不知道网络上所有域名的IP地址信息,若该DNS服务器无法解析,则会询问其他DNS服务器并提供给用户,如图1-2

图1-2   DNS和请求获取IP地址信息

图1-2展示了默认DNS服务器无法解析主机询问的域名对应IP地址时的应答过程,可以看出,默认DNS服务器收到自己无法解析的请求时,向上级DNS服务器询问,通过这种方式逐级向上传递信息,到达顶级DNS服务器时——根DNS服务器,它知道该向哪个DNS服务器询问,向下级DNS传递解析请求,得到IP地址后原路返回,最终将解析的IP地址传递到发起请求的主机,DNS就是这样层次化管理的一种分布式数据库系统

利用域名获取IP地址

#include <netdb.h>
struct hostent *gethostbyname(const char *hostname);//成功时返回hostent结构体指针,失败时返回NULL指针

  

只要向这个函数传递域名字符串,就会返回域名所对应的IP地址。只是返回时,地址信息装入hostent结构体,此结构体定义如下:

struct hostent
{
char *h_name; //正式主机名
char **h_aliases; //主机别名
int h_addrtype; //主机IP地址类型:IPV4-AF_INET
int h_length; //主机IP地址字节长度,对于IPv4是四字节,即32位
char **h_addr_list; //主机的IP地址列表
};

  

从上述结构体可以看出,当调用gethostbyname函数时不止返回IP信息,同时还带着其他信息,下面简要介绍下上述结构体中的各个成员:

  • h_name:该变量存有官方域名,官方域名代表某一主页,但实际上,一些著名公司的域名并未使用官方域名注册
  • h_aliases:可以通过多个域名访问同一主页,同一IP可以绑定多个域名,因此,除官方域名外还可指定其他域名
  • h_addrtype:gethostbyname函数不仅支持IPv4,还支持IPv6。因此可以通过此变量获取保存在h_addr_list的IP地址的地址族信息。若是IPv4,则此变量存有AF_INET
  • h_length:保存IP地址长度。若是IPv4地址,因为是4个字节,则保存4;若是IPv6,因为是16个字节,故保存16
  • h_addr_list:该变量以整数形式保存域名的IP地址。另外,用于访问量较大的网站可能分配多个IP给同一域名,利用多个服务器进行负载均衡,同样可以通过此变量获取IP地址信息

调用gethostbyname函数后返回的hostent结构体变量结构如图1-3所示:

图1-3   hostent结构体变量

下面我们看一下gethostbyname函数的应用,并说明hostent结构体变量的特性

gethostbyname.c

#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);
}

  

  • 第17行:通过main函数传递的字符串作为参数传给gethostbyname函数
  • 第20行:输出官方域名
  • 第21、22行:输出除官方域名以外的域名
  • 第24、25行:输出IP地址信息

编译gethostbyname.c并运行

# gcc gethostbyname.c -o gethostbyname
# ./gethostbyname www.taobao.com
Official name:www.taobao.com.danuoyi.tbcache.com
Aliases 1:www.taobao.com
Address type:AF_INET
IP addr 1:61.154.126.109
IP addr 2:218.67.61.254

  

大家可在编译gethostbyname.c后自行选择一个域名进行测试,现在我们看一下第24和25行,如果我们只看hostent结构体的定义,结构体成员h_addr_list指向了一个字符串指针数组(由多个字符串地址构成的数组)。但字符串指针数组中的元素实际指向的是in_addr结构体地址而非字符串,如图1-4所示:

图1-4   h_addr_list结构体成员

图1-4给出了h_addr_list结构体的参照关系,正因如此,上述代码中才有了第25行的类型转换,并调用inet_ntoa函数

这里可能大家会有疑问,为什么是char *而不是in_addr *?hostent结构体的成员h_addr_list指向的数组类型并不是in_addr结构体的指针数组,而是采用了char指针。是因为hostent结构体并非只为IPv4准备,因此考虑到通用性,声明为char指针类型的数组。那么,声明为void指针类型的数组是否更合理?确实,如果指针对象不明确时,声明为void指针类型更合理,但我们所学习的套接字函数是早在void指针标准化之前定义的,在当时无法确定指针类型时都采用char指针

利用IP地址获取域名

之前介绍的gethostbyname函数利用域名获取包括IP地址在内的域相关信息,而gethostbyaddr函数利用IP地址获取域相关信息

#include <sys/socket.h>
struct hostent *gethostbyaddr(const char *addr, socklen_t len, int family);//成功时返回hostent结构体变量地址值,失败时返回NULL指针

  

  • addr:含有IP地址信息的in_addr结构体指针,为了同时传递IPv4地址之外的其他信息,该变量的类型声明为char指针
  • len:向第一个参数传递的地址信息的字节数,IPv4时为4,IPv6时为16
  • family:传递地址族信息,IPv4时为AF_INET,IPv6时为AF_INET6

现在我们来看一下gethostbyaddr函数的使用方法

gethostbyaddr.c

#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);
}

  

除第22行的gethostbyaddr函数调用外,与gethostbyname.c并无区别,因为函数调用的结果是通过hostent结构体变量地址值传递的

编译gethostbyaddr.c并运行

# gcc gethostbyaddr.c -o gethostbyaddr
# ./gethostbyaddr 218.67.61.254
Official name: 254.61.67.218.broad.sm.fj.dynamic.163data.com.cn
Address type: AF_INET
IP addr 1: 218.67.61.254

  

之前我们通过gethostbyname.c获得了某宝的IP地址,现在我们尝试用之前获得的IP地址反过来获取域名信息。从运行结果可以看到,记录于DNS的官方主页地址具有特殊格式

TCP/IP网络编程之域名及网络地址的更多相关文章

  1. 《TCP/IP网络编程》

    <TCP/IP网络编程> 基本信息 作者: (韩)尹圣雨 译者: 金国哲 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9787115358851 上架时间:2014-6- ...

  2. TCP/IP网络编程 读书笔记1

    本篇主干内容是TCP/IP网络编程1-9章学习笔记 1. linux文件描述符 描述符从3开始以由小到大的顺序编号,0,1,2,分配给标准I/O用作标准输入.标准输出和标准错误. 2. 协议族与套接字 ...

  3. TCP/IP网络编程系列之三(初级)

    TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值.端口号并非赋予计算机值, ...

  4. TCP/IP网络编程之多播与广播

    多播 多播方式的数据传输是基于UDP完成的,因此,与UDP服务端/客户端的实现非常接近.区别在于,UDP数据传输以单一目标进行,而多播数据同时传递到加入(注册)特定组的大量主机.换言之,采用多播方式时 ...

  5. TCP/IP网络编程之套接字类型与协议设置

    套接字与协议 如果相隔很远的两人要进行通话,必须先决定对话方式.如果一方使用电话,另一方也必须使用电话,而不是书信.可以说,电话就是两人对话的协议.协议是对话中使用的通信规则,扩展到计算机领域可整理为 ...

  6. 浅谈TCP/IP网络编程中socket的行为

    我认为,想要熟练掌握Linux下的TCP/IP网络编程,至少有三个层面的知识需要熟悉: 1. TCP/IP协议(如连接的建立和终止.重传和确认.滑动窗口和拥塞控制等等) 2. Socket I/O系统 ...

  7. TCP/IP网络编程系列之四(初级)

    TCP/IP网络编程系列之四-基于TCP的服务端/客户端 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于流的 ...

  8. TCP/IP网络编程系列之二(初级)

    套接字类型与协议设置 我们先了解一下创建套接字的那个函数 int socket(int domain,int type,int protocol);成功时返回文件描述符,失败时返回-1.其中,doma ...

  9. TCP/IP网络编程之多线程服务端的实现(二)

    线程存在的问题和临界区 上一章TCP/IP网络编程之多线程服务端的实现(一)的thread4.c中,我们发现多线程对同一变量进行加减,最后的结果居然不是我们预料之内的.其实,如果多执行几次程序,会发现 ...

随机推荐

  1. 解决WinSCP连接虚拟机

    其实虚拟机你也可以将它形象化,认为它就是一台电脑,只是这个电脑在你的内存中,所以,一般电脑所具有的的功能虚拟机一样拥有,它也可以当成一台独立的个体哦. 针对很多使用WinSCP连接不上虚拟机的问题,这 ...

  2. cf1040E. Network Safety(并查集)

    题意 题目链接 一张图,n个点,m条边,每个点有个权值x,x<=1e18.如果一条边的两个端点不一样,那么这条边是安全的,开始时所有边都是安全的. 现在有一个病毒y,病毒可以入侵任意的点,入侵一 ...

  3. Today is the first day of the rest of your life.

    Today is the first day of the rest of your life. 今天是你余下人生的第一天.

  4. git reset命令学习

    我们在使用git进行版本管理的时候,如果遇到需要回退代码的情况,一般会用 git reset 命令,不过这个命令还有几个参数,这篇文章就来详解一下. 先来了解一下 git 大致的工作流程,配合这张图会 ...

  5. 如何使用VS将项目生成一个安装包?

    VS2010项目的部署与安装winform程序,我想进行安装.1.在解决方案中 ——点击右键——添加 2.然后选择 安装和部署 ——安装向导 可以更改名称 3.点击 下一步 4.然后选择上那3个 5. ...

  6. 推荐一个VS2015 插件 Favorite Documents

    随着解决方案越来越庞大,查找某个文件变的非常费神,考眼力 有了这个工具我们可以将常用的几个文件或文件夹添加到收藏夹中,随时展开双击即可到达收藏位置 从 视图>其他窗口中打开     安装 在Vi ...

  7. netcat 详解

    简介 netcat 是一款调试 TCP/UDP 网络连接的利器,常被称作网络调试的瑞士军刀,可见其功能强大. netcat 在 Linux, Windows 等各大操作系统上都有对应等发行版,以下以 ...

  8. Android(java)学习笔记75:ListViewProject案例(ListView + BaseAdapter + CheckBox)

    这个案例可能稍微复杂一点,我会讲述详细一点: 1. 首先是AndroidManifest.xml: <?xml version="1.0" encoding="ut ...

  9. 在O(1)时间复杂度删除链表节点

    题目描述: 给定一个单链表中的一个等待被删除的节点(非表头或表尾).请在在O(1)时间复杂度删除该链表节点. 您在真实的面试中是否遇到过这个题? Yes 样例 给定 1->2->3-> ...

  10. NUMA的原理与局限

    为什么要有NUMA 在NUMA架构出现前,CPU欢快的朝着频率越来越高的方向发展.受到物理极限的挑战,又转为核数越来越多的方向发展.如果每个core的工作性质 都是share-nothing(类似于m ...