域名系统
DNS是对IP地址和域名进行互相转换的系统,其核心是DNS服务器。提供网络服务的服务端也是通过IP地址来区分的,但由于IP地址难于记忆,因此通过容易记忆并表述的域名来取代IP地址
在浏览器地址栏输入www.baidu.com,或如图1-1用ping命令获取其IP地址,便可访问百度主页,那么通用域名访问和通过IP访问这二者有何区别?

计算机内置的默认DNS服务器并不知道网络上所有域名的IP地址信息,若该DNS服务器无法解析,则会询问其他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结构体变量结构如下图所示:

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结构体地址而非字符串,如下图所示:

这里可能大家会有疑问,为什么是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